summaryrefslogtreecommitdiffstats
path: root/src/jaegertracing/thrift/lib/cpp
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 18:45:59 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 18:45:59 +0000
commit19fcec84d8d7d21e796c7624e521b60d28ee21ed (patch)
tree42d26aa27d1e3f7c0b8bd3fd14e7d7082f5008dc /src/jaegertracing/thrift/lib/cpp
parentInitial commit. (diff)
downloadceph-19fcec84d8d7d21e796c7624e521b60d28ee21ed.tar.xz
ceph-19fcec84d8d7d21e796c7624e521b60d28ee21ed.zip
Adding upstream version 16.2.11+ds.upstream/16.2.11+dsupstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/jaegertracing/thrift/lib/cpp')
-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
213 files changed, 45145 insertions, 0 deletions
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