diff --git a/.asf.yaml b/.asf.yaml new file mode 100644 index 00000000000..b6b2e894357 --- /dev/null +++ b/.asf.yaml @@ -0,0 +1,8 @@ +notifications: + commits: commits@thrift.apache.org + issues: dev@thrift.apache.org + pullrequests_status: dev@thrift.apache.org + pullrequests_comment: notifications@thrift.apache.org + jira_options: link label worklog + + \ No newline at end of file diff --git a/.flake8 b/.flake8 new file mode 100644 index 00000000000..df8fdb261fd --- /dev/null +++ b/.flake8 @@ -0,0 +1,5 @@ +[flake8] +exclude = .git,__pycache__,**/gen-*/**,contrib/**,docs/source/conf.py,old,build,dist +ignore = W504,E402,E501 +max-complexity = 30 +max-line-length = 120 diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md new file mode 100644 index 00000000000..6a18a51847c --- /dev/null +++ b/.github/pull_request_template.md @@ -0,0 +1,16 @@ + + + + + +- [ ] Did you create an [Apache Jira](https://issues.apache.org/jira/projects/THRIFT/issues/) ticket? (not required for trivial changes) +- [ ] If a ticket exists: Does your pull request title follow the pattern "THRIFT-NNNN: describe my issue"? +- [ ] Did you squash your changes to a single commit? (not required, but preferred) +- [ ] Did you do your best to avoid breaking changes? If one was needed, did you label the Jira ticket with "Breaking-Change"? +- [ ] If your change does not involve any code, include `[skip ci]` anywhere in the commit message to free up build resources. + + diff --git a/.gitignore b/.gitignore index b7f7b454bb2..cf73c4c2f0f 100644 --- a/.gitignore +++ b/.gitignore @@ -105,6 +105,8 @@ project.lock.json /contrib/fb303/py/fb303/ttypes.py /depcomp /install-sh +/lib/as3/.gradle/ +/lib/as3/build/ /lib/cl/backport-update.zip /lib/cl/lib /lib/cl/run-tests @@ -182,9 +184,6 @@ project.lock.json /lib/c_glib/test/testserialization /lib/c_glib/thriftc.pc /lib/c_glib/thrift_c_glib.pc -/lib/csharp/**/bin/ -/lib/csharp/**/obj/ -/lib/csharp/src/packages /lib/d/test/*.pem /lib/d/libthriftd*.a /lib/d/test/async_test @@ -196,23 +195,27 @@ project.lock.json /lib/d/test/transport_test /lib/d/unittest/ /lib/dart/coverage +/lib/dart/**/.dart_tool /lib/dart/**/.packages /lib/dart/**/packages /lib/dart/**/.pub/ /lib/dart/**/pubspec.lock /lib/delphi/test/skip/*.request /lib/delphi/test/skip/*.response +/lib/delphi/test/serializer/*.dat /lib/delphi/**/*.identcache /lib/delphi/**/*.local /lib/delphi/**/*.dcu /lib/delphi/**/*.2007 /lib/delphi/**/*.dproj /lib/delphi/**/codegen/*.bat +/lib/erl/_build/ /lib/erl/.eunit /lib/erl/.generated /lib/erl/.rebar/ -/lib/erl/deps/ +/lib/erl/_build/ /lib/erl/ebin +/lib/erl/rebar.lock /lib/erl/src/thrift.app.src /lib/erl/test/*.beam /lib/erl/test/*.hrl @@ -223,12 +226,13 @@ project.lock.json /lib/java/.gradle /lib/java/android/.gradle /lib/java/build +/lib/java/out /lib/java/target /lib/js/dist /lib/js/doc /lib/js/test/build -/lib/netcore/**/bin -/lib/netcore/**/obj +/lib/netstd/**/bin +/lib/netstd/**/obj /lib/nodejs/coverage /lib/nodejs/node_modules/ /lib/perl/MANIFEST @@ -261,6 +265,7 @@ project.lock.json /lib/php/src/ext/thrift_protocol/mkinstalldirs /lib/php/src/ext/thrift_protocol/modules/ /lib/php/src/ext/thrift_protocol/php_thrift_protocol.lo +/lib/php/src/ext/thrift_protocol/php_thrift_protocol.loT /lib/php/src/ext/thrift_protocol/run-tests.php /lib/php/src/ext/thrift_protocol/thrift_protocol.la /lib/php/src/ext/thrift_protocol/tmp-php.ini @@ -273,6 +278,18 @@ project.lock.json /lib/go/src /lib/go/test/gopath/ /lib/go/test/ThriftTest.thrift +/lib/nodets/test-compiled/ +/lib/ocaml/_build/ +/lib/ocaml/_tags +/lib/ocaml/configure +/lib/ocaml/setup.data +/lib/ocaml/setup.ml +/lib/ocaml/myocamlbuild.ml +/lib/ocaml/*/META +/lib/ocaml/*/*.mllib +/lib/ocaml/*/*.mldylib +/lib/ocaml/Makefile +/lib/ocaml/OCamlMakefile /lib/rs/target/ /lib/rs/Cargo.lock /lib/rs/test/Cargo.lock @@ -283,8 +300,12 @@ project.lock.json /lib/rs/test/src/midlayer.rs /lib/rs/test/src/recursive.rs /lib/rs/test/src/ultimate.rs +/lib/rs/test/src/identifiers.rs /lib/rs/*.iml /lib/rs/**/*.iml +/lib/swift/.build +/lib/ts/test/build/ +/lib/ts/test/gen-* /libtool /ltmain.sh /missing @@ -302,8 +323,7 @@ project.lock.json /test/cpp/StressTestNonBlocking /test/cpp/TestClient /test/cpp/TestServer -/test/csharp/obj -/test/csharp/bin +/test/dart/**/.dart_tool /test/dart/**/.packages /test/dart/**/packages /test/dart/**/.pub/ @@ -313,6 +333,8 @@ project.lock.json /test/erl/.generated /test/erl/.rebar /test/erl/ebin +/test/erl/_build/ +/test/erl/rebar.lock /test/go/bin/ /test/go/ThriftTest.thrift /test/go/gopath @@ -329,9 +351,15 @@ project.lock.json /test/php/php_ext_dir/ /test/py.twisted/_trial_temp/ /test/rb/Gemfile.lock -/test/netcore/**/bin -/test/netcore/**/obj -/test/netcore/Thrift +/test/netstd/**/bin +/test/netstd/**/obj +/test/netstd/*.psess +/test/netstd/*.vspx +/test/netstd/*.vsp +/test/netstd/*.diagsession +/test/netstd/Client/ThriftTest +/test/netstd/Server/ThriftTest +/test/netstd/Thrift /test/php/php_ext_dir/ /test/rs/Cargo.lock /test/rs/src/thrift_test.rs @@ -354,10 +382,6 @@ project.lock.json /tutorial/cpp/TutorialServer /tutorial/c_glib/tutorial_client /tutorial/c_glib/tutorial_server -/tutorial/csharp/CsharpServer/obj -/tutorial/csharp/CsharpServer/bin -/tutorial/csharp/CsharpClient/obj -/tutorial/csharp/CsharpClient/bin /tutorial/d/async_client /tutorial/d/client /tutorial/d/server @@ -382,14 +406,16 @@ project.lock.json /tutorial/hs/dist/ /tutorial/java/build/ /tutorial/js/build/ -/tutorial/netcore/**/bin -/tutorial/netcore/**/obj -/tutorial/netcore/Thrift +/tutorial/netstd/**/bin +/tutorial/netstd/**/obj +/tutorial/netstd/Interfaces /tutorial/rs/*.iml /tutorial/rs/src/shared.rs /tutorial/rs/src/tutorial.rs /tutorial/rs/bin /tutorial/rs/target /tutorial/rs/Cargo.lock +/tutorial/netstd/Interfaces/shared +/tutorial/netstd/Interfaces/tutorial /ylwrap diff --git a/.travis.yml b/.travis.yml index e09b8a4a3be..f3dc7e4a708 100644 --- a/.travis.yml +++ b/.travis.yml @@ -68,18 +68,8 @@ jobs: # ========================= stage: thrift ======================= # ------------------------- phase: cross ------------------------ - # apache/thrift official PR builds can exceed 50 minutes per job so combine all cross tests - stage: thrift script: build/docker/run.sh - if: repo = apache/thrift - env: - - JOB="Cross Language Tests" - - SCRIPT="cross-test.sh" - - # fork based PR builds cannot exceed 50 minutes per job - - stage: thrift - script: build/docker/run.sh - if: repo != apache/thrift env: - JOB="Cross Language Tests (Binary Protocol)" - SCRIPT="cross-test.sh" @@ -87,7 +77,6 @@ jobs: - stage: thrift script: build/docker/run.sh - if: repo != apache/thrift env: - JOB="Cross Language Tests (Header, JSON Protocols)" - SCRIPT="cross-test.sh" @@ -95,7 +84,6 @@ jobs: - stage: thrift script: build/docker/run.sh - if: repo != apache/thrift env: - JOB="Cross Language Tests (Compact and Multiplexed Protocols)" - SCRIPT="cross-test.sh" @@ -135,23 +123,12 @@ jobs: - script: build/docker/run.sh env: - JOB="CMake" + - BUILD_ARG="-DCMAKE_BUILD_TYPE=Debug" - # C++ specific options: compiler plug-in, threading model - script: build/docker/run.sh env: - - JOB="C++98 (Boost Thread)" - - SCRIPT="cmake.sh" - - BUILD_LIBS="CPP TESTING TUTORIALS" - - BUILD_ARG="-DCMAKE_CXX_STANDARD=98 -DCMAKE_CXX_STANDARD_REQUIRED=ON -DCMAKE_CXX_EXTENSIONS=OFF --DWITH_BOOSTTHREADS=ON -DWITH_PYTHON=OFF -DWITH_C_GLIB=OFF -DWITH_JAVA=OFF -DWITH_HASKELL=OFF" - - BUILD_ENV="-e CC=clang -e CXX=clang++" - - - script: build/docker/run.sh - env: - - JOB="C++ (Std Thread) and Plugin" - - SCRIPT="cmake.sh" - - BUILD_LIBS="CPP TESTING TUTORIALS" - - BUILD_ARG="-DWITH_PLUGIN=ON -DWITH_STDTHREADS=ON -DWITH_PYTHON=OFF -DWITH_C_GLIB=OFF -DWITH_JAVA=OFF -DWITH_HASKELL=OFF" - - BUILD_ENV="-e CC=clang -e CXX=clang++" + - JOB="CMake" + - BUILD_ARG="-DCMAKE_BUILD_TYPE=Release" # ------------------------- phase: dist ------------------------- - script: build/docker/run.sh @@ -171,7 +148,17 @@ jobs: env: - JOB="Coverity Scan" - SCRIPT="covscan.sh" - + + # ------------------------- phase: swift ------------------------ + # We lint the podspec + - os: osx + osx_image: xcode11.3 + language: swift + script: + - gem update cocoapods + - pod lib lint --allow-warnings --swift-version=5.1 + env: + - JOB="pod lib lint" ### ------------------------- phase: osx -------------------------- # disabled due to the time delays it imposes on build jobs diff --git a/ApacheThrift.nuspec b/ApacheThrift.nuspec new file mode 100644 index 00000000000..61caf575398 --- /dev/null +++ b/ApacheThrift.nuspec @@ -0,0 +1,45 @@ + + + + + + + ApacheThrift + 0.14.0 + Apache Thrift 0.14.0 + Apache Thrift Developers + Apache Software Foundation + Apache-2.0 + http://thrift.apache.org/ + true + Apache Thrift .NET Library + + Contains runtime libraries from lib/netstd for netstandard2.0 framework development. + + + Apache Thrift RPC + + + + + \ No newline at end of file diff --git a/CHANGES b/CHANGES deleted file mode 100644 index e3e50b6bc85..00000000000 --- a/CHANGES +++ /dev/null @@ -1,2913 +0,0 @@ -Apache Thrift Changelog - -================================================================================ -Thrift 0.12.0 --------------------------------------------------------------------------------- -## New Languages - * Common LISP (cl) - * Swift - * Typescript (nodets) - -## Deprecated Languages - * Cocoa - -## Breaking Changes (since 0.11.0) - * [THRIFT-4529] - Rust enum variants are now camel-cased instead of uppercased to conform to Rust naming conventions - * [THRIFT-4448] - Support for golang 1.6 and earlier has been dropped. - * [THRIFT-4474] - PHP now uses the PSR-4 loader by default instead of class maps. - * [THRIFT-4532] - method signatures changed in the compiler's t_oop_generator. - * [THRIFT-4648] - The C (GLib) compiler's handling of namespaces has been improved. - -## Known Issues (Blocker or Critical) - * [THRIFT-4037] - build: use a single build system for thrift - * [THRIFT-4119] - build: bootstrap.sh is missing from source tarball - * [THRIFT-3289] - csharp: socket exhaustion in csharp implementation - * [THRIFT-3029] - cocoa: Getters for fields defined with uppercase names do not work - * [THRIFT-3325] - cocoa: Extended services aren't subclasses in generated Cocoa - * [THRIFT-4116] - cocoa: Thrift de-capitalizes the name of IsSet property in Cocoa - * [THRIFT-3877] - cpp: the http implementation is not standard; interop with other languages is spotty at best - * [THRIFT-4180] - cpp: Impossible to build Thrift C++ library for Android (NDK) - * [THRIFT-4384] - cpp: Using multiple async services simultaneously is not thread-safe - * [THRIFT-3108] - haskell: Defaulted struct parameters on a service generates invalid Haskell - * [THRIFT-3990] - nodejs: Exception swallowed by deserialization function - * [THRIFT-4214] - nodejs: map key treated as hex value in JavaScript - * [THRIFT-4602] - nodejs: ERROR in ./node_modules/thrift/lib/nodejs/lib/thrift/connection.js Module not found: Error: Can't resolve 'child_process' - * [THRIFT-4639] - nodejs: Sequence numbering for multiplexed protocol broken - * [THRIFT-1310] - php: sequence and reconnection management issues - * [THRIFT-1538] - php: Error during deserialization int64 on 32-bit architecture - * [THRIFT-1580] - php: thrift type i64 java to php serialize/deserealize not working - * [THRIFT-1950] - php: PHP gets stuck in infinite loop - * [THRIFT-2954] - python: sending int or float in a double field breaks the connection - * [THRIFT-4080] - python: unix sockets can get stuck forever - * [THRIFT-4281] - python: generated code is out of order and causes load issues - * [THRIFT-4677] - py3: UnicodeDecideError in Python3 - -## Build Process - * [THRIFT-4308] - D language docker images need demios for libevent and openssl fixed to re-enable make cross on dlang - * [THRIFT-4579] - Use Ubuntu Bionic (18.04 LTS) for CI builds instead of Artful (17.10) - * [THRIFT-4508] - Define CI operating system coverage rules for the project and (hopefully) simplify CI a little more - * [THRIFT-4397] - ubuntu install instructions broken on 16.04 - * [THRIFT-4545] - Appveyor builds are failing due to a haskell / cabal update in chocolatey - * [THRIFT-4452] - optimize Dockerfile (only onetime apt-get update) - * [THRIFT-4440] - rm `build/docker/ubuntu-trusty/Dockerfile.orig` - * [THRIFT-4352] - Ubuntu Artful doesn't appear to be compatible with Thrift and Haxe 3.4.2 - * [THRIFT-4666] - DLang Client Pool Test fails sporadically - * [THRIFT-4676] - CL tutorial build fails sporadically - * [THRIFT-4456] - Make haxelib download quiet so it doesn't blow up the build log - * [THRIFT-4605] - bootstrap.sh fails if automake=1.16.1 - -## c_glib - * [THRIFT-4648] - The C (GLib) compiler's handling of namespaces has been improved. - * [THRIFT-4622] - glibC compilation issue - * [THRIFT-4671] - c glib is unable to handle client close unexpectedly - -## cl (new language support in 0.12.0) - * [THRIFT-82] - Common Lisp support - -## csharp - * [THRIFT-4558] - reserved Csharp keywords are not escaped in some cases - * [THRIFT-4637] - C# async mode generates incorrect code with inherited services - * [THRIFT-4672] - IAsyncResult style methods not being supported by certain transports leads to issues in mixed ISync/IAsync use cases - * [THRIFT-4539] - Allow TBufferedTransport to be used as base class - * [THRIFT-4535] - XML docs; code cleanup (tabs->spaces; String->string) - * [THRIFT-4492] - protected ExceptionType type member of TApplicationException cannot be accessed - * [THRIFT-4446] - JSONProtocol Base64 Encoding Trims Padding - * [THRIFT-4455] - Missing dispose calls in ThreadedServer & ThreadpoolServer - * [THRIFT-4609] - keep InnerException wherever appropriate - * [THRIFT-4673] - IAsyncResult not supported by layered transports (buffered/framed) - -## cpp - * [THRIFT-4476] - Typecasting problem on list items - * [THRIFT-4465] - TNonblockingServer throwing THRIFT LOGGER: TConnection::workSocket(): THRIFT_EAGAIN (unavailable resources) - * [THRIFT-4680] - TBufferTransports.h does not compile under Visual Studio 2017 - * [THRIFT-4618] - TNonblockingServer crash because of limitation of select() - * [THRIFT-4620] - TZlibTransport.cpp doesn't ensure that there is enough space for the zlib flush marker in the buffer. - * [THRIFT-4571] - ZeroMQ contrib library needs a refresh - * [THRIFT-4559] - TSSLServerSocket incorrectly prints errors - * [THRIFT-4578] - Move `TAsyncProtocolProcessor` into main thrift library - * [THRIFT-4418] - evhttp_connection_new is deprecated; use evhttp_connection_base_new - -## compiler - * [THRIFT-4644] - Compiler cannot be compiled on macOS(maybe also on other platforms with clang) - * [THRIFT-4531] - Thrift generates wrong Python code for immutable structures with optional members - * [THRIFT-4513] - thrift generated code is not stable for constants - * [THRIFT-4532] - Avoid updating Thrift compiler generated code if the output has not changed - * [THRIFT-4400] - Visual Studio Compiler project should link runtime statically in release builds - * [THRIFT-4399] - plugin.thrift t_const_value is not used as a union in C++ code -- fix this - * [THRIFT-4496] - Dealing with language keywords in Thrift (e.g. service method names) - * [THRIFT-4393] - repeated runs of compiler produce different binary output at plugin interface - -## dlang - * [THRIFT-4478] - Thrift will not build with dlang 2.078 or later - * [THRIFT-4503] - dlang servers logError on normal client disconnection - * [THRIFT-4308] - D language docker images need demios for libevent and openssl fixed to re-enable make cross on dlang - -## dart - * [THRIFT-4646] - Effective Dart and Exceptions - * [THRIFT-4439] - Shouldn't download dart.deb directly. - -## delphi - * [THRIFT-4562] - Calling wrong exception CTOR leads to "call failed: unknown result" instead of the real exception being thrown - * [THRIFT-4554] - uncompileable code with member names that are also types under specific conditions - * [THRIFT-4422] - Add Async implementation via IFuture - * [THRIFT-4485] - Possible invalid ptr AV with overlapped read/write on pipes - * [THRIFT-4549] - Thrift exceptions should derive from TException - * [THRIFT-4540] - buffered transport broken when trying to re-open a formerly closed transport - * [THRIFT-4473] - Move Thrift.Console.pas out of the Library - * [THRIFT-4490] - Allow a default service as fallback for multiplex processors connected by old clients - * [THRIFT-4454] - Large writes/reads may cause range check errors in debug mode - * [THRIFT-4461] - Compiler directive should match Delphi XE4 - * [THRIFT-4462] - First line in Console duplicated - * [THRIFT-4642] - FPU ctrl word settings may cause an unexpected "denormalized" error - -## erlang - * [THRIFT-4497] - Erlang records should use map() for map type - * [THRIFT-4495] - Erlang records should allow 'undefined' for non-required fields - * [THRIFT-4580] - Fix erlang tutorial unpack on Windows - * [THRIFT-4582] - Ubuntu Xenial erlang 18.3 "make check" fails - -## golang - * [THRIFT-4448] - Support for golang 1.6 and earlier has been dropped. - * [THRIFT-4253] - Go generator assigns strings to field in const instead of pointers. - * [THRIFT-4573] - Unions Field Count Does Not Consider Binary - * [THRIFT-4447] - Golang: Panic on p.c.Call when using deprecated initializers - * [THRIFT-4650] - Required field incorrectly marked as set when fieldType does not match - * [THRIFT-4486] - Golang: -remote.go client cleanup - * [THRIFT-4537] - TSimpleServer can exit Accept loop with lock still acquired - * [THRIFT-4516] - Add support for go 1.10 - * [THRIFT-4421] - golang tests rely on gomock, which has change behaviour, causing tests to fail - * [THRIFT-4626] - Communication crash when using binary/compact protocol and zlib transport - * [THRIFT-4659] - golang race detected when closing listener socket - -## haskell - * [THRIFT-4634] - Haskell builds with older cabal cannot reconcile complex version requirements - -## java - * [THRIFT-4259] - Thrift does not compile due to Ant Maven task errors - * [THRIFT-1418] - Compiling Thrift from source: Class org.apache.tools.ant.taskdefs.ConditionTask doesn't support the nested "typefound" element - * [THRIFT-4530] - proposal: add nullability annotations to generated Java code - * [THRIFT-4614] - Generate missing @Nullable annotations for Java iterator getters - * [THRIFT-4555] - Getter of binary field in Java creates unnecessary copy - * [THRIFT-3983] - libthrift is deployed on central with pom packaging instead of jar - * [THRIFT-4294] - Java Configure Fails for Ant >= 1.10 - * [THRIFT-4178] - Java libraries missing from package when using cmake - * [THRIFT-4120] - pom files are not generated or provided in the build - * [THRIFT-1507] - Maven can't download resource from central when behind a proxy and won't use local repository - * [THRIFT-4556] - Optional rethrow of unhandled exceptions in java processor - * [THRIFT-4337] - Able to set keyStore and trustStore as InputStream in the TSSLTransportFactory.TSSLTransportParameters - * [THRIFT-4566] - Pass message of unhandled exception to optional rethrow. - * [THRIFT-4506] - Remove assertion in Java SASL code that would be ignored in release builds - * [THRIFT-4470] - Include popular IDE file templates to gitignore - * [THRIFT-4429] - Make TThreadPoolServer.executorService_ available in inherited classes and refactor methods to be able customization - * [THRIFT-3769] - Fix logic of THRIFT-2268 - * [THRIFT-4494] - Increase Java Socket Buffer Size - * [THRIFT-4499] - Remove Magic Number In TFIleTransport - -## js - * [THRIFT-4406] - JavaScript: Use modern Promise implementations - * [THRIFT-4625] - let / const variable decorators for es6 compiler - * [THRIFT-4653] - ES6 Classes - * [THRIFT-4592] - JS: readI32 performance on large arrays is very poor in Chrome - * [THRIFT-4509] - js and nodejs libraries need to be refreshed with current libraries - * [THRIFT-4403] - thrift.js: Incorrect usage of 'this' in TWebSocketTransport.__onOpen - * [THRIFT-4436] - Deserialization of nested list discards content - * [THRIFT-4437] - JS WebSocket client callbacks invoked twice on parallel requests - * [THRIFT-4679] - Duplicate declaration of InputBufferUnderrunError in lib/nodejs/lib/thrift/json_protocol.js - * [THRIFT-4551] - Add prettier for consistent JS code formatting - -## lua - * [THRIFT-4591] - lua client uses two write() calls per framed message send - * [THRIFT-3863] - Can't "make install" Lua Library - -## netcore - * [THRIFT-4524] - .NET Core Server doesn't close properly when cancelled - * [THRIFT-4434] - Update .NET Core components, add tests for .Net Core library and .Net Core compiler, fix bugs and build process - * [THRIFT-4446] - JSONProtocol Base64 Encoding Trims Padding - -## node.js - * [THRIFT-4225] - Error handling malformed arguments leaks memory, corrupts transport buffers causing next RPC to fail - * [THRIFT-3950] - Memory leak while calling oneway method - * [THRIFT-3143] - add typescript directory support - * [THRIFT-4564] - TBufferedTransport can leave corrupt data in the buffer - * [THRIFT-4647] - Node.js Fileserver webroot path - * [THRIFT-4489] - Unix domain socket support for NodeJS client - * [THRIFT-4443] - node.js json_protocol throws error in skip function - * [THRIFT-4604] - NodeJS: Expose Int64 from browser.js for consumption by browser - * [THRIFT-4480] - NodeJS warning on binary_protocol writeMessageEnd when seqid = 0 - -## perl - * [THRIFT-4382] - Replace the use of Perl Indirect Object Syntax calls to new() - * [THRIFT-4471] - Thrift CPAN release is missing Makefile.PL and the clients are unable to build the module - * [THRIFT-4416] - Perl CPAN Packaging Improvements - -## php - * [THRIFT-4474] - PHP generator use PSR-4 default - * [THRIFT-4463] - PHP generated code match PSR-2 - * [THRIFT-4373] - Extending Thrift class results in "Attempt serialize from non-Thrift object" - * [THRIFT-4354] - TSocket block on read - * [THRIFT-4423] - migrate php library to psr-4 - * [THRIFT-4656] - infinite loop in latest PHP library - * [THRIFT-4477] - TBufferedTransport must have underlying transport - * [THRIFT-4475] - lib/php/test should be checked for PSR-2 - * [THRIFT-4498] - add phpcs back - * [THRIFT-4460] - php library use PSR-2 - * [THRIFT-4641] - TCurlClient doesn't check for HTTP status code - * [THRIFT-4645] - TCurlClient: show actual error message when throwing TTransportException - * [THRIFT-4674] - Add stream context support into PHP/THttpClient - * [THRIFT-4459] - reduce php library directory depth - -## python - * [THRIFT-4670] - Twisted, slots, and void method fails with "object has no attribute 'success'" - * [THRIFT-4464] - Potentially server-crashing typo in Python TNonblockingServer - * [THRIFT-4548] - Supporting TBinaryProtocolAccelerated protocol when using TMultiplexedProcessor in Python - * [THRIFT-4577] - Outdated cipher string in python unit test - * [THRIFT-4505] - python build on Vagrant Windows boxes fails - * [THRIFT-4621] - THeader for Python - * [THRIFT-4668] - make socket backlog configurable for python - * [THRIFT-4561] - Python: cleanup socket timeout settings - -## ruby - * [THRIFT-4289] - Thrift RSpec test suite fails with Ruby 2.4.x due to Fixnum deprecation - * [THRIFT-4342] - Support ruby rspec 3 - * [THRIFT-4525] - Add ssl socket option to ruby cross tests - * [THRIFT-4450] - Add seek support to TCompactInputProtocol in Rust - * [THRIFT-4631] - Codegen Creates Invalid Ruby for Recursive Structs - * [THRIFT-4472] - Fix the genspec for ruby so it does not complain about an invalid license - -## rust - * [THRIFT-4662] - Rust const string calls function at compile time - * [THRIFT-4661] - Rust enum name wrong case in generated structs - * [THRIFT-4617] - Avoid generating conflicting struct names in Rust code - * [THRIFT-4529] - Rust generation should include #![allow(non_snake_case)] or force conform to Rust style guidelines - * [THRIFT-4390] - Rust binary protocol and buffered transport cannot handle writes above 4096 bytes - * [THRIFT-4419] - Rust framed transport cannot handle writes above 4096 bytes - * [THRIFT-4658] - Rust's TBinaryInputProtocol fails when strict is false - * [THRIFT-4187] - Dart -> Rust Framed cross tests fail - * [THRIFT-4664] - Rust cannot create ReadHalf/WriteHalf to implement custom tranports - -## swift (new language support in 0.12.0) - * [THRIFT-3773] - Swift Library - -## test suite - * [THRIFT-4515] - Gracefully shutdown cross-test servers to fully test teardown - * [THRIFT-4085] - Add .NET Core to the make cross standard test suite - * [THRIFT-4358] - Add unix domain sockets in ruby to cross test - code exists - -## typescript (new language support in 0.12.0) - * [THRIFT-3143] - add typescript directory support - -================================================================================ -Thrift 0.11.0 --------------------------------------------------------------------------------- -## Sub-task - * [THRIFT-2733] - Erlang coding standards - * [THRIFT-2740] - Perl coding standards - * [THRIFT-3610] - Streamline exception handling in Python server handler - * [THRIFT-3686] - Java processor should report internal error on uncaught exception - * [THRIFT-4049] - Skip() should throw TProtocolException.INVALID_DATA on unknown data types - * [THRIFT-4053] - Skip() should throw TProtocolException.INVALID_DATA on unknown data types - * [THRIFT-4136] - Align is_binary() method with is_string() to simplify those checks - * [THRIFT-4137] - Fix remaining undefined behavior invalid vptr casts in Thrift Compiler - * [THRIFT-4138] - Fix remaining undefined behavior invalid vptr casts in C++ library - * [THRIFT-4296] - Fix Ubuntu Xenial build environment for the python language - * [THRIFT-4298] - Fix Ubuntu Xenial build environment for the go 1.6 language - * [THRIFT-4299] - Fix Ubuntu Xenial build environment for the D language - * [THRIFT-4300] - Fix make cross in Ubuntu Xenial docker environment, once all language support issues are fixed - * [THRIFT-4302] - Fix Ubuntu Xenial make cross testing for lua and php7 - * [THRIFT-4398] - Update EXTRA_DIST for "make dist" - -## Bug - * [THRIFT-381] - Fail fast if configure detects C++ problems - * [THRIFT-1677] - MinGW support broken - * [THRIFT-1805] - Thrift should not swallow ALL exceptions - * [THRIFT-2026] - Fix TCompactProtocol 64 bit builds - * [THRIFT-2642] - Recursive structs don't work in python - * [THRIFT-2889] - stable release 0.9.2, erlang tutorial broken - * [THRIFT-2913] - Ruby Server Thrift::ThreadPoolServer should serve inside a thread - * [THRIFT-2998] - Node.js: Missing header from http request - * [THRIFT-3000] - .NET implementation has trouble with mixed IP modes - * [THRIFT-3281] - Travis CI build passed but the log says BUILD FAILED - * [THRIFT-3358] - Makefile:1362: *** missing separator. Stop. - * [THRIFT-3600] - Make TTwisted server send exception on unexpected handler error - * [THRIFT-3602] - Make Tornado server send exception on unexpected handler error - * [THRIFT-3657] - D TFileWriterTransport close should use non-priority send - * [THRIFT-3700] - Go Map has wrong default value when optional - * [THRIFT-3703] - Unions Field Count Does Not Consider Map/Set/List Fields - * [THRIFT-3730] - server log error twice - * [THRIFT-3778] - go client can not pass method parameter to server of other language if no field_id is given - * [THRIFT-3784] - thrift-maven-plugin generates invalid include directories for IDL in dependency JARs - * [THRIFT-3801] - Node Thrift client throws exception with multiplexer and responses that are bigger than a single buffer - * [THRIFT-3821] - TMemoryBuffer buffer may overflow when resizing - * [THRIFT-3832] - Thrift version 0.9.3 example on Windows, Visual Studio, linking errors during compiling - * [THRIFT-3847] - thrift/config.h includes a #define for VERSION which will likely conflict with existing user environment or code - * [THRIFT-3873] - Fix various build warnings when using Visual Studio - * [THRIFT-3891] - TNonblockingServer configured with more than one IO threads does not always return from serve() upon stop() - * [THRIFT-3892] - Thrift uses TLS SNI extension provided by OpenSSL library. Older version of OpenSSL(< 0.9.8f) may create problem because they do not support 'SSL_set_tlsext_host_name()'. - * [THRIFT-3895] - Build fails using Java 1.8 with Ant < 1.9 - * [THRIFT-3896] - map data with number string key cannot access that deserialized by php extension - * [THRIFT-3938] - Python TNonblockingServer does not work with SSL - * [THRIFT-3944] - TSSLSocket has dead code in checkHandshake - * [THRIFT-3946] - Java 1.5 compatibility broken for binary fields (java5 option) - * [THRIFT-3960] - Inherited services in Lua generator are not named correctly - * [THRIFT-3962] - Ant build.xml broken on Windows for Java library - * [THRIFT-3963] - Thrift.cabal filename does not match module name - * [THRIFT-3967] - gobject/gparam.h:166:33: warning: enumerator value for ‘G_PARAM_DEPRECATED’ is not an integer constant expression - * [THRIFT-3968] - Deserializing empty string/binary fields - * [THRIFT-3974] - Using clang-3.8 and ThreadSanitizer on the concurrency_test claims bad PThread behavior - * [THRIFT-3984] - PHP7 extension causes segfault - * [THRIFT-4008] - broken ci due to upstream dependency versioning break - * [THRIFT-4009] - Use @implementer instead of implements in TTwisted.py - * [THRIFT-4010] - Q.fcall messing up with *this* pointer inside called function - * [THRIFT-4011] - Sets of Thrift structs generate Go code that can't be serialized to JSON - * [THRIFT-4012] - Python Twisted implementation uses implements, not compatible with Py3 - * [THRIFT-4014] - align C# meta data in AssemblyInfo.cs - * [THRIFT-4015] - Fix wrongly spelled "Thirft"s - * [THRIFT-4016] - testInsanity() impl does not conform to test spec in ThriftTest.thrift - * [THRIFT-4023] - Skip unexpected field types on read/write - * [THRIFT-4024] - Skip() should throw on unknown data types - * [THRIFT-4026] - TSSLSocket doesn't work with Python < 2.7.9 - * [THRIFT-4029] - Accelerated protocols do not build from thrift-py 0.10.0 on PyPI - * [THRIFT-4031] - Go plugin generates invalid code for lists of typedef'ed built-in types - * [THRIFT-4033] - Default build WITH_PLUGIN=ON for all builds results in packaging errors - * [THRIFT-4034] - CMake doesn't work to build compiler on MacOS - * [THRIFT-4036] - Add .NET Core environment/build support to the docker image - * [THRIFT-4038] - socket check: checking an unsigned number against >= 0 never fails - * [THRIFT-4042] - ExtractionError when using accelerated thrift in a multiprocess test - * [THRIFT-4043] - thrift perl debian package is placing files in the wrong place - * [THRIFT-4044] - Build job 17 failing on every pull request; hspec core (haskell) 2.4 issue - * [THRIFT-4046] - MinGW with gcc 6.2 does not compile on Windows - * [THRIFT-4060] - Thrift printTo ostream overload mechanism breaks down when types are nested - * [THRIFT-4062] - Remove debug print from TServiceClient - * [THRIFT-4065] - Document Perl ForkingServer signal restriction imposed by THRIFT-3848 and remove unnecessary code - * [THRIFT-4068] - A code comment in Java ServerSocket is wrong around accept() - * [THRIFT-4073] - enum files are still being generated with unused imports - * [THRIFT-4076] - Appveyor builds failing because ant 1.9.8 was removed from apache servers - * [THRIFT-4077] - AI_ADDRCONFIG redefined after recent change to PlatformSocket header - * [THRIFT-4079] - Generated perl code that returns structures from included thrift files is missing a necessary use clause - * [THRIFT-4087] - Spurious exception destroying TThreadedServer because of incorrect join() call - * [THRIFT-4102] - TBufferedTransport performance issue since 0.10.0 - * [THRIFT-4106] - concurrency_test fails randomly - * [THRIFT-4108] - c_glib thrift ssl has multiple bugs and deprecated functions - * [THRIFT-4109] - Configure Script uses string comparison for versions - * [THRIFT-4129] - C++ TNonblockingServer fd leak when failing to dispatch new connections - * [THRIFT-4131] - Javascript with WebSocket handles oneway methods wrong - * [THRIFT-4134] - Fix remaining undefined behavior invalid vptr casts - * [THRIFT-4140] - Use of non-thread-safe function gmtime() - * [THRIFT-4141] - Installation of haxe in docker files refers to a redirect link and fails - * [THRIFT-4147] - Rust: protocol should accept transports with non-static lifetime - * [THRIFT-4148] - [maven-thrift-plugin] compile error while import a thrift in dependency jar file. - * [THRIFT-4149] - System.out pollutes log files - * [THRIFT-4154] - PHP close() of a TSocket needs to close any type of socket - * [THRIFT-4158] - minor issue in README-MSYS2.md - * [THRIFT-4159] - Building tests fails on MSYS2 (MinGW64) due to a (small?) linker error - * [THRIFT-4160] - TNonblocking server fix use of closed/freed connections - * [THRIFT-4161] - TNonBlocking server using uninitialized event in error paths - * [THRIFT-4162] - TNonBlocking handling of TSockets in error state is incorrect after fd is closed - * [THRIFT-4164] - Core in TSSLSocket cleanupOpenSSL when destroying a mutex used by openssl - * [THRIFT-4165] - C++ build has many warnings under c++03 due to recent changes, cmake needs better platform-independent language level control - * [THRIFT-4166] - Recent fix to remove boost::lexical_cast usage broke VS2010 - * [THRIFT-4167] - Missing compile flag - * [THRIFT-4170] - Support lua 5.1 or earlier properly for object length determination - * [THRIFT-4172] - node.js tutorial client does not import assert, connection issues are not handled properly - * [THRIFT-4177] - Java compiler produces deep copy constructor that could make shallow copy instead - * [THRIFT-4184] - Building on Appveyor: invalid escape sequence \L - * [THRIFT-4185] - fb303 counter encoding fix - * [THRIFT-4189] - Framed/buffered transport Dispose() does not dispose the nested transport - * [THRIFT-4193] - Lower the default maxReadBufferBytes for non-blocking servers - * [THRIFT-4195] - Compilation to GO produces broken code - * [THRIFT-4196] - Cannot generate recursive Rust types - * [THRIFT-4204] - typo in compact spec - * [THRIFT-4206] - Strings in container fields are not decoded properly with py:dynamic and py:utf8strings - * [THRIFT-4208] - C# NamedPipesServer not really working in some scenarios - * [THRIFT-4211] - Fix GError glib management under Thrift - * [THRIFT-4212] - c_glib flush tries to close SSL even if socket is invalid - * [THRIFT-4213] - Travis build fails at curl -sSL https://www.npmjs.com/install.sh | sh - * [THRIFT-4215] - Golang TTransportFactory Pattern Squelches Errors - * [THRIFT-4216] - Golang Http Clients Do Not Respect User Options - * [THRIFT-4218] - Set TCP_NODELAY for PHP client socket - * [THRIFT-4219] - Golang HTTP clients created with Nil buffer - * [THRIFT-4231] - TJSONProtocol throws unexpected non-Thrift-exception on null strings - * [THRIFT-4232] - ./configure does bad ant version check - * [THRIFT-4234] - Travis build fails cross language tests with "Unsupported security protocol type" - * [THRIFT-4237] - Go TServerSocket Race Conditions - * [THRIFT-4240] - Go TSimpleServer does not close properly - * [THRIFT-4243] - Go TSimpleServer race on wait in Stop() method - * [THRIFT-4245] - Golang TFramedTransport's writeBuffer increases if writes to transport failed - * [THRIFT-4246] - Sequence number mismatch on multiplexed clients - * [THRIFT-4247] - Compile fails with openssl 1.1 - * [THRIFT-4248] - Compile fails - strncpy, memcmp, memset not declared in src/thrift/transport/TSSLSocket.cpp - * [THRIFT-4251] - Java Epoll Selector Bug - * [THRIFT-4257] - Typescript async callbacks do not provide the correct types - * [THRIFT-4258] - Boost/std thread wrapping faultiness - * [THRIFT-4260] - Go context generation issue. Context is parameter in Interface not in implementation - * [THRIFT-4261] - Go context generation issue: breaking change in generated code regarding thrift.TProcessorFunction interface - * [THRIFT-4262] - Invalid binding to InterlockedCompareExchange64() with 64-bit targets - * [THRIFT-4263] - Fix use after free bug for thrown exceptions - * [THRIFT-4266] - Erlang library throws during skipping fields of composite type (maps, lists, structs, sets) - * [THRIFT-4268] - Erlang library emits debugging output in transport layer - * [THRIFT-4273] - erlang:now/0: Deprecated BIF. - * [THRIFT-4274] - Python feature tests for SSL/TLS failing - * [THRIFT-4279] - Wrong path in include directive in generated Thrift sources - * [THRIFT-4283] - TNamedPipeServer race condition in interrupt - * [THRIFT-4284] - File contains a NBSP: lib/nodejs/lib/thrift/web_server.js - * [THRIFT-4290] - C# nullable option generates invalid code for non-required enum field with default value - * [THRIFT-4292] - TimerManager::remove() is not implemented - * [THRIFT-4307] - Make ssl-open timeout effective in golang client - * [THRIFT-4312] - Erlang client cannot connect to Python server: exception error: econnrefused - * [THRIFT-4313] - Program code of the Erlang tutorial files contain syntax errors - * [THRIFT-4316] - TByteBuffer.java will read too much data if a previous read returns fewer bytes than requested - * [THRIFT-4319] - command line switch for "evhttp" incorrectly resolved to anon pipes - * [THRIFT-4323] - range check errors or NPE in edge cases - * [THRIFT-4324] - field names can conflict with local vars in generated code - * [THRIFT-4328] - Travis CI builds are timing out (job 1) and haxe builds are failing since 9/11 - * [THRIFT-4329] - c_glib Doesn't have a multiplexed processor - * [THRIFT-4331] - C++: TSSLSockets bug in handling huge messages, bug in handling polling - * [THRIFT-4332] - Binary protocol has memory leaks - * [THRIFT-4334] - Perl indentation incorrect when defaulting field attribute to a struct - * [THRIFT-4339] - Thrift Framed Transport in Erlang crashes server when client disconnects - * [THRIFT-4340] - Erlang fix a crash on client close - * [THRIFT-4355] - Javascript indentation incorrect when defaulting field attribute to a struct - * [THRIFT-4356] - thrift_protocol call Transport cause Segmentation fault - * [THRIFT-4359] - Haxe compiler looks like it is producing incorrect code for map or set key that is binary type - * [THRIFT-4362] - Missing size-check can lead to huge memory allocation - * [THRIFT-4364] - Website contributing guide erroneously recommends submitting patches in JIRA - * [THRIFT-4365] - Perl generated code uses indirect object syntax, which occasionally causes compilation errors. - * [THRIFT-4367] - python TProcessor.process is missing "self" - * [THRIFT-4370] - Ubuntu Artful cppcheck and flake8 are more stringent and causing SCA build job failures - * [THRIFT-4372] - Pipe write operations across a network are limited to 65,535 bytes per write. - * [THRIFT-4374] - cannot load thrift_protocol due to undefined symbol: _ZTVN10__cxxabiv120__si_class_type_infoE - * [THRIFT-4376] - Coverity high impact issue resolution - * [THRIFT-4377] - haxe. socket handles leak in TSimpleServer - * [THRIFT-4381] - Wrong isset bitfield value after transmission - * [THRIFT-4385] - Go remote client -u flag is broken - * [THRIFT-4392] - compiler/..../plugin.thrift structs mis-ordered blows up ocaml generator - * [THRIFT-4395] - Unable to build in the ubuntu-xenial docker image: clap 2.28 requires Rust 1.20 - * [THRIFT-4396] - inconsistent (or plain wrong) version numbers in master/trunk - -## Documentation - * [THRIFT-4157] - outdated readme about Haxe installation on Linux - -## Improvement - * [THRIFT-105] - make a thrift_spec for a structures with negative tags - * [THRIFT-281] - Cocoa library code needs comments, badly - * [THRIFT-775] - performance improvements for Perl - * [THRIFT-2221] - Generate c++ code with std::shared_ptr instead of boost::shared_ptr. - * [THRIFT-2364] - OCaml: Use Oasis exclusively for build process - * [THRIFT-2504] - TMultiplexedProcessor should allow registering default processor called if no service name is present - * [THRIFT-3207] - Enable build with OpenSSL 1.1.0 series - * [THRIFT-3272] - Perl SSL Authentication Support - * [THRIFT-3357] - Generate EnumSet/EnumMap where elements/keys are enums - * [THRIFT-3369] - Implement SSL/TLS support on C with c_glib - * [THRIFT-3467] - Go Maps for Thrift Sets Should Have Values of Type struct{} - * [THRIFT-3580] - THeader for Haskell - * [THRIFT-3627] - Missing basic code style consistency of JavaScript. - * [THRIFT-3706] - There's no support for Multiplexed protocol on c_glib library - * [THRIFT-3766] - Add getUnderlyingTransport() to TZlibTransport - * [THRIFT-3776] - Go code from multiple thrift files with the same namespace - * [THRIFT-3823] - Escape documentation while generating non escaped documetation - * [THRIFT-3854] - allow users to clear read buffers - * [THRIFT-3859] - Unix Domain Socket Support in Objective-C - * [THRIFT-3921] - C++ code should print enums as strings - * [THRIFT-3926] - There should be an error emitted when http status code is not 200 - * [THRIFT-4007] - Micro-optimization of TTransport.py - * [THRIFT-4040] - Add real cause of TNonblockingServerSocket error to exception - * [THRIFT-4064] - Update node library dependencies - * [THRIFT-4069] - All perl packages should have proper namespace, version syntax, and use proper thrift exceptions - * [THRIFT-4071] - Consolidate the Travis CI jobs where possible to put less stress on the Apache Foundation's allocation of CI build slaves - * [THRIFT-4072] - Add the possibility to send custom headers in TCurlClient - * [THRIFT-4075] - Better MinGW support for headers-only boost (without thread library) - * [THRIFT-4081] - Provide a MinGW 64-bit Appveyor CI build for better pull request validation - * [THRIFT-4084] - Improve SSL security in thrift by adding a make cross client that checks to make sure SSLv3 protocol cannot be negotiated - * [THRIFT-4095] - Add multiplexed protocol to Travis CI for make cross - * [THRIFT-4099] - Auto-derive Hash for generated Rust structs - * [THRIFT-4110] - The debian build files do not produce a "-dbg" package for debug symbols of libthrift0 - * [THRIFT-4114] - Space after '///' in doc comments - * [THRIFT-4126] - Validate objects in php extension - * [THRIFT-4130] - Ensure Apache Http connection is released back to pool after use - * [THRIFT-4151] - Thrift Mutex Contention Profiling (pthreads) should be disabled by default - * [THRIFT-4176] - Implement a threaded and threadpool server type for Rust - * [THRIFT-4183] - Named pipe client blocks forever on Open() when there is no server at the other end - * [THRIFT-4190] - improve C# TThreadPoolServer defaults - * [THRIFT-4197] - Implement transparent gzip compression for HTTP transport - * [THRIFT-4198] - Ruby should log Thrift internal errors to global logger - * [THRIFT-4203] - thrift server stop gracefully - * [THRIFT-4205] - c_glib is not linking against glib + gobject - * [THRIFT-4209] - warning CS0414 in T[TLS]ServerSocket.cs - * [THRIFT-4210] - include Thrift.45.csproj into CI runs - * [THRIFT-4217] - HttpClient should support gzip and deflate - * [THRIFT-4222] - Support Unix Domain Sockets in Golang TServerSocket - * [THRIFT-4233] - Make THsHaServer.invoker available (get method only) in inherited classes - * [THRIFT-4236] - Support context in go generated code. - * [THRIFT-4238] - JSON generator: make annotation-aware - * [THRIFT-4269] - Don't append '.' to Erlang namespace if it ends in '_'. - * [THRIFT-4270] - Generate Erlang mapping functions for const maps and lists - * [THRIFT-4275] - Add support for zope.interface only, apart from twisted support. - * [THRIFT-4285] - Pull generated send/recv into library to allow behaviour to be customised - * [THRIFT-4287] - Add c++ compiler "no_skeleton" flag option - * [THRIFT-4288] - Implement logging levels properly for node.js - * [THRIFT-4295] - Refresh the Docker image file suite for Ubuntu, Debian, and CentOS - * [THRIFT-4305] - Emit ddoc for generated items - * [THRIFT-4306] - Thrift imports not replicated to D service output - * [THRIFT-4315] - Add default message for TApplicationException - * [THRIFT-4318] - Delphi performance improvements - * [THRIFT-4325] - Simplify automake cross compilation by relying on one global THRIFT compiler path - * [THRIFT-4327] - Improve TimerManager API to allow removing specific task - * [THRIFT-4330] - Allow unused crates in Rust files - * [THRIFT-4333] - Erlang tutorial examples are using a different port (9999) - * [THRIFT-4343] - Change CI builds to use node.js 8.x LTS once available - * [THRIFT-4345] - Create a docker build environment that uses the minimum supported language levels - * [THRIFT-4346] - Allow Zlib transport factory to wrap other transports - * [THRIFT-4348] - Perl HTTP Client custom HTTP headers - * [THRIFT-4350] - Update netcore build for dotnet 2.0 sdk and make cross validation - * [THRIFT-4351] - Use Travis CI Build Stages to optimize the CI build - * [THRIFT-4353] - cannot read via thrift_protocol at server side - * [THRIFT-4378] - add set stopTimeoutUnit method to TThreadPoolServer - -## New Feature - * [THRIFT-750] - C++ Compiler Virtual Function Option - * [THRIFT-2945] - Implement support for Rust language - * [THRIFT-3857] - thrift js:node complier support an object as parameter not an instance of struct - * [THRIFT-3933] - Port official C# .NET library for Thrift to C# .NET Core libary - * [THRIFT-4039] - Update of Apache Thrift .Net Core lib - * [THRIFT-4113] - Provide a buffer transport for reading/writing in memory byte stream - -## Question - * [THRIFT-2956] - autoconf - possibly undefined macro - AC_PROG_BISON - * [THRIFT-4223] - Add support to the isServing() method for the C++ library - -## Task - * [THRIFT-3622] - Fix deprecated uses of std::auto_ptr - * [THRIFT-4028] - Please remove System.out.format from the source code - * [THRIFT-4186] - Build and test rust client in Travis - -## Test - * [THRIFT-4264] - PHP - Support both shared & static linking of sockets library - -## Wish - * [THRIFT-4344] - Define and maintain the minimum language level for all languages in one place - - -Thrift 0.10.0 --------------------------------------------------------------------------------- -## Bug - * [THRIFT-1840] - Thrift Generated Code Causes Global Variable Leaks - * [THRIFT-1828] - moc_TQTcpServer.cpp was removed from source tree but is in thrift-0.9.0.tar.gz - * [THRIFT-1790] - cocoa: Duplicate interface definition error - * [THRIFT-1776] - TPipeServer should implement "listen", so that TServerEventHandler preServe will work right - * [THRIFT-1351] - Compiler does not care about binary strings - * [THRIFT-1229] - Python fastbinary.c can not handle unicode as generated python code - * [THRIFT-749] - C++ TBufferedTransports do not flush their buffers on delete - * [THRIFT-747] - C++ TSocket->close calls shutdown breaking forked parent process - * [THRIFT-732] - server exits abnormally when client calls send_xxx function without calling recv_xxx function - * [THRIFT-3942] - TSSLSocket does not honor send and receive timeouts - * [THRIFT-3941] - WinXP version of thrift_poll() relies on undefined behavior by passing a destructed variable to select() - * [THRIFT-3940] - Visual Studio project file for compiler is broken - * [THRIFT-3943] - Coverity Scan identified some high severity defects - * [THRIFT-3929] - PHP "nsglobal" Option Results in Syntax Error in Generated Code (Trailing Backslash) - * [THRIFT-3936] - Cannot compile 0.10.0 development tip with VS2013 and earlier (snprintf, uint32_t) - * [THRIFT-3935] - Incorrect skipping of map and set - * [THRIFT-3920] - Ruby: Ensuring that HTTP failures will clear the http transport outbuf var - * [THRIFT-3919] - C# TTLSServerSocket does not use clientTimeout - * [THRIFT-3917] - Check backports.ssl_match_hostname module version - * [THRIFT-3909] - Fix c_glib static lib CMake build - * [THRIFT-3904] - Typo in node tutorial leads to wrong transport being used - * [THRIFT-3848] - As an implementer of a perl socket server, I do not want to have to remember to ignore SIGCHLD for it to work properly - * [THRIFT-3844] - thrift_protocol cannot compile in 7.0.7 - * [THRIFT-3843] - integer issues with Haxe PHP targets cause ZigZag encoding to fail - * [THRIFT-3842] - Dart generates incorrect code for a const struct - * [THRIFT-3841] - dart compact protocol incorrectly serializes/deserialized doubles - * [THRIFT-3708] - NameError: global name 'TProtocol' is not defined - * [THRIFT-3704] - "TConnectedClient died: Could not refill buffer" message shown when using HTTP Server - * [THRIFT-3678] - Fix javadoc errors on JDK 8 - * [THRIFT-3014] - AppVeyor support - * [THRIFT-2994] - Node.js TJSONProtocol cannot be used for object serialization. - * [THRIFT-2974] - writeToParcel throws NPE for optional enum fields - * [THRIFT-2948] - Python TJSONProtocol doesn't handle structs with binary fields containing invalid unicode. - * [THRIFT-2845] - ChildService.Plo: No such file or directory - * [THRIFT-3276] - Binary data does not decode correctly using the TJSONProtocol when the base64 encoded data is padded. - * [THRIFT-3253] - Using latest version of D gives deprecation notices - * [THRIFT-2883] - TTwisted.py, during ConnectionLost processing: exceptions.RuntimeError: dictionary changed size during iteration - * [THRIFT-2019] - Writing on a disconnected socket on Mac causes SIG PIPE - * [THRIFT-2020] - Thrift library has some empty files that haven't really been deleted - * [THRIFT-2049] - Go compiler doesn't build on native Windows - * [THRIFT-2024] - TServer.cpp warns on 64-bit platforms about truncating an rlim_t into an int - * [THRIFT-2023] - gettimeofday implementation on Windows errors when no time zone is passed in. - * [THRIFT-2022] - CoB and dense code generation still uses TR1 bind, even though that doesn't work with clang - * [THRIFT-2027] - Minor 64-bit and NOMINMAX issues in C++ library - * [THRIFT-2156] - TServerSocket::listen() is throwing exceptions with misleading information - * [THRIFT-2154] - Missing #deepCopy should return T - * [THRIFT-3157] - TBase signature should be TBase, F extends TFieldIdEnum> - * [THRIFT-3156] - Node TLS: server executes processing logic two full times - * [THRIFT-3154] - tutorial/py.tornado throw EOF exception - * [THRIFT-3063] - C++ build -Wunused-parameter warnings on processor_test, TransportTest - * [THRIFT-3056] - Add string/collection length limits for Python protocol readers - * [THRIFT-3237] - Fix TNamedPipeServer::createNamedPipe memory leak - * [THRIFT-3233] - Fix C++ ThreadManager::Impl::removeWorker worker join - * [THRIFT-3232] - Cannot deserialize json messages created with fieldNamesAsString - * [THRIFT-3206] - Fix Visual Studio build failure due 'pthread_self': identifier not found - * [THRIFT-3200] - JS and nodejs do not encode JSON protocol binary fields as base64 - * [THRIFT-3199] - Exception field has basic metadata - * [THRIFT-3182] - TFramedTransport is in an invalid state after frame size exception - * [THRIFT-2536] - new TSocket, uninitialised value reported by valgrind - * [THRIFT-2527] - Apache Thrift IDL Compiler code generated for Node.js should be jshint clean - * [THRIFT-2519] - "processor" class is not being generated - * [THRIFT-2431] - TFileTransportTest fails with "check delta < XXX failed" - * [THRIFT-2708] - Erlang library does not support "oneway" message type - * [THRIFT-3377] - Deep copy is actually shallow when using typedef members - * [THRIFT-3376] - C# and Python JSON protocol double values lose precision - * [THRIFT-3373] - Various fixes for cross test servers and clients - * [THRIFT-3370] - errno extern variable redefined. Not compiling for Android - * [THRIFT-3379] - Potential out of range panic in Go JSON protocols - * [THRIFT-3371] - Abstract namespace Unix domain sockets broken in C++ - * [THRIFT-3380] - nodejs: 0.9.2 -> 0.9.3 upgrade breaks Protocol and Transport requires - * [THRIFT-3367] - Fix bad links to coding_standards.md #634 - * [THRIFT-3401] - Nested collections emit Objective-C code that cannot compile - * [THRIFT-3403] - JSON String reader doesn't recognize UTF-16 surrogate pairs - * [THRIFT-3362] - make check fails for C++ at the SecurityTest - * [THRIFT-3395] - Cocoa compiler produces corrupt code when boxing enums inside map. - * [THRIFT-3394] - compiler generates uncompilable code - * [THRIFT-3388] - hash doesn't work on set/list - * [THRIFT-3391] - Wrong bool formatting in test server - * [THRIFT-3390] - TTornado server doesn't handle closed connections properly - * [THRIFT-3382] - TBase class for C++ Library - * [THRIFT-3392] - Java TZlibTransport does not close its wrapper streams upon close() - * [THRIFT-3383] - i64 related warnings - * [THRIFT-3386] - misc. warnings with make check - * [THRIFT-3385] - warning: format ‘%lu’ expects ‘long unsigned int’, but has type ‘std::basic_string::size_type {aka unsigned int} - * [THRIFT-3355] - npm WARN package.json thrift@1.0.0-dev No license field. - * [THRIFT-3360] - Improve cross test servers and clients further - * [THRIFT-3359] - Binary field incompatibilities - * [THRIFT-3354] - Fix word-extraction substr bug in initialism code - * [THRIFT-3350] - Python JSON protocol does not encode binary as Base64 - * [THRIFT-3577] - assertion failed at line 512 of testcontainertest.c - * [THRIFT-3576] - Boost test --log_format arg does not accept lowercase - * [THRIFT-3575] - Go compiler tries to use unexported library methods when using read_write_private - * [THRIFT-3574] - Cocoa generator makes uncompilable imports - * [THRIFT-3570] - Remove duplicate instances that are added by upstream - * [THRIFT-3571] - Make feature test result browsable - * [THRIFT-3569] - c_glib protocols do not check number of bytes read by transport - * [THRIFT-3568] - THeader server crashes on readSlow - * [THRIFT-3567] - GLib-GObject-CRITICAL **: g_object_unref: assertion 'G_IS_OBJECT (object)' failed - * [THRIFT-3566] - C++/Qt: TQTcpServerTest::test_communicate() is never executed - * [THRIFT-3564] - C++/Qt: potential core dump in TQTcpServer in case an exception occurs in TAsyncProcessor::process() - * [THRIFT-3558] - typos in c_glib tests - * [THRIFT-3559] - Fix awkward extra semi-colons with Cocoa container literals - * [THRIFT-3555] - 'configure' script does not honor --with-openssl= for libcrypto for BN_init - * [THRIFT-3554] - Constant decls may lead to "Error: internal error: prepare_member_name_mapping() already active for different struct" - * [THRIFT-3552] - glib_c Memory Leak - * [THRIFT-3551] - Thrift perl library missing package declaration - * [THRIFT-3549] - Exceptions are not properly stringified in Perl library - * [THRIFT-3546] - NodeJS code should not be namespaced (and is currently not strict-mode compliant) - * [THRIFT-3545] - Container type literals do not compile - * [THRIFT-3538] - Remove UnboundMethodType in TProtocolDecorator - * [THRIFT-3536] - Error 'char' does not contain a definition for 'IsLowSurrogate' for WP7 target - * [THRIFT-3534] - Link error when building with Qt5 - * [THRIFT-3533] - Can not send nil pointer as service method argument - * [THRIFT-3507] - THttpClient does not use proxy from http_proxy, https_proxy environment variables - * [THRIFT-3502] - C++ TServerSocket passes small buffer to getsockname - * [THRIFT-3501] - Forward slash in comment causes compiler error - * [THRIFT-3498] - C++ library assumes optional function pthread_attr_setschedpolicy is available - * [THRIFT-3497] - Build fails with "invalid use of incomplete type" - * [THRIFT-3496] - C++: Cob style client fails when sending a consecutive request - * [THRIFT-3493] - libthrift does not compile on windows using visual studio - * [THRIFT-3488] - warning: unused variable 'program' - * [THRIFT-3489] - warning: deprecated conversion from string constant to 'char*' [-Wwrite-strings] - * [THRIFT-3487] - Full support for newer Delphi versions - * [THRIFT-3528] - Fix warnings in thrift.ll - * [THRIFT-3527] - -gen py:dynamic,utf8strings ignores utf8strings option - * [THRIFT-3526] - Code generated by py:utf8strings does not work for Python3 - * [THRIFT-3524] - dcc32 warning "W1000 Symbol 'IsLowSurrogate' is deprecated: 'Use TCharHelper'" in Thrift.Protocol.JSON.pas - * [THRIFT-3525] - py:dynamic fails to handle binary list/set/map element - * [THRIFT-3521] - TSimpleJSONProtocolTest is not deterministic (fails when run on JDK 8) - * [THRIFT-3520] - Dart TSocket onError stream should be typed as Object - * [THRIFT-3519] - fastbinary does not work with -gen py:utf8strings - * [THRIFT-3518] - TConcurrentClientSyncInfo files were missing for Visual Studio - * [THRIFT-3512] - c_glib: Build fails due to missing features.h - * [THRIFT-3483] - Incorrect empty binary handling introduced by THRIFT-3359 - * [THRIFT-3479] - Oneway calls should not return exceptions to clients - * [THRIFT-3478] - Restore dropped method to THsHaServer.java - * [THRIFT-3477] - Parser fails on enum item that starts with 'E' letter and continues with number - * [THRIFT-3476] - Missing include in ./src/thrift/protocol/TJSONProtocol.cpp - * [THRIFT-3474] - Docker: thrift-compiler - * [THRIFT-3473] - When "optional' is used with a struct member, C++ server seems to not return it correctly - * [THRIFT-3468] - Dart TSocketTransport onError handler is too restrictive - * [THRIFT-3451] - thrift_protocol PHP extension missing config.m4 file - * [THRIFT-3456] - rounding issue in static assert - * [THRIFT-3455] - struct write method's return value is incorrect - * [THRIFT-3454] - Python Tornado tutorial is broken - * [THRIFT-3463] - Java can't be disabled in CMake build - * [THRIFT-3450] - NPE when using SSL - * [THRIFT-3449] - TBaseAsyncProcessor fb.responseReady() never called for oneway functions - * [THRIFT-3471] - Dart generator does not handle uppercase argument names - * [THRIFT-3470] - Sporadic timeouts with pipes - * [THRIFT-3465] - Go Code With Complex Const Initializer Compilation Depends On Struct Order - * [THRIFT-3464] - Fix several defects in c_glib code generator - * [THRIFT-3462] - Cocoa generates Incorrect #import header names - * [THRIFT-3453] - remove rat_exclude - * [THRIFT-3418] - Use of ciphers in ssl.wrap_socket() breaks python 2.6 compatibility - * [THRIFT-3417] - "namespace xsd" is not really working - * [THRIFT-3413] - Thrift code generation bug in Go when extending service - * [THRIFT-3420] - C++: TSSLSockets are not interruptable - * [THRIFT-3415] - include unistd.h conditionally - * [THRIFT-3414] - #include in THeaderTransport.h breaks windows build - * [THRIFT-3411] - Go generates remotes with wrong package qualifiers when including - * [THRIFT-3430] - Go THttpClient does not read HTTP response body to completion when closing - * [THRIFT-3423] - First call to thrift_transport:read_exact fails to dispatch correct function - * [THRIFT-3422] - Go TServerSocket doesn't close on Interrupt - * [THRIFT-3421] - rebar as dependency instead of bundling (was: rebar fails if PWD contains Unicode) - * [THRIFT-3428] - Go test fails when running make check - * [THRIFT-3445] - Throwable messages are hidden from JVM stack trace output - * [THRIFT-3443] - Thrift include can generate uncompilable code - * [THRIFT-3444] - Large 64 bit Integer does not preserve value through Node.js JSONProtocol - * [THRIFT-3436] - misc. cross test issues with UTF-8 path names - * [THRIFT-3435] - Put generated Java code for fullcamel tests in a separate package/namespace - * [THRIFT-3433] - Doubles aren't interpreted correctly - * [THRIFT-3437] - Mingw-w64 build fail - * [THRIFT-3434] - Dart generator produces empty name in pubspec.yaml for includes without namespaces - * [THRIFT-3408] - JSON generator emits incorrect types - * [THRIFT-3406] - Cocoa client should not schedule streams on main runloop - * [THRIFT-3404] - JSON String reader doesn't recognize UTF-16 surrogate pair - * [THRIFT-3636] - Double precision is not fully preserved in C++ TJSONProtocol - * [THRIFT-3632] - c_glib testserialization fails with glib assertion - * [THRIFT-3619] - Using Thrift 0.9.3 with googletest on Linux gcc 4.9 / C++11 - * [THRIFT-3617] - CMake does not build gv/xml generators - * [THRIFT-3615] - Fix Python SSL client resource leak on connection failure - * [THRIFT-3616] - lib/py/test/test_sslsocket.py is flaky - * [THRIFT-3643] - Perl SSL server crushes if a client disconnect without handshake - * [THRIFT-3639] - C# Thrift library forces TLS 1.0, thwarting TLS 1.2 usage - * [THRIFT-3633] - Travis "C C++ - GCC" build was using clang - * [THRIFT-3634] - Fix Python TSocket resource leak on connection failure - * [THRIFT-3630] - Debian/Ubuntu install docs need an update - * [THRIFT-3629] - Parser sets exitcode on errors, but generator does not - * [THRIFT-3608] - lib/cpp/test/SecurityTest is flaky in jenkins Thrift-precommit build. - * [THRIFT-3601] - Better conformance to PEP8 for generated code - * [THRIFT-3599] - Validate client IP address against cert's SubjectAltName - * [THRIFT-3598] - TBufferedTransport doesn't instantiate client connection - * [THRIFT-3597] - `make check` hangs in go tests - * [THRIFT-3589] - Dart generator uses wrong name in constructor for uppercase arguments with defaults - * [THRIFT-3588] - Using TypeScript with --noImplicitAny fails - * [THRIFT-3584] - boolean false value cannot be transferred - * [THRIFT-3578] - Make THeaderTransport detect TCompact framed and unframed - * [THRIFT-3323] - Python library does not handle escaped forward slash ("/") in JSON - * [THRIFT-3322] - CMake generated "make check" failes on python_test - * [THRIFT-3321] - Thrift can't be added as a subdirectory of another CMake-based project - * [THRIFT-3314] - Dots in file names of includes causes dots in javascript variable names - * [THRIFT-3307] - Segfault in Ruby serializer - * [THRIFT-3309] - Missing TConstant.php in /lib/php/Makefile.am - * [THRIFT-3810] - unresolved external symbol public: virtual void __cdecl apache::thrift::server::TServerFramework::serve(void) - * [THRIFT-3736] - C++ library build fails if OpenSSL does not surrpot SSLv3 - * [THRIFT-3878] - Compile error in TSSLSocket.cpp with new OpenSSL [CRYPTO_num_locks] - * [THRIFT-3949] - missing make dist entry for compiler/cpp/test - * [THRIFT-449] - The wire format of the JSON Protocol may not always be valid JSON if it contains non-UTF8 encoded strings - * [THRIFT-162] - Thrift structures are unhashable, preventing them from being used as set elements - * [THRIFT-3961] - TConnectedClient does not terminate the connection to the client if an exception while processing the received message occures. - * [THRIFT-3881] - Travis CI builds are failing due to docker failures (three retries, and gives up) - * [THRIFT-3937] - Cannot compile 0.10.0 development tip with gcc-4.6.x - * [THRIFT-3964] - Unsupported mechanism type ????? due to dependency on default OS-dependent charset - * [THRIFT-3038] - Use of volatile in cpp library - * [THRIFT-3301] - Java generated code uses imports that can lead to class name collisions with IDL defined types - * [THRIFT-3348] - PHP TCompactProtocol bool&int64 readvalue bug - * [THRIFT-3955] - TThreadedServer Memory Leak - * [THRIFT-3829] - Thrift does not install Python Libraries if Twisted is not installed - * [THRIFT-3932] - C++ ThreadManager has a rare termination race - * [THRIFT-3828] - cmake fails when Boost_INCLUDE_DIRS (and other variables passed to include_directories()) is empty - * [THRIFT-3958] - CMake WITH_MT option for windows static runtime linking does not support the cmake build type RelWithDebInfo - * [THRIFT-3957] - TConnectedClient does not disconnect from clients when their timeout is reached. - * [THRIFT-3953] - TSSLSocket::close should handle exceptions from waitForEvent because it is called by the destructor. - * [THRIFT-3977] - PHP extension creates undefined values when deserializing sets - * [THRIFT-3947] - sockaddr type isn't always large enough for the return of getsockname - * [THRIFT-2755] - ThreadSanitizer reports data race in ThreadManager::Impl::addWorker - * [THRIFT-3948] - errno is not the correct method of getting the error in windows - * [THRIFT-4008] - broken ci due to upstream dependency versioning break - * [THRIFT-3999] - Fix Debian & Ubuntu package dependencies - * [THRIFT-3886] - PHP cross test client returns 0 even when failing - * [THRIFT-3997] - building thrift libs does not support new openssl - -## Documentation - * [THRIFT-3867] - Specify BinaryProtocol and CompactProtocol - -## Epic - * [THRIFT-3049] - As an iOS developer, I want a generator and library that produces Swift code - * [THRIFT-2336] - UTF-8 sent by PHP as JSON is not understood by TJsonProtocol - -## Improvement - * [THRIFT-1867] - Python client/server should support client-side certificates. - * [THRIFT-1313] - c_glib compact support - * [THRIFT-1385] - make install doesn't install java library in the setted folder - * [THRIFT-1437] - Update RPM spec - * [THRIFT-847] - Test Framework harmonization across all languages - * [THRIFT-819] - add Enumeration for protocol, transport and server types - * [THRIFT-3927] - Emit an error instead of throw an error in the async callback - * [THRIFT-3931] - TSimpleServer: If process request encounter UNKNOWN_METHOD, don't close transport. - * [THRIFT-3934] - Automatically resolve OpenSSL binary version on Windows CI - * [THRIFT-3918] - Run subset of make cross - * [THRIFT-3908] - Remove redundant dependencies from Dockerfile - * [THRIFT-3907] - Skip Docker image build on CI when unchanged - * [THRIFT-3868] - Java struct equals should do identity check before field comparison - * [THRIFT-3849] - Port Go serializer and deserializer to dart - * [THRIFT-2989] - Complete CMake build for Apache Thrift - * [THRIFT-2980] - ThriftMemoryBuffer doesn't have a constructor option to take an existing buffer - * [THRIFT-2856] - refactor erlang basic transports and unify interfaces - * [THRIFT-2877] - Optimize generated hashCode - * [THRIFT-2869] - JSON: run schema validation from tests - * [THRIFT-3112] - [Java] AsyncMethodCallback should be typed in generated AsyncIface - * [THRIFT-3263] - PHP jsonSerialize() should cast scalar types - * [THRIFT-2905] - Cocoa compiler should have option to produce "modern" Objective-C - * [THRIFT-2821] - Enable the use of custom HTTP-Header in the Transport - * [THRIFT-2093] - added the ability to set compression level in C++ zlib transport - * [THRIFT-2089] - Compiler ignores duplicate typenames - * [THRIFT-2056] - Moved all #include config.h statements to #include - * [THRIFT-2031] - Make SO_KEEPALIVE configurable for C++ lib - * [THRIFT-2021] - Improve large binary protocol string performance - * [THRIFT-2028] - Cleanup threading headers / libraries - * [THRIFT-2014] - Change C++ lib includes to use style throughout - * [THRIFT-2312] - travis.yml: build everything - * [THRIFT-1915] - Multiplexing Services - * [THRIFT-1736] - Visual Studio top level project files within msvc - * [THRIFT-1735] - integrate tutorial into regular build - * [THRIFT-1533] - Make TTransport should be Closeable - * [THRIFT-35] - Move language tests into their appropriate library directory - * [THRIFT-1079] - Support i64 in AS3 - * [THRIFT-1108] - SSL support for the Ruby library - * [THRIFT-3856] - update debian package deependencies - * [THRIFT-3833] - haxe http server implementation (by embeding into php web server) - * [THRIFT-3839] - Performance issue with big message deserialization using php extension - * [THRIFT-3820] - Erlang: Detect OTP >= 18 to use new time correction - * [THRIFT-3816] - Reduce docker build duration on Travis-CI - * [THRIFT-3815] - Put appveyor dependency versions to one place - * [THRIFT-3788] - Compatibility improvements and Win64 support - * [THRIFT-3792] - Timeouts for anonymous pipes should be configurable - * [THRIFT-3794] - Split Delphi application, protocol and transport exception subtypes into separate exceptions - * [THRIFT-3774] - The generated code should have exception_names meta info - * [THRIFT-3762] - Fix build warnings for deprecated Thrift "byte" fields - * [THRIFT-3756] - Improve requiredness documentation - * [THRIFT-3761] - Add debian package for Python3 - * [THRIFT-3742] - haxe php cli support - * [THRIFT-3733] - Socket timeout improvements - * [THRIFT-3728] - http transport for thrift-lua - * [THRIFT-3905] - Dart compiler does not initialize bool, int, and double properties - * [THRIFT-3911] - Loosen Ruby dev dependency version requirements - * [THRIFT-3906] - Run C# tests with make check - * [THRIFT-3900] - Add Python SSL flags - * [THRIFT-3897] - Provide meaningful exception type based on WebExceptionStatus in case of timeout - * [THRIFT-3808] - Missing `DOUBLE` in thrift type enumeration - * [THRIFT-3803] - Remove "file" attribute from XML generator - * [THRIFT-3660] - Add V4 mapped address to test client cert's altname - * [THRIFT-3661] - Use https to download meck in erlang test build - * [THRIFT-3659] - Check configure result of CMake on CI - * [THRIFT-3667] - Add TLS SNI support to clients - * [THRIFT-3651] - Make backports.match_hostname and ipaddress optional - * [THRIFT-3666] - Build D tutorial as part of Autotools build - * [THRIFT-3665] - Add D libevent and OpenSSL to docker images - * [THRIFT-3664] - Remove md5.c - * [THRIFT-3662] - Add Haskell to debian docker image - * [THRIFT-3711] - Add D to cross language test - * [THRIFT-3691] - Run flake8 Python style check on Travis-CI - * [THRIFT-3692] - (Re)enable Appveyor C++ and Python build - * [THRIFT-3677] - Improve CMake Java build - * [THRIFT-3679] - Add stdout log to testBinary in Java test server - * [THRIFT-3718] - Reduce size of docker image for build environment - * [THRIFT-3698] - [Travis-CI] Introduce retry to apt commands - * [THRIFT-3127] - switch -recurse to --recurse and reserve -r - * [THRIFT-3087] - Pass on errors like "connection closed" - * [THRIFT-3240] - Thrift Python client should support subjectAltName and wildcard certs in TSSLSocket - * [THRIFT-3213] - make cross should indicate when it skips a known failing test - * [THRIFT-3208] - Fix Visual Studio solution build failure due to missing source - * [THRIFT-3186] - Add TServerHTTP to Go library - * [THRIFT-2342] - Add __FILE__ and __LINE__ to Thrift C++ excpetions - * [THRIFT-3372] - Add dart generator to Visual Studio project - * [THRIFT-3366] - ThriftTest to implement standard return values - * [THRIFT-3402] - Provide a perl Unix Socket implementation - * [THRIFT-3361] - Improve C# library - * [THRIFT-3393] - Introduce i8 to provide consistent set of Thrift IDL integer types - * [THRIFT-3339] - Support for database/sql - * [THRIFT-3565] - C++: T[Async]Processor::getEventHandler() should be declared as const member functions - * [THRIFT-3563] - C++/Qt: removed usage of macro QT_PREPEND_NAMESPACE as it isn't consequently used for all references to Qt types. - * [THRIFT-3562] - Removed unused TAsyncProcessor::getAsyncServer() - * [THRIFT-3561] - C++/Qt: make use of Q_DISABLE_COPY() to get rid of copy ctor and assignment operator - * [THRIFT-3556] - c_glib file descriptor transport - * [THRIFT-3544] - Make cross test fail when server process died unexpectedly - * [THRIFT-3540] - Make python tutorial more in line with PEP8 - * [THRIFT-3535] - Dart generator argument to produce a file structure usable in parent library - * [THRIFT-3505] - Enhance Python TSSLSocket - * [THRIFT-3506] - Eliminate old style classes from library code - * [THRIFT-3503] - Enable py:utf8string by default - * [THRIFT-3499] - Add package_prefix to python generator - * [THRIFT-3495] - Minor enhancements and fixes for cross test - * [THRIFT-3486] - Java generated `getFieldValue` is incompatible with `setFieldValue` for binary values. - * [THRIFT-3484] - Consolidate temporary buffers in Java's TCompactProtocol - * [THRIFT-3516] - Add feature test for THeader TBinaryProtocol interop - * [THRIFT-3515] - Python 2.6 compatibility and test on CI - * [THRIFT-3514] - PHP 7 compatible version of binary protocol - * [THRIFT-3469] - Docker: Debian support - * [THRIFT-3416] - Retire old "xxx_namespace" declarations from the IDL - * [THRIFT-3426] - Align autogen comment in XSD - * [THRIFT-3424] - Add CMake android build option - * [THRIFT-3439] - Run make cross using Python3 when available - * [THRIFT-3440] - Python make check takes too much time - * [THRIFT-3441] - Stabilize Travis-CI builds - * [THRIFT-3431] - Avoid "schemes" HashMap lookups during struct reads/writes - * [THRIFT-3432] - Add a TByteBuffer transport to the Java library - * [THRIFT-3438] - Enable py:new_style by default - * [THRIFT-3405] - Go THttpClient misuses http.Client objects - * [THRIFT-3614] - Improve logging of test_sslsocket.py - * [THRIFT-3647] - Fix php extension build warnings - * [THRIFT-3642] - Speed up cross test runner - * [THRIFT-3637] - Implement compact protocol for dart - * [THRIFT-3613] - Port Python C extension to Python 3 - * [THRIFT-3612] - Add Python C extension for compact protocol - * [THRIFT-3611] - Add --regex filter to cross test runner - * [THRIFT-3631] - JSON protocol implementation for Lua - * [THRIFT-3609] - Remove or replace TestPortFixture.h - * [THRIFT-3605] - Have the compiler complain about invalid arguments and options - * [THRIFT-3596] - Better conformance to PEP8 - * [THRIFT-3585] - Compact protocol implementation for Lua - * [THRIFT-3582] - Erlang libraries should have service metadata - * [THRIFT-3579] - Introduce retry to make cross - * [THRIFT-3306] - Java: TBinaryProtocol: Use 1 temp buffer instead of allocating 8 - * [THRIFT-3910] - Do not invoke pip as part of build process - * [THRIFT-1857] - Python 3.X Support - * [THRIFT-1944] - Binding to zero port - * [THRIFT-3954] - Enable the usage of structs called "Object" in Java - * [THRIFT-3981] - Enable analyzer strong mode in Dart library - * [THRIFT-3998] - Document ability to add custom tags to thrift structs - * [THRIFT-4006] - Add a removeEventListener method on TSocket - -## New Feature - * [THRIFT-640] - Support deprecation - * [THRIFT-948] - SSL socket support for PHP - * [THRIFT-764] - add Support for Vala language - * [THRIFT-3046] - Allow PSR4 class loading for generated classes (PHP) - * [THRIFT-2113] - Erlang SSL Socket Support - * [THRIFT-1482] - Unix domain socket support under PHP - * [THRIFT-519] - Support collections of types without having to explicitly define it - * [THRIFT-468] - Rack Middleware Application for Rails - * [THRIFT-1708] - Add event handlers for processor events - * [THRIFT-3834] - Erlang namespacing and exception metadata - * [THRIFT-2510] - Implement TNonblockingServer's ability to listen on unix domain sockets - * [THRIFT-3397] - Implement TProcessorFactory in C# to enable per-client processors - * [THRIFT-3523] - XML Generator - * [THRIFT-3510] - Add HttpTaskAsyncHandler implementation - * [THRIFT-3318] - PHP: SimpleJSONProtocol Implementation - * [THRIFT-3299] - Dart language bindings in Thrift - * [THRIFT-2835] - Add possibility to distribute generators separately from thrift core, and load them dynamically - * [THRIFT-184] - Add OSGi Manifest headers to the libthrift java library to be able to use Thrift in the OSGi runtime - * [THRIFT-141] - If a required field is not present on serialization, throw an exception - * [THRIFT-1891] - Add Windows ALPC transport which is right counterpart of Unix domain sockets - -## Question - * [THRIFT-1808] - The Thrift struct should be considered self-contained? - * [THRIFT-2895] - Tutorial cpp - * [THRIFT-3860] - Elephant-bird application Test fails for Thrift - * [THRIFT-3811] - HTTPS Support for C++ applications - * [THRIFT-3509] - "make check" error - -## Story - * [THRIFT-3452] - .travis.yml: Migrating from legacy to container-based infrastructure - -## Sub-task - * [THRIFT-1811] - ruby tutorial as part of the regular build - * [THRIFT-2779] - PHP TJSONProtocol encode unicode into UCS-4LE which can't be parsed by other language bindings - * [THRIFT-2110] - Erlang: Support for Multiplexing Services on any Transport, Protocol and Server - * [THRIFT-3852] - A Travis-CI job fails with "write error" - * [THRIFT-3740] - Fix haxelib.json classpath - * [THRIFT-3653] - incorrect union serialization - * [THRIFT-3652] - incorrect serialization of optionals - * [THRIFT-3655] - incorrect union serialization - * [THRIFT-3654] - incorrect serialization of optionals - * [THRIFT-3656] - incorrect serialization of optionals - * [THRIFT-3699] - Fix integer limit symbol includes in Python C extension - * [THRIFT-3693] - Fix include issue in C++ TSSLSocketInterruptTest on Windows - * [THRIFT-3694] - [Windows] Disable tests of a few servers that are not supported - * [THRIFT-3696] - Install pip to CentOS Docker images to fix Python builds - * [THRIFT-3638] - Fix haxelib.json - * [THRIFT-3251] - Add http transport for server to Go lib - * [THRIFT-2424] - Recursive Types - * [THRIFT-2423] - THeader - * [THRIFT-2413] - Python: UTF-8 sent by PHP as JSON is not understood by TJsonProtocol - * [THRIFT-2409] - Java: UTF-8 sent by PHP as JSON is not understood by TJsonProtocol - * [THRIFT-2412] - D: UTF-8 sent by PHP as JSON is not understood by TJsonProtocol - * [THRIFT-2411] - C++: UTF-8 sent by PHP as JSON is not understood by TJsonProtocol - * [THRIFT-2410] - JavaMe: UTF-8 sent by PHP as JSON is not understood by TJsonProtocol - * [THRIFT-2668] - TestSuite: detailed result on passed tests by feature - * [THRIFT-2659] - python Test Server fails when throwing TException - * [THRIFT-3398] - Add CMake build for Haskell library and tests - * [THRIFT-3396] - DART: UTF-8 sent by PHP as JSON is not understood by TJsonProtocol - * [THRIFT-3364] - Fix ruby binary field encoding in TJSONProtocol - * [THRIFT-3381] - Fix for misc. codegen issues with THRIFT-2905 - * [THRIFT-3573] - No rule to make target `../../../test/c_glib/src/.deps/testthrifttest-thrift_test_handler.Po'. - * [THRIFT-3572] - "Unable to determine the behavior of a signed right shift" - * [THRIFT-3542] - Add length limit support to Java test server - * [THRIFT-3537] - Remove the (now obsolete) csharp:asyncctp flag - * [THRIFT-3532] - Add configurable string and container read size limit to Python protocols - * [THRIFT-3531] - Create cross lang feature test for string and container read length limit - * [THRIFT-3482] - Haskell JSON protocol does not encode binary field as Base64 - * [THRIFT-3425] - Minor fixes + simplification for CentOS Dockerfile - * [THRIFT-3442] - Run CMake tests on Appveyor - * [THRIFT-3409] - NodeJS binary field issues - * [THRIFT-3621] - Fix lib/cpp/test/SecurityTest.cpp to use ephemeral ports - * [THRIFT-3628] - Fix lib/cpp/test/TServerIntegrationTest.cpp to use ephemeral ports - * [THRIFT-3625] - Kill unused #include "TestPortFixture.h" in lib/cpp/test/TServerTransportTest.cpp. - * [THRIFT-3646] - Fix Python extension build warnings - * [THRIFT-3626] - Fix lib/cpp/test/TSocketInterruptTest.cpp to use ephemeral ports. - * [THRIFT-3624] - Fix lib/cpp/test/TServerSocketTest.cpp to use ephemeral ports - * [THRIFT-3623] - Fix Fix cpp/lib/test/TSSLSocketInterruptTest.cpp to use ephemeral ports - * [THRIFT-3592] - Add basic test client - * [THRIFT-3980] - add TExtendedBinaryProtocol.java - -## Task - * [THRIFT-1801] - Sync up TApplicationException codes across languages and thrift implementations - * [THRIFT-1259] - Automate versioning - -## Test - * [THRIFT-3400] - Add Erlang to cross test - * [THRIFT-3504] - Fix FastbinaryTest.py - -## Wish - * [THRIFT-3923] - Maybe remove Aereo from the "Powered by" list - * [THRIFT-2149] - Add an option to disable the generation of default operators - - - -Thrift 0.9.3 --------------------------------------------------------------------------------- -## Bug - * [THRIFT-2441] - Cannot shutdown TThreadedServer when clients are still connected - * [THRIFT-2465] - TBinaryProtocolT breaks if copied/moved - * [THRIFT-2474] - thrift.h causes a compile failure - * [THRIFT-2540] - Running configure from outside the source directory fails - * [THRIFT-2598] - Add check for minimum Go version to configure.ac - * [THRIFT-2647] - compiler-hs: don't decapitalize field names, do decapitalize argument bindings - * [THRIFT-2773] - Generated Java code for 'oneway' methods is incorrect. - * [THRIFT-2789] - TNonblockingServer leaks socket FD's under load - * [THRIFT-2682] - TThreadedServer leaks per-thread memory - * [THRIFT-2674] - JavaScript: declare Accept: and Content-Type: in request - * [THRIFT-3078] - TNonblockingServerSocket's logger is not named after TNonblockingServerSocket - * [THRIFT-3077] - C++ TFileTransport ignores return code from ftruncate - * [THRIFT-3067] - C++ cppcheck performance related warnings - * [THRIFT-3066] - C++ TDenseProtocol assert modifies instead of checks - * [THRIFT-3071] - bootstrap.sh on Ubuntu 12.04 (Precise) automake error - * [THRIFT-3069] - C++ TServerSocket leaks socket on fcntl get or set flags error - * [THRIFT-3079] - TNonblockingServerSocket's logger is not named after TNonblockingServerSocket - * [THRIFT-3080] - C++ TNonblockingServer connection leak while accept huge number connections. - * [THRIFT-3086] - C++ Valgrind Error Cleanup - * [THRIFT-3085] - thrift_reconnecting_client never try to reconnect - * [THRIFT-3123] - Missing include in compiler/cpp/src/main.h breaks build in some environments - * [THRIFT-3125] - Fix the list of exported headers in automake input - * [THRIFT-3126] - PHP JSON serializer converts empty or int-indexed maps to lists - * [THRIFT-3132] - Properly format date in Java @Generated annotations - * [THRIFT-3137] - Travis build hangs after failure - * [THRIFT-3138] - "make check" parallel execution is underministic - * [THRIFT-3139] - JS library test is flaky - * [THRIFT-3140] - ConcurrentModificationException is thrown by JavaScript test server - * [THRIFT-3124] - Some signed/unsigned warnings while building compiler - * [THRIFT-3128] - Go generated code produces name collisions between services - * [THRIFT-3146] - Graphviz generates function name collisions between services - * [THRIFT-3147] - Segfault while receiving data - * [THRIFT-3148] - Markdown links to coding_standards are dead - * [THRIFT-3090] - cmake build is broken on MacOSX - * [THRIFT-3097] - cmake targets unconditionally depend on optional libraries - * [THRIFT-3094] - master as of 2015-APR-13 fails -DBOOST_THREADS cmake build - * [THRIFT-3099] - cmake build is broken on FreeBSD - * [THRIFT-3089] - Assigning default ENUM values results in non-compilable java code if java namespace is not defined - * [THRIFT-3093] - mingw compile fixes for c++ library 0.9.2 - * [THRIFT-3098] - Thrift does not pretty print binary typedefs the way it does binary fields - * [THRIFT-3091] - c_glib service method should return result from handler method - * [THRIFT-3088] - TThreadPoolServer with Sasl auth may leak CLOSE_WAIT socket - * [THRIFT-3109] - Cross test log file cannot be browsed when served in HTTP server - * [THRIFT-3113] - m4 C++11 macro issue - * [THRIFT-3105] - C++ libthriftnb library on Windows build failure - * [THRIFT-3115] - Uncompileable code due to name collision with predefined used types - * [THRIFT-3117] - Java TSSLTransportFactory can't load certificates within JAR archive - * [THRIFT-3102] - could not make check for Go Library - * [THRIFT-3120] - Minor spelling errors and an outdated URL - * [THRIFT-3121] - Librt does not exist on OS X - * [THRIFT-3152] - Compiler error on Mac OSX (missing #include ) - * [THRIFT-3162] - make fails for dmd 2.067 - * [THRIFT-3164] - Thrift C++ library SSL socket by default allows for unsecure SSLv3 negotiation - * [THRIFT-3168] - Fix Maven POM - * [THRIFT-3170] - Initialism code in the Go compiler causes chaos - * [THRIFT-3169] - Do not export thrift.TestStruct and thrift.TestEnum in thrift Go library - * [THRIFT-3191] - Perl compiler does not add support for unexpected exception handling - * [THRIFT-3178] - glib C does not compile - * [THRIFT-3189] - Perl ServerSocket should allow a specific interface to be listened to - * [THRIFT-3252] - Missing TConcurrentClientSyncInfo.h in cpp Makefile, so doesn't install - * [THRIFT-3255] - Thrift generator doesn't exclude 'package' keyword for thrift property names breaking java builds - * [THRIFT-3260] - multiple warnings in c_glib tutorial - * [THRIFT-3256] - Some D test timings are too aggressive for slow machines - * [THRIFT-3257] - warning: extra tokens at end of #endif directive - * [THRIFT-3184] - Thrift Go leaves file descriptors open - * [THRIFT-3203] - DOAP - please fix "Ocaml" => "OCaml" - * [THRIFT-3210] - (uncompileable) code generated for server events while are events not enabled - * [THRIFT-3215] - TJSONProtocol '(c++) uses "throw new" to throw exceptions instead of "throw" - * [THRIFT-3202] - Allow HSHAServer to configure min and max worker threads separately. - * [THRIFT-3205] - TCompactProtocol return a wrong error when the io.EOF happens - * [THRIFT-3209] - LGPL mentioned in license file - * [THRIFT-3197] - keepAliveTime is hard coded as 60 sec in TThreadPoolServer - * [THRIFT-3196] - Misspelling in lua TBinaryProtocol (stirctWrite => strictWrite) - * [THRIFT-3198] - Allow construction of TTransportFactory with a specified maxLength - * [THRIFT-3192] - Go import paths changed in 1.4, and expired June 1 - * [THRIFT-3271] - Could not find or load main class configtest_ax_javac_and_java on some non-english systems - * [THRIFT-3273] - c_glib: Generated code tries to convert between function and void pointers - * [THRIFT-3264] - Fix Erlang 16 namespaced types - * [THRIFT-3270] - reusing TNonblockingServer::TConnection cause dirty TSocket - * [THRIFT-3267] - c_glib: "Critical" failure during unit tests - * [THRIFT-3277] - THttpClient leaks connections if it's used for multiple requests - * [THRIFT-3278] - NodeJS: Fix exception stack traces and names - * [THRIFT-3279] - Fix a bug in retry_max_delay (NodeJS) - * [THRIFT-3280] - Initialize retry variables on construction - * [THRIFT-3283] - c_glib: Tutorial server always exits with warning - * [THRIFT-3284] - c_glib: Empty service produces unused-variable warning - * [THRIFT-1925] - c_glib generated code does not compile - * [THRIFT-1849] - after transport->open() opens isOpen returns true and next open() goes thru when it shall not - * [THRIFT-1866] - java compiler generates non-compiling code with const's defined in a thrift when name includes non-identifier chars - * [THRIFT-1938] - FunctionRunner.h -- uses wrong path for Thread.h when installed - * [THRIFT-1844] - Password string not cleared - * [THRIFT-2004] - Thrift::Union violates :== method contract and crashes - * [THRIFT-2073] - Thrift C++ THttpClient error: cannot refill buffer - * [THRIFT-2127] - Autoconf scripting does not properly account for cross-compile - * [THRIFT-2180] - Integer types issues in Cocoa lib on ARM64 - * [THRIFT-2189] - Go needs "isset" to fully support "union" type (and optionals) - * [THRIFT-2192] - autotools on Redhat based systems - * [THRIFT-2546] - cross language tests fails at 'TestMultiException' when using nodejs server - * [THRIFT-2547] - nodejs servers and clients fails to connect with cpp using compact protocol - * [THRIFT-2548] - Nodejs servers and clients does not work properly with -ssl - * [THRIFT-1471] - toString() does not print ByteBuffer values when nested in a List - * [THRIFT-1201] - getaddrinfo resource leak - * [THRIFT-615] - TThreadPoolServer doesn't call task_done after pulling tasks from it's clients queue - * [THRIFT-162] - Thrift structures are unhashable, preventing them from being used as set elements - * [THRIFT-810] - Crashed client on TSocket::close under loads - * [THRIFT-557] - charset problem with file Autogenerated by Thrift - * [THRIFT-233] - IDL doesn't support negative hex literals - * [THRIFT-1649] - contrib/zeromq does not build in 0.8.0 - * [THRIFT-1642] - Miscalculation lead to throw unexpected "TTransportException::TIMED_OUT"(or called "EAGAIN (timed out)") exception - * [THRIFT-1587] - TSocket::setRecvTimeout error - * [THRIFT-1248] - pointer subtraction in TMemoryBuffer relies on undefined behavior - * [THRIFT-1774] - Sasl Transport client would hang when trying to connect non-sasl transport server - * [THRIFT-1754] - RangeError in buffer handling - * [THRIFT-1618] - static structMap in FieldMetaData is not thread safe and can lead to deadlocks - * [THRIFT-2335] - thrift incompatibility with py:tornado as server, java as client - * [THRIFT-2803] - TCP_DEFER_ACCEPT not supported with domain sockets - * [THRIFT-2799] - Build Problem(s): ld: library not found for -l:libboost_unit_test_framework.a - * [THRIFT-2801] - C++ test suite compilation warnings - * [THRIFT-2802] - C++ tutorial compilation warnings - * [THRIFT-2795] - thrift_binary_protocol.c: 'dereferencing type-punned pointer will break strict-aliasing rules' - * [THRIFT-2817] - TSimpleJSONProtocol reads beyond end of message - * [THRIFT-2826] - html:standalone sometimes ignored - * [THRIFT-2829] - Support haxelib installation via github - * [THRIFT-2828] - slightly wrong help screen indent - * [THRIFT-2831] - Removes dead code in web_server.js introduced in THRIFT-2819 - * [THRIFT-2823] - All JS-tests are failing when run with grunt test - * [THRIFT-2827] - Thrift 0.9.2 fails to compile on Yosemite due to tr1/functional include in ProcessorTest.cpp - * [THRIFT-2843] - Automake configure.ac has possible typo related to Java - * [THRIFT-2813] - multiple haxe library fixes/improvements - * [THRIFT-2825] - Supplying unicode to python Thrift client can cause next request arguments to get overwritten - * [THRIFT-2840] - Cabal file points to LICENSE file outside the path of the Haskell project. - * [THRIFT-2818] - Trailing commas in array - * [THRIFT-2830] - Clean up ant warnings in tutorial dir - * [THRIFT-2842] - Erlang thrift client has infinite timeout - * [THRIFT-2810] - Do not leave the underlying ServerSocket open if construction of TServerSocket fails - * [THRIFT-2812] - Go server adding redundant buffering layer - * [THRIFT-2839] - TFramedTransport read bug - * [THRIFT-2844] - Nodejs support broken when running under Browserify - * [THRIFT-2814] - args/result classes not found when no namespace is set - * [THRIFT-2847] - function IfValue() is a duplicate of System.StrUtils.IfThen - * [THRIFT-2848] - certain Delphi tests do not build if TypeRegistry is used - * [THRIFT-2854] - Go Struct writer and reader looses important error information - * [THRIFT-2858] - Enable header field case insensitive match in THttpServer - * [THRIFT-2857] - C# generator creates uncompilable code for struct constants - * [THRIFT-2860] - Delphi server closes connection on unexpected exceptions - * [THRIFT-2868] - Enhance error handling in the Go client - * [THRIFT-2879] - TMemoryBuffer: using lua string in wrong way - * [THRIFT-2851] - Remove strange public Peek() from Go transports - * [THRIFT-2852] - Better Open/IsOpen/Close behavior for StreamTransport. - * [THRIFT-2871] - Missing semicolon in thrift.js - * [THRIFT-2872] - ThreadManager deadlock for task expiration - * [THRIFT-2881] - Handle errors from Accept() correctly - * [THRIFT-2849] - Spell errors reported by codespell tool - * [THRIFT-2870] - C++ TJSONProtocol using locale dependent formatting - * [THRIFT-2882] - Lua Generator: using string.len funtion to get struct(map,list,set) size - * [THRIFT-2864] - JSON generator missing from Visual Studio build project - * [THRIFT-2878] - Go validation support of required fields - * [THRIFT-2873] - TPipe and TPipeServer don't compile on Windows with UNICODE enabled - * [THRIFT-2888] - import of is missing in JSON generator - * [THRIFT-2900] - Python THttpClient does not reset socket timeout on exception - * [THRIFT-2907] - 'ntohll' macro redefined - * [THRIFT-2884] - Map does not serialize correctly for JSON protocol in Go library - * [THRIFT-2887] - --with-openssl configure flag is ignored - * [THRIFT-2894] - PHP json serializer skips maps with int/bool keys - * [THRIFT-2904] - json_protocol_test.go fails - * [THRIFT-2906] - library not found for -l:libboost_unit_test_framework.a - * [THRIFT-2890] - binary data may lose bytes with JSON transport under specific circumstances - * [THRIFT-2891] - binary data may cause a failure with JSON transport under specific circumstances - * [THRIFT-2901] - Fix for generated TypeScript functions + indentation of JavaScript maps - * [THRIFT-2916] - make check fails for D language - * [THRIFT-2918] - Race condition in Python TProcessPoolServer test - * [THRIFT-2920] - Erlang Thrift test uses wrong IDL file - * [THRIFT-2922] - $TRIAL is used with Python tests but not tested accordingly - * [THRIFT-2912] - Autotool build for C++ Qt library is invalid - * [THRIFT-2914] - explicit dependency to Lua5.2 fails on some systems - * [THRIFT-2910] - libevent is not really optional - * [THRIFT-2911] - fix c++ version zeromq transport, the old version cannot work - * [THRIFT-2915] - Lua generator missing from Visual Studio build project - * [THRIFT-2917] - "make clean" breaks test/c_glib - * [THRIFT-2919] - Haxe test server timeout too large - * [THRIFT-2923] - JavaScript client assumes a message being written - * [THRIFT-2924] - TNonblockingServer crashes when user-provided event_base is used - * [THRIFT-2925] - CMake build does not work with OpenSSL nor anything installed in non-system location - * [THRIFT-2931] - Access to undeclared static property: Thrift\Protocol\TProtocol::$TBINARYPROTOCOLACCELERATED - * [THRIFT-2893] - CMake build fails with boost thread or std thread - * [THRIFT-2902] - Generated c_glib code does not compile with clang - * [THRIFT-2903] - Qt4 library built with CMake does not work - * [THRIFT-2942] - CSharp generate invalid code for property named read or write - * [THRIFT-2932] - Node.js Thrift connection libraries throw Exceptions into event emitter - * [THRIFT-2933] - v0.9.2: doubles encoded in node with compact protocol cannot be decoded by python - * [THRIFT-2934] - createServer signature mismatch - * [THRIFT-2981] - IDL with no namespace produces unparsable PHP - * [THRIFT-2999] - Addition of .gitattributes text auto in THRIFT-2724 causes modified files on checkout - * [THRIFT-2949] - typo in compiler/cpp/README.md - * [THRIFT-2957] - warning: source file %s is in a subdirectory, but option 'subdir-objects' is disabled - * [THRIFT-2953] - TNamedPipeServerTransport is not Stop()able - * [THRIFT-2962] - Docker Thrift env for development and testing - * [THRIFT-2971] - C++ test and tutorial parallel build is unstable - * [THRIFT-2972] - Missing backslash in lib/cpp/test/Makefile.am - * [THRIFT-2951] - Fix Erlang name conflict test - * [THRIFT-2955] - Using list of typedefs does not compile on Go - * [THRIFT-2960] - namespace regression for Ruby - * [THRIFT-2959] - nodejs: fix binary unit tests - * [THRIFT-2966] - nodejs: Fix bad references to TProtocolException and TProtocolExceptionType - * [THRIFT-2970] - grunt-jsdoc fails due to dependency issues - * [THRIFT-3001] - C# Equals fails for binary fields (byte[]) - * [THRIFT-3003] - Missing LICENSE file prevents package from being installed - * [THRIFT-3008] - Node.js server does not fully support exception - * [THRIFT-3007] - Travis build is broken because of directory conflict - * [THRIFT-3009] - TSSLSocket does not use the correct hostname (breaks certificate checks) - * [THRIFT-3011] - C# test server testException() not implemented according to specs - * [THRIFT-3012] - Timing problems in NamedPipe implementation due to unnecessary open/close - * [THRIFT-3019] - Golang generator missing docstring for structs - * [THRIFT-3021] - Service remote tool does not import stub package with package prefix - * [THRIFT-3026] - TMultiplexedProcessor does not have a constructor - * [THRIFT-3028] - Regression caused by THRIFT-2180 - * [THRIFT-3017] - order of map key/value types incorrect for one CTOR - * [THRIFT-3020] - Cannot compile thrift as C++03 - * [THRIFT-3024] - User-Agent "BattleNet" used in some Thrift library files - * [THRIFT-3047] - Uneven calls to indent_up and indent_down in Cocoa generator - * [THRIFT-3048] - NodeJS decoding of I64 is inconsistent across protocols - * [THRIFT-3043] - go compiler generator uses non C++98 code - * [THRIFT-3044] - Docker README.md paths to Dockerfiles are incorrect - * [THRIFT-3040] - bower.json wrong "main" path - * [THRIFT-3051] - Go Thrift generator creates bad go code - * [THRIFT-3057] - Java compiler build is broken - * [THRIFT-3061] - C++ TSSLSocket shutdown delay/vulnerability - * [THRIFT-3062] - C++ TServerSocket invalid port number (over 999999) causes stack corruption - * [THRIFT-3065] - Update libthrift dependencies (slf4j, httpcore, httpclient) - * [THRIFT-3244] - TypeScript: fix namespace of included types - * [THRIFT-3246] - Reduce the number of trivial warnings in Windows C++ CMake builds - * [THRIFT-3224] - Fix TNamedPipeServer unpredictable behavior on accept - * [THRIFT-3230] - Python compiler generates wrong code if there is function throwing a typedef of exception with another namespace - * [THRIFT-3236] - MaxSkipDepth never checked - * [THRIFT-3239] - Limit recursion depth - * [THRIFT-3241] - fatal error: runtime: cannot map pages in arena address space - * [THRIFT-3242] - OSGi Import-Package directive is missing the Apache HTTP packages - * [THRIFT-3234] - Limit recursion depth - * [THRIFT-3222] - TypeScript: Generated Enums are quoted - * [THRIFT-3229] - unexpected Timeout exception when desired bytes are only partially available - * [THRIFT-3231] - CPP: Limit recursion depth to 64 - * [THRIFT-3235] - Limit recursion depth - * [THRIFT-3175] - fastbinary.c python deserialize can cause huge allocations from garbage - * [THRIFT-3176] - Union incorrectly implements == - * [THRIFT-3177] - Fails to run rake test - * [THRIFT-3180] - lua plugin: framed transport do not work - * [THRIFT-3179] - lua plugin cant connect to remote server because function l_socket_create_and_connect always bind socket to localhost - * [THRIFT-3248] - TypeScript: additional comma in method signature without parameters - * [THRIFT-3302] - Go JSON protocol should encode Thrift byte type as signed integer string - * [THRIFT-3297] - c_glib: an abstract base class is not generated - * [THRIFT-3294] - TZlibTransport for Java does not write data correctly - * [THRIFT-3296] - Go cross test does not conform to spec - * [THRIFT-3295] - C# library does not build on Mono 4.0.2.5 or later - * [THRIFT-3293] - JavaScript: null values turn into empty structs in constructor - * [THRIFT-3310] - lib/erl/README.md has incorrect formatting - * [THRIFT-3319] - CSharp tutorial will not build using the *.sln - * [THRIFT-3335] - Ruby server does not handle processor exception - * [THRIFT-3338] - Stray underscore in generated go when service name starts with "New" - * [THRIFT-3324] - Update Go Docs for pulling all packages - * [THRIFT-3345] - Clients blocked indefinitely when a java.lang.Error is thrown - * [THRIFT-3332] - make dist fails on clean build - * [THRIFT-3326] - Tests do not compile under *BSD - * [THRIFT-3334] - Markdown notation of protocol spec is malformed - * [THRIFT-3331] - warning: ‘etype’ may be used uninitialized in this function - * [THRIFT-3349] - Python server does not handle processor exception - * [THRIFT-3343] - Fix haskell README - * [THRIFT-3340] - Python: enable json tests again - * [THRIFT-3311] - Top level README.md has incorrect formmating - * [THRIFT-2936] - Minor memory leak in SSL - * [THRIFT-3290] - Using from in variable names causes the generated Python code to have errors - * [THRIFT-3225] - Fix TPipeServer unpredictable behavior on interrupt() - * [THRIFT-3354] - Fix word-extraction substr bug in initialism code - * [THRIFT-2006] - TBinaryProtocol message header call name length is not validated and can be used to core the server - * [THRIFT-3329] - C++ library unit tests don't compile against the new boost-1.59 unit test framework - * [THRIFT-2630] - windows7 64bit pc. ipv4 and ipv6 pc.can't use - * [THRIFT-3336] - Thrift generated streaming operators added in 0.9.2 cannot be overridden - * [THRIFT-2681] - Core of unwind_cleanup - * [THRIFT-3317] - cpp namespace org.apache issue appears in 0.9 - -## Documentation - * [THRIFT-3286] - Apache Ant is a necessary dependency - -## Improvement - * [THRIFT-227] - Byte[] in collections aren't pretty printed like regular binary fields - * [THRIFT-2744] - Vagrantfile for Centos 6.5 - * [THRIFT-2644] - Haxe support - * [THRIFT-2756] - register Media Type @ IANA - * [THRIFT-3076] - Compatibility with Haxe 3.2.0 - * [THRIFT-3081] - C++ Consolidate client processing loops in TServers - * [THRIFT-3083] - C++ Consolidate server processing loops in TSimpleServer, TThreadedServer, TThreadPoolServer - * [THRIFT-3084] - C++ add concurrent client limit to threaded servers - * [THRIFT-3074] - Add compiler/cpp/lex.yythriftl.cc to gitignore. - * [THRIFT-3134] - Remove use of deprecated "phantom.args" - * [THRIFT-3133] - Allow "make cross" and "make precross" to run without building all languages - * [THRIFT-3142] - Make JavaScript use downloaded libraries - * [THRIFT-3141] - Improve logging of JavaScript test - * [THRIFT-3144] - Proposal: make String representation of enums in generated go code less verbose - * [THRIFT-3130] - Remove the last vestiges of THRIFT_OVERLOAD_IF from THRIFT-1316 - * [THRIFT-3131] - Consolidate suggested import path for go thrift library to git.apache.org in docs and code - * [THRIFT-3092] - Generated Haskell types should derive Generic - * [THRIFT-3110] - Print error log after cross test failures on Travis - * [THRIFT-3114] - Using local temp variables to not pollute the global table - * [THRIFT-3106] - CMake summary should give more information why a library is set to off - * [THRIFT-3119] - Java's TThreadedSelectorServer has indistinguishable log messages in run() - * [THRIFT-3122] - Javascript struct constructor should properly initialize struct and container members from plain js arguments - * [THRIFT-3151] - Fix links to git-wip* - should be git.apache.org - * [THRIFT-3167] - Windows build from source instructions need to be revised - * [THRIFT-3155] - move contrib/mingw32-toolchain.cmake to build/cmake/ - * [THRIFT-3160] - Make generated go enums implement TextMarshaller and TextUnmarshaller interfaces - * [THRIFT-3150] - Add an option to thrift go generator to make Read and Write methods private - * [THRIFT-3149] - Make ReadFieldN methods in generated Go code private - * [THRIFT-3172] - Add tutorial to Thrift web site - * [THRIFT-3214] - Add Erlang option for using maps instead of dicts - * [THRIFT-3201] - Capture github test artifacts for failed builds - * [THRIFT-3266] - c_glib: Multiple compiler warnings building unit tests - * [THRIFT-3285] - c_glib: Build library with all warnings enabled, no warnings generated - * [THRIFT-1954] - Allow for a separate connection timeout value - * [THRIFT-2098] - Add support for Qt5+ - * [THRIFT-2199] - Remove Dense protocol (was: move to Contrib) - * [THRIFT-406] - C++ Test suite cleanup - * [THRIFT-902] - socket and connect timeout in TSocket should be distinguished - * [THRIFT-388] - Use a separate wire format for async calls - * [THRIFT-727] - support native C++ language specific exception message - * [THRIFT-1784] - pep-3110 compliance for exception handling - * [THRIFT-1025] - C++ ServerSocket should inherit from Socket with the necessary Ctor to listen on connections from a specific host - * [THRIFT-2269] - Can deploy libthrift-source.jar to maven center repository - * [THRIFT-2804] - Pull an interface out of TBaseAsyncProcessor - * [THRIFT-2806] - more whitespace fixups - * [THRIFT-2811] - Make remote socket address accessible - * [THRIFT-2809] - .gitignore update for compiler's visual project - * [THRIFT-2846] - Expose ciphers parameter from ssl.wrap_socket() - * [THRIFT-2859] - JSON generator: output complete descriptors - * [THRIFT-2861] - add buffered transport - * [THRIFT-2865] - Test case for Go: SeqId out of sequence - * [THRIFT-2866] - Go generator source code is hard to read and maintain - * [THRIFT-2880] - Read the network address from the listener if available. - * [THRIFT-2875] - Typo in TDenseProtocol.h comment - * [THRIFT-2874] - TBinaryProtocol member variable "string_buf_" is never used. - * [THRIFT-2855] - Move contributing.md to the root of the repository - * [THRIFT-2862] - Enable RTTI and/or build macros for generated code - * [THRIFT-2876] - Add test for THRIFT-2526 Assignment operators and copy constructors in c++ don't copy the __isset struct - * [THRIFT-2897] - Generate -isEqual: and -hash methods - * [THRIFT-2909] - Improve travis build - * [THRIFT-2921] - Make Erlang impl ready for OTP 18 release (dict/0 and set/0 are deprecated) - * [THRIFT-2928] - Rename the erlang test_server module - * [THRIFT-2940] - Allow installing Thrift from git as NPM module by providing package.json in top level directory - * [THRIFT-2937] - Allow setting a maximum frame size in TFramedTransport - * [THRIFT-2976] - nodejs: xhr and websocket support for browserify - * [THRIFT-2996] - Test for Haxe 3.1.3 or better - * [THRIFT-2969] - nodejs: DRY up library tests - * [THRIFT-2973] - Update Haxe lib readme regarding Haxe 3.1.3 - * [THRIFT-2952] - Improve handling of Server.Stop() - * [THRIFT-2964] - nodejs: move protocols and transports into separate files - * [THRIFT-2963] - nodejs - add test coverage - * [THRIFT-3006] - Attach 'omitempty' json tag for optional fields in Go - * [THRIFT-3027] - Go compiler does not ensure common initialisms have consistent case - * [THRIFT-3030] - TThreadedServer: Property for number of clientThreads - * [THRIFT-3023] - Go compiler is a little overly conservative with names of attributes - * [THRIFT-3018] - Compact protocol for Delphi - * [THRIFT-3025] - Change pure Int constants into @enums (where possible) - * [THRIFT-3031] - migrate "shouldStop" flag to TServer - * [THRIFT-3022] - Compact protocol for Haxe - * [THRIFT-3041] - Generate asynchronous clients for Cocoa - * [THRIFT-3053] - Perl SSL Socket Support (Encryption) - * [THRIFT-3247] - Generate a C++ thread-safe client - * [THRIFT-3217] - Provide a little endian variant of the binary protocol in C++ - * [THRIFT-3223] - TypeScript: Add initial support for Enum Maps - * [THRIFT-3220] - Option to suppress @Generated Annotation entirely - * [THRIFT-3300] - Reimplement TZlibTransport in Java using streams - * [THRIFT-3288] - c_glib: Build unit tests with all warnings enabled, no warnings generated - * [THRIFT-3347] - Improve cross test servers and clients - * [THRIFT-3342] - Improve ruby cross test client and server compatibility - * [THRIFT-2296] - Add C++ Base class for service - * [THRIFT-3337] - Add testBool method to cross tests - * [THRIFT-3303] - Disable concurrent cabal jobs on Travis to avoid GHC crash - * [THRIFT-2623] - Docker container for Thrift - * [THRIFT-3298] - thrift endian converters may conflict with other libraries - * [THRIFT-1559] - Provide memory pool for TBinaryProtocol to eliminate memory fragmentation - * [THRIFT-424] - Steal ProtocolBuffers' VarInt implementation for C++ - -## New Feature - * [THRIFT-3070] - Add ability to set the LocalCertificateSelectionCallback - * [THRIFT-1909] - Java: Add compiler flag to use the "option pattern" for optional fields - * [THRIFT-2099] - Stop TThreadPoolServer with alive connections. - * [THRIFT-123] - implement TZlibTransport in Java - * [THRIFT-2368] - New option: reuse-objects for Java generator - * [THRIFT-2836] - Optionally generate C++11 MoveConstructible types - * [THRIFT-2824] - Flag to disable html escaping doctext - * [THRIFT-2819] - Add WebsSocket client to node.js - * [THRIFT-3050] - Client certificate authentication for non-http TLS in C# - * [THRIFT-3292] - Implement TZlibTransport in Go - -## Question - * [THRIFT-2583] - Thrift on xPC target (SpeedGoat) - * [THRIFT-2592] - thrift server using c_glib - * [THRIFT-2832] - c_glib: Handle string lists correctly - * [THRIFT-3136] - thrift installation problem on mac - * [THRIFT-3346] - c_glib: Tutorials example crashes saying Calculator.ping implementation returned FALSE but did not set an error - -## Sub-task - * [THRIFT-2578] - Moving 'make cross' from test.sh to test.py - * [THRIFT-2734] - Go coding standards - * [THRIFT-2748] - Add Vagrantfile for Centos 6.5 - * [THRIFT-2753] - Misc. Haxe improvements - * [THRIFT-2640] - Compact Protocol in Cocoa - * [THRIFT-3262] - warning: overflow in implicit constant conversion in DenseProtoTest.cpp - * [THRIFT-3194] - Can't build with go enabled. gomock SCC path incorrect. - * [THRIFT-3275] - c_glib tutorial warnings in generated code - * [THRIFT-1125] - Multiplexing support for the Ruby Library - * [THRIFT-2807] - PHP Code Style - * [THRIFT-2841] - Add comprehensive integration tests for the whole Go stack - * [THRIFT-2815] - Haxe: Support for Multiplexing Services on any Transport, Protocol and Server - * [THRIFT-2886] - Integrate binary type in standard Thrift cross test - * [THRIFT-2946] - Enhance usability of cross test framework - * [THRIFT-2967] - Add .editorconfig to root - * [THRIFT-3033] - Perl: Support for Multiplexing Services on any Transport, Protocol and Server - * [THRIFT-3174] - Initialism code in the Go compiler doesn't check first word - * [THRIFT-3193] - Option to supress date value in @Generated annotation - * [THRIFT-3305] - Missing dist files for 0.9.3 release candidate - * [THRIFT-3341] - Add testBool methods - * [THRIFT-3308] - Fix broken test cases for 0.9.3 release candidate - -## Task - * [THRIFT-2834] - Remove semi-colons from python code generator - * [THRIFT-2853] - Adjust comments not applying anymore after THRIFT-2852 - -## Test - * [THRIFT-3211] - Add make cross support for php TCompactProtocol - -## Wish - * [THRIFT-2838] - TNonblockingServer can bind to port 0 (i.e., get an OS-assigned port) but there is no way to get the port number - - - -Thrift 0.9.2 --------------------------------------------------------------------------------- -## Bug - * [THRIFT-2793] - Go compiler produces uncompilable code - * [THRIFT-1481] - Unix domain sockets in C++ do not support the abstract namespace - * [THRIFT-1455] - TBinaryProtocolT::writeString casts from size_t to uint32_t, which is not safe on 64-bit platforms - * [THRIFT-1579] - PHP Extention - function thrift_protocol_read_binary not working from TBinarySerializer::deserialize - * [THRIFT-1584] - Error: could not SetMinThreads in ThreadPool on single-core machines - * [THRIFT-1614] - Thrift build from svn repo sources fails with automake-1.12 - * [THRIFT-1047] - rb_thrift_memory_buffer_write treats arg as string without check, segfaults if you pass non-string - * [THRIFT-1639] - Java/Python: Serialization/Deserialization of double type using CompactProtocol - * [THRIFT-1647] - NodeJS BufferedTransport does not work beyond the hello-world example - * [THRIFT-2130] - Thrift's D library/test: parts of "make check" code do not compile with recent dmd-2.062 through dmd-2.064alpha - * [THRIFT-2140] - Error compiling cpp tutorials - * [THRIFT-2139] - MSVC 2012 Error - Cannot compile due to BoostThreadFactory - * [THRIFT-2138] - pkgconfig file created with wrong include path - * [THRIFT-2160] - Warning in thrift.h when compiling with -Wunused and NDEBUG - * [THRIFT-2158] - Compact, JSON, and SimpleJSON protocols are not working correctly - * [THRIFT-2167] - nodejs lib throws error if options argument isn't passed - * [THRIFT-2288] - Go impl of Thrift JSON protocol wrongly writes/expects true/false for bools - * [THRIFT-2147] - Thrift IDL grammar allows for dotted identifier names - * [THRIFT-2145] - Rack and Thin are not just development dependencies - * [THRIFT-2267] - Should be able to choose socket family in Python TSocket - * [THRIFT-2276] - java path in spec file needs updating - * [THRIFT-2281] - Generated send/recv code ignores errors returned by the underlying protocol - * [THRIFT-2280] - TJSONProtocol.Flush() does not really flush the transport - * [THRIFT-2274] - TNonblockingServer and TThreadedSelectorServer do not close their channel selectors on exit and leak file descriptors - * [THRIFT-2265] - php library doesn't build - * [THRIFT-2232] - IsSet* broken in Go - * [THRIFT-2246] - Unset enum value is printed by ToString() - * [THRIFT-2240] - thrift.vim (contrib) does not correctly handle 'union' - * [THRIFT-2243] - TNonblockingServer in thrift crashes when TFramedTransport opens - * [THRIFT-2230] - Cannot Build on RHEL/Centos/Amazon Linux 6.x - * [THRIFT-2247] - Go generator doesn't deal well with map keys of type binary - * [THRIFT-2253] - Python Tornado TTornadoServer base class change - * [THRIFT-2261] - java: error: unmappable character for encoding ASCII - * [THRIFT-2259] - C#: unexpected null logDelegate() pointer causes AV in TServer.serve() - * [THRIFT-2225] - SSLContext destroy before cleanupOpenSSL - * [THRIFT-2224] - TSSLSocket.h and TSSLServerSocket.h should use the platfromsocket too - * [THRIFT-2229] - thrift failed to build on OSX 10.9 GM - * [THRIFT-2227] - Thrift compiler generates spurious warnings with Xlint - * [THRIFT-2219] - Thrift gem fails to build on OS X Mavericks with 1.9.3 rubies - * [THRIFT-2226] - TServerSocket - keepAlive wrong initialization order - * [THRIFT-2285] - TJsonProtocol implementation for Java doesn't allow a slash (/) to be escaped (\/) - * [THRIFT-2216] - Extraneous semicolon in TProtocolUtil.h makes clang mad - * [THRIFT-2215] - Generated HTML/Graphviz lists referenced enum identifiers as UNKNOWN. - * [THRIFT-2211] - Exception constructor does not contain namespace prefix. - * [THRIFT-2210] - lib/java TSimpleJSONProtocol can emit invalid JSON - * [THRIFT-2209] - Ruby generator -- please namespace classes - * [THRIFT-2202] - Delphi TServerImpl.DefaultLogDelegate may stop the server with I/O-Error 105 - * [THRIFT-2201] - Ternary operator returns different types (build error for some compilers) - * [THRIFT-2200] - nested structs cause generate_fingerprint() to slow down at excessive CPU load - * [THRIFT-2197] - fix jar output directory in rpm spec file - * [THRIFT-2196] - Fix invalid dependency in Makefile.am - * [THRIFT-2194] - Node: Not actually prepending residual data in TFramedTransport.receiver - * [THRIFT-2193] - Java code generator emits spurious semicolon when deep copying binary data - * [THRIFT-2191] - Fix charp JSONProtocol.ReadJSONDouble (specify InvariantCulture) - * [THRIFT-2214] - System header sys/param.h is included inside the Thrift namespace - * [THRIFT-2178] - Thrift generator returns error exit code on --version - * [THRIFT-2171] - NodeJS implementation has extremely low test coverage - * [THRIFT-2183] - gem install fails on zsh - * [THRIFT-2182] - segfault in regression tests (GC bug in rb_thrift_memory_buffer_write) - * [THRIFT-2181] - oneway calls don't work in NodeJS - * [THRIFT-2169] - JavaME Thrift Library causes "java.io.IOException: No Response Entries Available" after using the Thrift client for some time - * [THRIFT-2168] - Node.js appears broken (at least, examples don't work as intended) - * [THRIFT-2293] - TSSLTransportFactory.createSSLContext() leaves files open - * [THRIFT-2279] - TSerializer only returns the first 1024 bytes serialized - * [THRIFT-2278] - Buffered transport doesn't support writes > buffer size - * [THRIFT-2275] - Fix memory leak in golang compact_protocol. - * [THRIFT-2282] - Incorect code generated for some typedefs - * [THRIFT-2009] - Go redeclaration error - * [THRIFT-1964] - 'Isset' causes problems with C#/.NET serializers - * [THRIFT-2026] - Fix TCompactProtocol 64 bit builds - * [THRIFT-2108] - Fix TAsyncClientManager timeout race - * [THRIFT-2068] - Multiple calls from same connection are not processed in node - * [THRIFT-1750] - Make compiler build cleanly under visual studio 10 - * [THRIFT-1755] - Comment parsing bug - * [THRIFT-1771] - "make check" fails on x64 for libboost_unit_test_framework.a - * [THRIFT-1841] - NodeJS Thrift incorrectly parses non-UTF8-string types - * [THRIFT-1908] - Using php thrift_protocol accelerated transfer causes core dump - * [THRIFT-1892] - Socket timeouts are declared in milli-seconds, but are actually set in micro-seconds - * [THRIFT-2303] - TBufferredTransport not properly closing underlying transport - * [THRIFT-2313] - nodejs server crash after processing the first request when using MultiplexedProcessor/FramedBuffer/BinaryProtocol - * [THRIFT-2311] - Go: invalid code generated when exception name is a go keyword - * [THRIFT-2308] - node: TJSONProtocol parse error when reading from buffered message - * [THRIFT-2316] - ccp: TFileTransportTest - * [THRIFT-2352] - msvc failed to compile thrift tests - * [THRIFT-2337] - Golang does not report TIMED_OUT exceptions - * [THRIFT-2340] - Generated server implementation does not send response type EXCEPTION on the Thrift.TApplicationExceptionType.UNKNOWN_METHOD exception - * [THRIFT-2354] - Connection errors can lead to case_clause exceptions - * [THRIFT-2339] - Uncaught exception in thrift c# driver - * [THRIFT-2356] - c++ thrift client not working with ssl (SSL_connect hangs) - * [THRIFT-2331] - Missing call to ReadStructBegin() in TApplicationException.Read() - * [THRIFT-2323] - Uncompileable Delphi code generated for typedef'd structs - * [THRIFT-2322] - Correctly show the number of times ExecutorService (java) has rejected the client. - * [THRIFT-2389] - namespaces handled wrongly in acrionscript 3.0 implementation - * [THRIFT-2388] - GoLang - Fix data races in simple_server and server_socket - * [THRIFT-2386] - Thrift refuses to link yylex - * [THRIFT-2375] - Excessive
's in generated HTML - * [THRIFT-2373] - warning CS0414 in THttpClient.cs: private field 'Thrift.Transport.THttpClient.connection' assigned but never used - * [THRIFT-2372] - thrift/json_protocol.go:160: function ends without a return statement - * [THRIFT-2371] - ruby bundler version fails on ~1.3.1, remove and take latest avail - * [THRIFT-2370] - Compiler SEGFAULTs generating HTML documentation for complex strucre - * [THRIFT-2384] - Binary map keys produce uncompilable code in go - * [THRIFT-2380] - unreachable code (CID 1174546, CID 1174679) - * [THRIFT-2378] - service method arguments of binary type lead to uncompileable Go code - * [THRIFT-2363] - Issue with character encoding of Success returned from Login using Thrift Proxy and NodeJS - * [THRIFT-2359] - TBufferedTransport doesn't clear it's buffer on a failed flush call - * [THRIFT-2428] - Python 3 setup.py support - * [THRIFT-2367] - Build failure: stdlib and boost both define uint64_t - * [THRIFT-2365] - C# decodes too many binary bytes from JSON - * [THRIFT-2402] - byte count of FrameBuffer in AWAITING_CLOSE state is not subtracted from readBufferBytesAllocated - * [THRIFT-2396] - Build Error on MacOSX - * [THRIFT-2395] - thrift Ruby gem requires development dependency 'thin' regardless of environment - * [THRIFT-2414] - c_glib fix several bug. - * [THRIFT-2420] - Go argument parser for methods without arguments does not skip fields - * [THRIFT-2439] - Bug in TProtocolDecorator Class causes parsing errors - * [THRIFT-2419] - golang - Fix fmt.Errorf in generated code - * [THRIFT-2418] - Go handler function panics on internal error - * [THRIFT-2405] - Node.js Multiplexer tests fail (silently) - * [THRIFT-2581] - TFDTransport destructor should not throw - * [THRIFT-2575] - Thrift includes siginfo_t within apache::thrift::protocol namespace - * [THRIFT-2577] - TFileTransport missuse of closesocket on windows platform - * [THRIFT-2576] - Implement Thrift.Protocol.prototype.skip method in JavaScript library - * [THRIFT-2588] - Thrift compiler is not buildable in Visual Studio 2010 - * [THRIFT-2594] - JS Compiler: Single quotes are not being escaped in constants. - * [THRIFT-2591] - TFramedTransport does not handle payloads split across packets correctly - * [THRIFT-2599] - Uncompileable Delphi code due to naming conflicts with IDL - * [THRIFT-2590] - C++ Visual Studio solution doesn't include Multiplexing support - * [THRIFT-2595] - Node.js: Fix global leaks and copy-paste errors - * [THRIFT-2565] - autoconf fails to find mingw-g++ cross compiler on travis CI - * [THRIFT-2555] - excessive "unused field" comments - * [THRIFT-2554] - double initialization in generated Read() method - * [THRIFT-2551] - OutOfMemoryError "unable to create new native thread" kills serve thread - * [THRIFT-2543] - Generated enum type in haskell should be qualified - * [THRIFT-2560] - Thrift compiler generator tries to concat ints with strings using + - * [THRIFT-2559] - Centos 6.5 unable to "make" with Thrift 0.9.1 - * [THRIFT-2526] - Assignment operators and copy constructors in c++ don't copy the __isset struct - * [THRIFT-2454] - c_glib: There is no gethostbyname_r() in some OS. - * [THRIFT-2451] - Do not use pointers for optional fields with defaults. Do not write such fields if its value set to default. Also, do not use pointers for any optional fields mapped to go map or slice. generate Get accessors - * [THRIFT-2450] - include HowToContribute in the src repo - * [THRIFT-2448] - thrift/test/test.sh has incorrect Node.js test path - * [THRIFT-2460] - unopened socket fd must be less than zero. - * [THRIFT-2459] - --version should not exit 1 - * [THRIFT-2468] - Timestamp handling - * [THRIFT-2467] - Unable to build contrib/fb303 on OSX 10.9.2 - * [THRIFT-2466] - Improper error handling for SSL/TLS connections that don't complete a handshake - * [THRIFT-2463] - test/py/RunClientServer.py fails sometimes - * [THRIFT-2458] - Generated golang server code for "oneway" methods is incorrect - * [THRIFT-2456] - THttpClient fails when using async support outside Silverlight - * [THRIFT-2524] - Visual Studio project is missing TThreadedServer files - * [THRIFT-2523] - Visual Studio project is missing OverlappedSubmissionThread files - * [THRIFT-2520] - cpp:cob_style generates incorrect .tcc file - * [THRIFT-2508] - Uncompileable C# code due to language keywords in IDL - * [THRIFT-2506] - Update TProtocolException error codes to be used consistently throughout the library - * [THRIFT-2505] - go: struct should always be a pointer to avoid copying of potentially size-unbounded structs - * [THRIFT-2515] - TLS Method error during make - * [THRIFT-2503] - C++: Fix name collision when a struct has a member named "val" - * [THRIFT-2477] - thrift --help text with misplaced comma - * [THRIFT-2492] - test/cpp does not compile on mac - * [THRIFT-2500] - sending random data crashes thrift(golang) service - * [THRIFT-2475] - c_glib: buffered_transport_write function return always TRUE. - * [THRIFT-2495] - JavaScript/Node string constants lack proper escaping - * [THRIFT-2491] - unable to import generated ThriftTest service - * [THRIFT-2490] - c_glib: if fail to read a exception from server, client may be occurred double free - * [THRIFT-2470] - THttpHandler swallows exceptions from processor - * [THRIFT-2533] - Boost version in requirements should be updated - * [THRIFT-2532] - Java version in installation requirements should be updated - * [THRIFT-2529] - TBufferedTransport split Tcp data bug in nodeJs - * [THRIFT-2537] - Path for "go get" does not work (pull request 115) - * [THRIFT-2443] - Node fails cross lang tests - * [THRIFT-2437] - Author fields in Python setup.py must be strings not lists. - * [THRIFT-2435] - Java compiler doesn't like struct member names that are identical to an existing enum or struct type - * [THRIFT-2434] - Missing namespace import for php TMultiplexedProcessor implementation - * [THRIFT-2432] - Flaky parallel build - * [THRIFT-2430] - Crash during TThreadPoolServer shutdown - * [THRIFT-667] - Period should not be allowed in identifier names - * [THRIFT-1212] - Members capital case conflict - * [THRIFT-2584] - Error handler not listened on javascript client - * [THRIFT-2294] - Incorrect Makefile generation - * [THRIFT-2601] - Fix vagrant to work again for builds again - * [THRIFT-2092] - TNonblocking server should release handler as soon as connection closes - * [THRIFT-2557] - CS0542 member names cannot be the same as their enclosing type - * [THRIFT-2605] - TSocket warning on gcc 4.8.3 - * [THRIFT-2607] - ThreadManager.cpp warning on clang++ 3.4 - * [THRIFT-1998] - TCompactProtocol.tcc - one more warning on Visual 2010 - * [THRIFT-2610] - MSVC warning in TSocket.cpp - * [THRIFT-2614] - TNonblockingServer.cpp warnings on MSVC - * [THRIFT-2608] - TNonblockingServer.cpp warnings on clang 3.4 - * [THRIFT-2606] - ThreadManager.h warning in clang++ 3.4 - * [THRIFT-2609] - TFileTransport.h unused field warning (clang 3.4) - * [THRIFT-2416] - Cannot use TCompactProtocol with MSVC - * [THRIFT-1803] - Ruby Thrift 0.9.0 tries to encode UUID to UTF8 and crashes - * [THRIFT-2385] - Problem with gethostbyname2 during make check - * [THRIFT-2262] - thrift server 'MutateRow' operation gives no indication of success / failure - * [THRIFT-2048] - Prefer boolean context to nullptr_t conversion - * [THRIFT-2528] - Thrift Erlang Library: Multiple thrift applications in one bundle - * [THRIFT-1999] - warning on gcc 4.7 while compiling BoostMutex.cpp - * [THRIFT-2104] - Structs lose binary data when transferred from server to client in Java - * [THRIFT-2184] - undefined method rspec_verify for Thrift::MemoryBufferTransport - * [THRIFT-2351] - PHP TCompactProtocol has fails to decode messages - * [THRIFT-2016] - Resource Leak in thrift struct under compiler/cpp/src/parse/t_function.h - * [THRIFT-2273] - Please delete old releases from mirroring system - * [THRIFT-2270] - Faulty library version numbering at build or documentation - * [THRIFT-2203] - Tests keeping failing on Jenkins and Travis CI - * [THRIFT-2399] - thrift.el: recognize "//"-style comments in emacs thrift-mode - * [THRIFT-2582] - "FileTransport error" exception is raised when trying to use Java's TFileTransport - * [THRIFT-1682] - Multiple thread calling a Service function unsafely causes message corruption and terminates with Broken Pipe - * [THRIFT-2357] - recurse option has no effect when generating php - * [THRIFT-2248] - Go generator doesn't deal well with map keys of type binary - * [THRIFT-2426] - clarify IP rights and contributions from fbthrift - * [THRIFT-2041] - TNonblocking server compilation on windows (ARITHMETIC_RIGHT_SHIFT) - * [THRIFT-2400] - thrift.el: recognize "//"-style comments in emacs thrift-mode - * [THRIFT-1717] - Fix deb build in jenkins - * [THRIFT-2266] - ThreadManager.h:24:10: fatal error: 'tr1/functional' file not found on Mac 10.9 (Mavericks) - * [THRIFT-1300] - Test failures with parallel builds (make -j) - * [THRIFT-2487] - Tutorial requires two IDL files but only one is linked from the Thrift web site - * [THRIFT-2329] - missing release tags within git - * [THRIFT-2306] - concurent client calls with nodejs - * [THRIFT-2222] - ruby gem cannot be compiled on OS X mavericks - * [THRIFT-2381] - code which generated by thrift2/hbase.thrift compile error - * [THRIFT-2390] - no close event when connection lost - * [THRIFT-2146] - Unable to pass multiple "--gen" options to the thrift compiler - * [THRIFT-2438] - Unexpected readFieldEnd call causes JSON Parsing errors - * [THRIFT-2498] - Error message "Invalid method name" while trying to call HBase Thrift API - * [THRIFT-841] - Build cruft - * [THRIFT-2570] - Wrong URL given in http://thrift.apache.org/developers - * [THRIFT-2604] - Fix debian packaging - * [THRIFT-2618] - Unignore /aclocal files required for build - * [THRIFT-2562] - ./configure create MakeFile in lib/d with errors - * [THRIFT-2593] - Unable to build thrift on ubuntu-12.04 (Precise) - * [THRIFT-2461] - Can't install thrift-0.8.0 on OS X 10.9.2 - * [THRIFT-2602] - Fix missing dist files - * [THRIFT-2620] - Fix python packaging - * [THRIFT-2545] - Test CPP fails to build (possibly typo) - -## Documentation - * [THRIFT-2155] - Adding one liner guide to rename the version.h.in and rename thrifty.cc.h - * [THRIFT-1991] - Add exceptions to examples - * [THRIFT-2334] - add a tutorial for node JS - * [THRIFT-2392] - Actionscript tutorial - * [THRIFT-2383] - contrib: sample for connecting Thrift with Rebus - * [THRIFT-2382] - contrib: sample for connecting Thrift with STOMP - -## Improvement - * [THRIFT-1457] - Capacity of TframedTransport write buffer is never reset - * [THRIFT-1135] - Node.js tutorial - * [THRIFT-1371] - Socket timeouts (SO_RCVTIMEO and SO_SNDTIMEO) not supported on Solaris - * [THRIFT-2142] - Minor tweaks to thrift.el for better emacs package compatibility - * [THRIFT-2268] - Modify TSaslTransport to ignore TCP health checks from loadbalancers - * [THRIFT-2264] - GitHub page incorrectly states that Thrift is still incubating - * [THRIFT-2263] - Always generate good hashCode for Java - * [THRIFT-2233] - Java compiler should defensively copy its binary inputs - * [THRIFT-2239] - Address FindBugs errors - * [THRIFT-2249] - Add SMP Build option to thrift.spec (and three config defines) - * [THRIFT-2254] - Exceptions generated by Go compiler should implement error interface - * [THRIFT-2260] - Thrift imposes unneeded dependency on commons-lang3 - * [THRIFT-2258] - Add TLS v1.1/1.2 support to TSSLSocket.cpp - * [THRIFT-2205] - Node.js Test Server to support test.js JavaScript Browser test and sundry fixes - * [THRIFT-2204] - SSL client for the cocoa client - * [THRIFT-2172] - Java compiler allocates optionals array for every struct with an optional field - * [THRIFT-2185] - use cabal instead of runhaskell in haskell library - * [THRIFT-1926] - PHP Constant Generation Refactoring - * [THRIFT-2029] - Port C++ tests to Windows - * [THRIFT-2054] - TSimpleFileTransport - Java Lib has no straight forward TTransport based file transport - * [THRIFT-2040] - "uninitialized variable" warnings on MSVC/windows - * [THRIFT-2034] - Give developers' C++ code direct access to socket FDs on server side - * [THRIFT-2095] - Use print function for Python 3 compatiblity - * [THRIFT-1868] - Make the TPC backlog configurable in the Java servers - * [THRIFT-1813] - Add @Generated annotation to generated classes - * [THRIFT-1815] - Code generators line buffer output - * [THRIFT-2305] - TFramedTransport empty constructor should probably be private - * [THRIFT-2304] - Move client assignments from construtor in method - * [THRIFT-2309] - Ruby (gem) & PHP RPM subpackages - * [THRIFT-2318] - perl: dependency Class::Accessor not checked - * [THRIFT-2317] - exclude tutorial from build - * [THRIFT-2320] - Program level doctext does not get attached by parser - * [THRIFT-2349] - Golang - improve tutorial - * [THRIFT-2348] - PHP Generator: add array typehint to functions - * [THRIFT-2344] - configure.ac: compiler-only option - * [THRIFT-2343] - Golang - Return a single error for all exceptions instead of multiple return values - * [THRIFT-2341] - Enable generation of Delphi XMLDoc comments (a.k.a. "Help Insight") - * [THRIFT-2355] - Add SSL and Web Socket Support to Node and JavaScript - * [THRIFT-2350] - Add async calls to normal JavaScript - * [THRIFT-2330] - Generate PHPDoc comments - * [THRIFT-2332] - RPMBUILD: run bootstrap (if needed) - * [THRIFT-2391] - simple socket transport for actionscript 3.0 - * [THRIFT-2376] - nodejs: allow Promise style calls for client and server - * [THRIFT-2369] - Add ssl support for nodejs implementation - * [THRIFT-2401] - Haskell tutorial compiles - * [THRIFT-2417] - C# Union classes are not partial - * [THRIFT-2415] - Named pipes server performance & message mode - * [THRIFT-2404] - emit warning on (typically inefficient) list - * [THRIFT-2398] - Improve Node Server Library - * [THRIFT-2397] - Add CORS and CSP support for JavaScript and Node.js libraries - * [THRIFT-2407] - use markdown (rename README => README.md) - * [THRIFT-2300] - D configure info output should follow same format as other languages - * [THRIFT-2579] - Windows CE support - * [THRIFT-2574] - Compiler option to generate namespace directories for Ruby - * [THRIFT-2571] - Simplify cross compilation using CMake - * [THRIFT-2569] - Introduce file to specify third party library locations on Windows - * [THRIFT-2568] - Implement own certificate handler - * [THRIFT-2552] - eliminate warning from configure.ac - * [THRIFT-2549] - Generate json tag for struct members. use go.tag annotation to override the default generated tag. - * [THRIFT-2544] - Add support for socket transport for c# library when using Windows Phone projects - * [THRIFT-2453] - haskell tutorial: fix up division by 0 example - * [THRIFT-2449] - Enhance typedef structure to distinguish between forwards and real typedefs - * [THRIFT-2446] - There is no way to handle server stream errors - * [THRIFT-2455] - Allow client certificates to be used with THttpClient - * [THRIFT-2511] - Node.js needs the compact protocol - * [THRIFT-2493] - Node.js lib needs HTTP client - * [THRIFT-2502] - Optimize go implementations of binary and compact protocols for speed - * [THRIFT-2494] - Add enum toString helper function in c_glib - * [THRIFT-2471] - Make cpp.ref annotation language agnostic - * [THRIFT-2497] - server and client for test/go, also several fixes and improvements - * [THRIFT-2535] - TJSONProtocol when serialized yields TField ids rather than names - * [THRIFT-2220] - Add a new struct structv? - * [THRIFT-1352] - Thrift server - * [THRIFT-989] - Push boost m4 macros upstream - * [THRIFT-1349] - Remove unnecessary print outs - * [THRIFT-2496] - server and client for test/go, also several fixes and improvements - * [THRIFT-1114] - Maven publish shouldn't require passwords hardcoded in settings.xml - * [THRIFT-2043] - visual 2010 warnings - unreachable code - * [THRIFT-1683] - Implement alternatives to Javascript Client side Transport protocol, just as NPAPI and WebSocket. - * [THRIFT-1746] - provide a SPDX file - * [THRIFT-1772] - Serialization does not check types of embedded structures. - * [THRIFT-2387] - nodejs: external imports should be centralized in index.js - * [THRIFT-2037] - More general macro THRIFT_UNUSED_VARIABLE - -## New Feature - * [THRIFT-1012] - Transport for DataInput DataOutput interface - * [THRIFT-2256] - Using c++11/c++0x std library replace boost library - * [THRIFT-2250] - JSON and MemoryBuffer for JavaME - * [THRIFT-2114] - Python Service Remote SSL Option - * [THRIFT-1719] - SASL client support for Python - * [THRIFT-1894] - Thrift multi-threaded async Java Server using Java 7 AsynchronousChannelGroup - * [THRIFT-1893] - HTTP/JSON server/client for node js - * [THRIFT-2347] - C# TLS Transport based on THRIFT-181 - * [THRIFT-2377] - Allow addition of custom HTTP Headers to an HTTP Transport - * [THRIFT-2408] - Named Pipe Transport Option for C# - * [THRIFT-2572] - Add string/collection length limit checks (from C++) to java protocol readers - * [THRIFT-2469] - "java:fullcamel" option to automatically camel-case underscored attribute names - * [THRIFT-795] - Importing service functions (simulation multiple inheritance) - * [THRIFT-2164] - Add a Get/Post Http Server to Node along with examples - * [THRIFT-2255] - add Parent Class for generated Struct class - -## Question - * [THRIFT-2539] - Tsocket.cpp addrinfo ai_flags = AI_ADDRCONFIG - * [THRIFT-2440] - how to connect as3 to java by thrift , - * [THRIFT-2379] - Memmory leaking while using multithreading in C++ server. - * [THRIFT-2277] - Thrift: installing fb303 error - * [THRIFT-2567] - Csharp slow ? - * [THRIFT-2573] - thrift 0.9.2 release - -## Sub-task - * [THRIFT-981] - cocoa: add version Info to the library - * [THRIFT-2132] - Go: Support for Multiplexing Services on any Transport, Protocol and Server - * [THRIFT-2299] - TJsonProtocol implementation for Ruby does not allow for both possible slash (solidus) encodings - * [THRIFT-2298] - TJsonProtocol implementation for C# does not allow for both possible slash (solidus) encodings - * [THRIFT-2297] - TJsonProtocol implementation for Delphi does not allow for both possible slash (solidus) encodings - * [THRIFT-2271] - JavaScript: Support for Multiplexing Services - * [THRIFT-2251] - go test for compact protocol is not running - * [THRIFT-2195] - Delphi: Add event handlers for server and processing events - * [THRIFT-2176] - TSimpleJSONProtocol.ReadFieldBegin() does not return field type and ID - * [THRIFT-2175] - Wrong field type set for binary - * [THRIFT-2174] - Deserializing JSON fails in specific cases - * [THRIFT-2053] - NodeJS: Support for Multiplexing Services - * [THRIFT-1914] - Python: Support for Multiplexing Services on any Transport, Protocol and Server - * [THRIFT-1810] - add ruby to test/test.sh - * [THRIFT-2310] - PHP: Client-side support for Multiplexing Services - * [THRIFT-2346] - C#: UTF-8 sent by PHP as JSON is not understood by TJsonProtocol - * [THRIFT-2345] - Delphi: UTF-8 sent by PHP as JSON is not understood by TJsonProtocol - * [THRIFT-2338] - First doctext wrongly interpreted as program doctext in some cases - * [THRIFT-2325] - SSL test certificates - * [THRIFT-2358] - C++: add compact protocol to cross language test suite - * [THRIFT-2425] - PHP: Server-side support for Multiplexing Services - * [THRIFT-2421] - Tree/Recursive struct support in thrift - * [THRIFT-2290] - Update Go tutorial to align with THRIFT-2232 - * [THRIFT-2558] - CSharp compiler generator tries to concat ints with strings using + - * [THRIFT-2507] - Additional LUA TProtocolException error code needed? - * [THRIFT-2499] - Compiler: allow annotations without "= value" - * [THRIFT-2534] - Cross language test results should recorded to a status.md or status.html file automatically - * [THRIFT-66] - Java: Allow multiplexing multiple services over a single TCP connection - * [THRIFT-1681] - Add Lua Support - * [THRIFT-1727] - Ruby-1.9: data loss: "binary" fields are re-encoded - * [THRIFT-1726] - Ruby-1.9: "binary" fields are represented by string whose encoding is "UTF-8" - * [THRIFT-988] - perl: add version Info to the library via configure - * [THRIFT-334] - Compact Protocol for PHP - * [THRIFT-2444] - pull request 88: thrift: clean up enum value assignment - -## Task - * [THRIFT-2223] - Spam links on wiki - * [THRIFT-2566] - Please create a DOAP file for your TLP - * [THRIFT-2237] - Update archive to contain all versions - * [THRIFT-962] - Tutorial page on our website is really unhelpful - -## Test - * [THRIFT-2327] - nodejs: nodejs test suite should be bundled with the library - * [THRIFT-2445] - THRIFT-2384 (code generation for go maps with binary keys) should be tested - * [THRIFT-2501] - C# The test parameters from the TestServer and TestClient are different from the http://thrift.apache.org/test/ - -## Wish - * [THRIFT-2190] - Add the JavaScript thrift.js lib to the Bower registry - * [THRIFT-2076] - boost::optional instead of __isset - - - -Thrift 0.9.1 --------------------------------------------------------------------------------- -## Bug - * [THRIFT-1440] - debian packaging: minor-ish policy problems - * [THRIFT-1402] - Generated Y_types.js does not require() X_types.js when an include in the IDL file was used - * [THRIFT-1551] - 2 thrift file define only struct (no service), one include another, the gen nodejs file didn't have "requires" at the top - * [THRIFT-1264] - TSocketClient is queried by run loop after deallocation in Cocoa - * [THRIFT-1600] - Thrift Go Compiler and Library out of date with Go 1 Release. - * [THRIFT-1603] - Thrift IDL allows for multiple exceptions, args or struct member names to be the same - * [THRIFT-1062] - Problems with python tutorials - * [THRIFT-864] - default value fails if identifier is a struct - * [THRIFT-930] - Ruby and Haskell bindings don't properly support DESTDIR (makes packaging painful) - * [THRIFT-820] - The readLength attribute of TBinaryProtocol is used as an instance variable and is decremented on each call of checkReadLength - * [THRIFT-1640] - None of the tutorials linked on the website contain content - * [THRIFT-1637] - NPM registry does not include version 0.8 - * [THRIFT-1648] - NodeJS clients always receive 0 for 'double' values. - * [THRIFT-1660] - Python Thrift library can be installed with pip but not easy_install - * [THRIFT-1657] - Chrome browser sending OPTIONS method before POST in xmlHttpRequest - * [THRIFT-2118] - Certificate error handling still incorrect - * [THRIFT-2137] - Ruby test lib fails jenkins build #864 - * [THRIFT-2136] - Vagrant build not compiling java, ruby, php, go libs due to missing dependencies - * [THRIFT-2135] - GO lib leaves behind test files that are auto generated - * [THRIFT-2134] - mingw-cross-compile script failing with strip errors - * [THRIFT-2133] - java TestTBinaryProtocol.java test failing - * [THRIFT-2126] - lib/cpp/src/thrift/concurrency/STD* files missing from DIST - * [THRIFT-2125] - debian missing from DIST - * [THRIFT-2124] - .o, .so, .la, .deps, .libs, gen-* files left tutorials, test and lib/cpp when making DIST - * [THRIFT-2123] - GO lib missing files in DIST build - * [THRIFT-2121] - Compilation bug for Node.js - * [THRIFT-2129] - php ext missing from dist - * [THRIFT-2128] - lib GO tests fail with funct ends without a return statement - * [THRIFT-2286] - Failed to compile Thrift0.9.1 with boost1.55 by VS2010 if select Debug-mt&x64 mode. - * [THRIFT-1973] - TCompactProtocol in C# lib does not serialize and deserialize negative int32 and int64 number correctly - * [THRIFT-1992] - casts in TCompactProtocol.tcc causing "dereferencing type-punned pointer will break strict-aliasing rules" warnings from gcc - * [THRIFT-1930] - C# generates unsigned byte for Thrift "byte" type - * [THRIFT-1929] - Update website to use Mirrors for downloads - * [THRIFT-1928] - Race may still exist in TFileTransport::flush() - * [THRIFT-1934] - Tabs in Example section on main page are not working - * [THRIFT-1933] - Delphi generator crashes when a typedef references another typedef from an included file - * [THRIFT-1942] - Binary accelerated cpp extension does not use Thrift namespaces for Exceptions - * [THRIFT-1959] - C#: Add Union TMemoryBuffer support - * [THRIFT-1958] - C#: Use static Object.Equals instead of .Equals() calls in equals - * [THRIFT-1957] - NodeJS TFramedTransport and TBufferedTransport read bytes as unsigned - * [THRIFT-1955] - Union Type writer generated in C# does not WriteStructBegin - * [THRIFT-1952] - Travis CI - * [THRIFT-1949] - WP7 build broken - * [THRIFT-1943] - docstrings for enum values are ignored - * [THRIFT-2070] - Improper `HexChar' and 'HexVal' implementation in TJSONProtocol.cs - * [THRIFT-2017] - Resource Leak in thrift struct under compiler/cpp/src/parse/t_program.h - * [THRIFT-2032] - C# client leaks sockets/handles - * [THRIFT-1996] - JavaME Constants generation is broken / inconsistent with regular Java generation - * [THRIFT-2002] - Haskell: Test use Data.Maybe instead of Maybe - * [THRIFT-2051] - Vagrant fails to build erlang - * [THRIFT-2050] - Vagrant C# lib compile fails with TException missing - * [THRIFT-1978] - Ruby: Thrift should allow for the SSL verify mode to be set - * [THRIFT-1984] - namespace collision in python bindings - * [THRIFT-1988] - When trying to build a debian package it fails as the file NEWS doesn't exist - * [THRIFT-1975] - TBinaryProtocol CheckLength can't be used for a client - * [THRIFT-1995] - '.' allowed at end of identifier generates non-compilable code - * [THRIFT-2112] - Error in Go generator when using typedefs in map keys - * [THRIFT-2088] - Typos in Thrift compiler help text - * [THRIFT-2080] - C# multiplex processor does not catch IOException - * [THRIFT-2082] - Executing "gmake clean" is broken - * [THRIFT-2102] - constants are not referencing to correct type when included from another thrift file - * [THRIFT-2100] - typedefs are not correctly referenced when including from other thrift files - * [THRIFT-2066] - 'make install' does not install two headers required for C++ bindings - * [THRIFT-2065] - Not valid constants filename in Java - * [THRIFT-2047] - Thrift.Protocol.TCompactProtocol, intToZigZag data lost (TCompactProtocol.cs) - * [THRIFT-2036] - Thrift gem warns about class variable access from top level - * [THRIFT-2057] - Vagrant fails on php tests - * [THRIFT-2105] - Generated code for default values of collections ignores t_field::T_REQUIRED - * [THRIFT-2091] - Unnecessary 'friend' declaration causes warning in TWinsockSingleton - * [THRIFT-2090] - Go generator, fix including of other thrift files - * [THRIFT-2106] - Fix support for namespaces in GO generator - * [THRIFT-1783] - C# doesn't handle required fields correctly - * [THRIFT-1782] - async only defined in silverlight - * [THRIFT-1779] - Missing process_XXXX method in generated TProcessor implementation for all 'oneway' service functions - * [THRIFT-1692] - SO_REUSEADDR allows for socket hijacking on Windows - * [THRIFT-1720] - JRuby times out on successful connection - * [THRIFT-1713] - Named and Anonymous Pipe transport (Delphi) - * [THRIFT-1699] - Native Union#read has extra read_field_end call - * [THRIFT-1749] - Python TSSLSocket error handling obscures actual error - * [THRIFT-1748] - Guard and RWGuard macros defined in global namespace - * [THRIFT-1734] - Front webpage is still advertising v0.8 as current release - * [THRIFT-1729] - C glib refactor left empty folders in svn - * [THRIFT-1767] - unions can't have required fields (Delphi) - * [THRIFT-1765] - Incorrect error message printed for null or negative keys - * [THRIFT-1778] - Configure requires manual intervention due to tar failure - * [THRIFT-1777] - TPipeServer is UNSTOPPABLE - * [THRIFT-1753] - Multiple C++ Windows, OSX, and iOS portability issues - * [THRIFT-1756] - 'make -j 8' fails with "unterminated #ifdef" error - * [THRIFT-1773] - Python library should run on python 2.4 - * [THRIFT-1769] - unions can't have required fields (C++) - * [THRIFT-1768] - unions can't have required fields (Compiler) - * [THRIFT-1666] - htonll usage in TBinaryProtocol.tcc generates warning with MSVC2010 - * [THRIFT-1919] - libthrift depends on httpcore-4.1.3 (directly) and httpcore-4.1.4 (transitively) - * [THRIFT-1864] - implement event handler for non-blocking server - * [THRIFT-1859] - Generated error c++ code with -out and include_prefix param - * [THRIFT-1869] - TThreadPoolServer (java) dies when threadpool is consumed - * [THRIFT-1842] - Memory leak with Pipes - * [THRIFT-1838] - Can't build compiler on OS X because of missing thrifty.h - * [THRIFT-1846] - Restore socket.h header to support builds with Android NDK - * [THRIFT-1850] - make check hangs on TSocket tests in TransportTest.cpp - * [THRIFT-1873] - Binary protocol factory ignores struct read/write flags - * [THRIFT-1872] - issues with TBufferedTransport buffer - * [THRIFT-1904] - Incorrect code is generated for typedefs which use included types - * [THRIFT-1903] - PHP namespaces cause binary protocols to not be used - * [THRIFT-1895] - Delphi: reserved variable name "result" not detected properly - * [THRIFT-1881] - TNonblockingServer does not release open connections or threads on shutdown - * [THRIFT-1888] - Java Thrift client can't connect to Python Thrift server on same host - * [THRIFT-1831] - Bug in list deserializer - * [THRIFT-1824] - many compile warning, becase Thread.h includes config.h - * [THRIFT-1823] - Missing parenthesis breaks "IS_..." macro in generated code - * [THRIFT-1806] - Python generation always truncates __init__.py files - * [THRIFT-1795] - Race condition in TThreadedServerPool java implementation - * [THRIFT-1794] - C# asyncctp broken - * [THRIFT-1804] - Binary+compact protocol single byte error in Ruby library (ARM architecture): caused by different char signedness - * [THRIFT-1800] - Documentation text not always escaped correctly when rendered to HTML - * [THRIFT-1788] - C#: Constants static constructor does not compile - * [THRIFT-1816] - Need "require" included thrift files in "xxx_types.js" - * [THRIFT-1907] - Compiling namespace and sub-namespace directives for unrecognized generators should only be a warning - * [THRIFT-1913] - skipping unknown fields in java unions - * [THRIFT-2553] - C++ linker error - transport/TSocket - * [THRIFT-274] - Towards a working release/versioning process - -## Documentation - * [THRIFT-1971] - [Graphviz] Adds tutorial/general description documentation - * [THRIFT-2001] - http://thrift.apache.org/ Example "C++ Server" tab is broken - -## Improvement - * [THRIFT-1574] - Apache project branding requirements: DOAP file [PATCH] - * [THRIFT-1347] - Unify the exceptions returned in generated Go code - * [THRIFT-1353] - Switch to performance branch, get rid of BinaryParser - * [THRIFT-1629] - Ruby 1.9 Compatibility during Thrift configure, make, install - * [THRIFT-991] - Refactor Haskell code and generator - * [THRIFT-990] - Sanify gettimeofday usage codebase-wide - * [THRIFT-791] - Let C++ TSimpleServer be driven by an external main loop - * [THRIFT-2117] - Cocoa TBinaryProtocol strictWrite should be set to true by default - * [THRIFT-2014] - Change C++ lib includes to use style throughout - * [THRIFT-1972] - Add support for async processors - * [THRIFT-1970] - [Graphviz] Adds option to render exceptions relationships - * [THRIFT-1966] - Support different files for SSL certificates and keys - * [THRIFT-1965] - Adds Graphviz (graph description language) generator - * [THRIFT-1956] - Switch to Apache Commons Lang 3 - * [THRIFT-1962] - Multiplex processor should send any TApplicationException back to client - * [THRIFT-1960] - main() declares 22 unused gen bools - * [THRIFT-1951] - libthrift.jar has source files in it - * [THRIFT-1997] - Add accept backlog configuration method to TServerSocket - * [THRIFT-2003] - Deprecate senum - * [THRIFT-2052] - Vagrant machine image defaults to only 384MB of RAM - * [THRIFT-1980] - Modernize Go tooling, fix go client libary. - * [THRIFT-1977] - C# compiler should generate constant files prefixed with thrift file name - * [THRIFT-1985] - add a Vagrantfile to build and test Apache Thrift fully reproducable - * [THRIFT-1994] - Deprecate slist - * [THRIFT-1993] - Factory to create instances from known (generated) interface types with Delphi - * [THRIFT-2081] - Specified timeout should be used in TSocket.Open() - * [THRIFT-2084] - Delphi: Ability to create entity Thrift-generated instances based on TypeInfo - * [THRIFT-2083] - Improve the go lib: buffered Transport, save memory allocation, handle concurrent request - * [THRIFT-2109] - Secure connections should be supported in Go - * [THRIFT-2107] - minor Go generator fixes - * [THRIFT-1695] - allow warning-free compilation in VS 2012 and GNU 4.6 - * [THRIFT-1735] - integrate tutorial into regular build - * [THRIFT-1716] - max allowed connections should be PIPE_UNLIMITED_INSTANCES - * [THRIFT-1715] - Allow excluding python parts when building contrib/fb303 - * [THRIFT-1733] - Fix RPM build issues on RHEL6/OL6 systems - * [THRIFT-1728] - Upgradation of httpcomponents - * [THRIFT-1876] - Use enum names instead of casted integers in assignments - * [THRIFT-1874] - timeout for the server-side end of a named pipe - * [THRIFT-1897] - Support validation of required fields - * [THRIFT-1896] - Add TBase protocol for Cocoa - * [THRIFT-1880] - Make named pipes server work asynchronously (overlapped) to allow for clean server stops - * [THRIFT-1878] - Add the possibility to send custom headers - * [THRIFT-1882] - Use single include - * [THRIFT-1793] - C#: Use static read instead of instance read - * [THRIFT-1799] - Option to generate HTML in "standalone mode" - * [THRIFT-1815] - Code generators line buffer output - * [THRIFT-1890] - C++: Make named pipes server work asynchronously - * [THRIFT-474] - Generating Ruby on Rails friendly code - -## New Feature - * [THRIFT-801] - Provide an interactive shell (irb) when generating ruby bindings - * [THRIFT-2292] - Android Library Project - * [THRIFT-2012] - Modernizing Go - * [THRIFT-1969] - C#: Tests not properly linked from the solution - * [THRIFT-1785] - C#: Add TMemoryBuffer serializer/deserializer - * [THRIFT-1780] - Add option to generate nullable values - * [THRIFT-1786] - C# Union Typing - * [THRIFT-591] - Make the C++ runtime library be compatible with Windows and Visual Studio - * [THRIFT-514] - Add option to configure compiler output directory - -## Question - * [THRIFT-1764] - how to get the context of client when on a rpc call in server side? - * [THRIFT-1791] - thrift's namespace directive when generating haskell code - -## Sub-task - * [THRIFT-1594] - Java test clients should have a return codes that reflect whether it succeeds or not. - * [THRIFT-1595] - Java test server should follow the documented behavior as of THRIFT-1590 - * [THRIFT-986] - st: add version Info to the library - * [THRIFT-985] - php: add version Info to the library - * [THRIFT-984] - ocaml: add version Info to the library - * [THRIFT-1924] - Delphi: Inconsistency in serialization of optional fields - * [THRIFT-1922] - C#: Inconsistency in serialization of optional fields - * [THRIFT-1961] - C# tests should be in lib/csharp/test/... - * [THRIFT-1822] - PHP unit test does not work - * [THRIFT-1902] - C++: Support for Multiplexing Services on any Transport, Protocol and Server - * [THRIFT-1901] - C#: Support for Multiplexing Services on any Transport, Protocol and Server - * [THRIFT-1899] - Delphi: Support for Multiplexing Services on any Transport, Protocol and Server - * [THRIFT-563] - Support for Multiplexing Services on any Transport, Protocol and Server - - - -Thrift 0.9 --------------------------------------------------------------------------------- -## Bug - * [THRIFT-1438] - lib/cpp/src/windows/config.h should read version from configure.ac rather than a #define - * [THRIFT-1446] - Compile error with Delphi 2009 in constant initializer - * [THRIFT-1450] - Problems building thrift 0.8.0 for Python and Ruby - * [THRIFT-1449] - Ruby client does not work on solaris (?) - * [THRIFT-1447] - NullpointerException in ProcessFunction.class :in "oneway" method - * [THRIFT-1433] - TServerSocket fix for MSVC - * [THRIFT-1429] - The nonblocking servers is supposed to use TransportFactory to read the data - * [THRIFT-1427] - PHP library uses non-multibyte safe functions with mbstring function overloading - * [THRIFT-1421] - Debian Packages can not be built - * [THRIFT-1394] - Treatment of optional fields is not consistent between C++ and Java - * [THRIFT-1511] - Server with oneway support ( JAVA ) - * [THRIFT-1496] - PHP compiler not namespacing enums - * [THRIFT-1495] - PHP TestClient fatals on missing class - * [THRIFT-1508] - TServerSocket does not allow for the user to specify the IP address to bind to - * [THRIFT-1504] - Cocoa Generator should use local file imports for base Thrift headers - * [THRIFT-1512] - Thrift socket support for Windows XP - * [THRIFT-1502] - TSimpleServer::serve(): Do not print out error message if server was stopped. - * [THRIFT-1501] - PHP old namespaces not generated for enums - * [THRIFT-1483] - java compiler does not generate type parameters for services in extended clauses - * [THRIFT-1479] - Compiled PHP process functions missing writeMessageEnd() - * [THRIFT-1492] - enabling c_glib render thrift unusable (even for C++ code) - * [THRIFT-1491] - Uninitialize processorFactory_ member in TServer.h - * [THRIFT-1475] - Incomplete records generation for Erlang - * [THRIFT-1486] - Javascript manual testserver not returning content types - * [THRIFT-1488] - src/concurrency/Thread.h:91:58: error: invalid conversion from 'pthread_t {aka _opaque_pthread_t*}' to 'apache::thrift::concurrency::Thread::id_t {aka long long unsigned int}' [-fpermissive] - * [THRIFT-1490] - Windows-specific header files - fixes & tweaks - * [THRIFT-1526] - Union TupleSchemeFactory returns StandardSchemes - * [THRIFT-1527] - Generated implementation of tupleReadStruct in unions return null when the setfield is unrecognized - * [THRIFT-1524] - TNonBlockingServer does not compile in Visual Studio 2010 - * [THRIFT-1529] - TupleProtocol can unintentionally include an extra byte in bit vectors when number of optional fields is an integral of 8 - * [THRIFT-1473] - JSON context stack may be left in an incorrect state when an exception is thrown during read or write operations - * [THRIFT-1456] - System.Net.HttpWebRequest' does not contain a definition for 'Proxy' - * [THRIFT-1468] - Memory leak in TSaslServerTransport - * [THRIFT-1461] - Recent TNonblockingServer changes broke --enable-boostthreads=yes, Windows - * [THRIFT-1460] - why not add unicode strings support to python directly? - * [THRIFT-1464] - AbstractNonblockingServer.FrameBuffer TNonblockingTransport accessor changed from public to private - * [THRIFT-1467] - Possible AV with empty strings when using JSON protocol - * [THRIFT-1523] - clientTimeout not worked as expected in TServerSocket created by TSSLTransportFactory - * [THRIFT-1537] - TFramedTransport issues - * [THRIFT-1519] - Thirft Build Failure referencing rb_intern2 symbol - * [THRIFT-1518] - Generated C++ code only sends the first optional field in the write() function for a struct. - * [THRIFT-1515] - NameError: global name 'TApplicationException' is not defined - * [THRIFT-1554] - Inherited service methods are not resolved in derived service implementations - * [THRIFT-1553] - thrift nodejs service side can't read map structure, key as enum, value as Object - * [THRIFT-1575] - Typo in server/TThreadPoolServer.h - * [THRIFT-1327] - Fix Spec Suite under Ruby-1.8.7 (works for MRI Ruby-1.9.2) - * [THRIFT-1326] - on some platforms, #include is necessary to be included in Thrift.h - * [THRIFT-1159] - THttpClient->Flush() issue (connection thru proxy) - * [THRIFT-1277] - Node.js serializes false booleans as null - * [THRIFT-1224] - Cannot insert UTF-8 text - * [THRIFT-1267] - Node.js can't throw exceptions. - * [THRIFT-1338] - Do not use an unpatched autoconf 2.65 to generate release tarball - * [THRIFT-1128] - MAC OS X: thrift.h incompatibility with Thrift.h - * [THRIFT-1631] - Fix C++ server constructor typos - * [THRIFT-1602] - PHP C Extension is not Compatible with PHP 5.4 - * [THRIFT-1610] - IWebProxy not available on WP7 platform - * [THRIFT-1606] - Race condition in BoostThreadFactory.cpp - * [THRIFT-1604] - Python exception handeling for changes from PEP 3110 - * [THRIFT-1607] - Incorrect file modes for several source files - * [THRIFT-1583] - c_glib leaks memory - * [THRIFT-1582] - Bad includes of nested thrift files in c_glib - * [THRIFT-1578] - C_GLib generated code does not compile - * [THRIFT-1597] - TJSONProtocol.php is missing from Makefile.am - * [THRIFT-1591] - Enable TCP_NODELAY for ruby gem - * [THRIFT-1624] - Isset Generated differently on different platforms - * [THRIFT-1622] - Incorrect size returned on read - * [THRIFT-1621] - Memory leaks - * [THRIFT-1612] - Base64 encoding is broken - * [THRIFT-1627] - compiler built using compilers.vcxproj cannot be used to build some test .thrift files - * [THRIFT-1571] - Update Ruby HTTP transport for recent Ruby versions - * [THRIFT-1023] - Thrift encoding (UTF-8) issue with Ruby 1.9.2 - * [THRIFT-1090] - Document the generation of a file called "Constants.java" - * [THRIFT-1082] - Thrift::FramedTransport sometimes calls close() on an undefined value - * [THRIFT-956] - Python module's version meta-data should be updated - * [THRIFT-973] - Cocoa library won't compile using clang - * [THRIFT-1632] - ruby: data corruption in thrift_native implementation of MemoryBufferTransport - * [THRIFT-1665] - TBinaryProtocol: exceeded message length raises generic TException - * [THRIFT-1664] - Reference to non-existing variable in build script - * [THRIFT-1663] - Java Thrift server is not throwing exceptions - * [THRIFT-1662] - "removeObject:" should be "removeObserver:" in [-TSocketServer dealloc]? - * [THRIFT-1643] - Denial of Service attack in TBinaryProtocol.readString - * [THRIFT-1674] - Update Thrift D library to be compatible with 2.060 - * [THRIFT-1673] - Ruby compile flags for extension for multi arch builds (os x) - * [THRIFT-1655] - Configure still trying to use thrift_generators in output - * [THRIFT-1654] - c_glib thrift_socket_read() returns corrupted data - * [THRIFT-1653] - TThreadedSelectorServer leaks CLOSE_WAIT sockets - * [THRIFT-1658] - Java thrift server is not throwing TApplicationException - * [THRIFT-1656] - Setting proper headers in THttpServer.cpp so that "Cross-Origin Resource Sharing" on js client can work. - * [THRIFT-1652] - TSaslTransport does not log the error when kerberos auth fails - * [THRIFT-2272] - CLONE - Denial of Service attack in TBinaryProtocol.readString - * [THRIFT-2086] - Invalid generated code for Node.JS when using namespaces - * [THRIFT-1686] - t_php_generator.cc uses "and" instead of "&&", and causes compiler errors with Visual Studio - * [THRIFT-1693] - libthrift has dependency on two different versions of httpcore - * [THRIFT-1689] - don't exit(-1) in TNonblockingServer - * [THRIFT-1679] - NodeJS: protocol readString() should treat string as utf8, not binary - * [THRIFT-1721] - Dist broken due to 0.8.0 to 0.9.0 changes - * [THRIFT-1710] - Minor issues in test case code - * [THRIFT-1709] - Warning "Bitwise-or operator used on a sign-extended operand; consider casting to a smaller unsigned type first" in TBinaryProtocol.cs at ReadInt64() - * [THRIFT-1707] - [ruby] Adjust server_spec.rb for RSpec 2.11.x and Ruby 1.9.3 - * [THRIFT-1671] - Cocoa code generator does not put keywords into generated method calls - * [THRIFT-1670] - Incompatibilities between different versions of a Thrift interface - * [THRIFT-1669] - NameError: global name 'TApplicationException' is not defined - * [THRIFT-1668] - Compile error in contrib/fb303, thrift/TDispatchProcessor.h: No such file or directory - * [THRIFT-1845] - Fix compiler warning caused by implicit string conversion with Xcode 4.6 - * [THRIFT-304] - Building the Python library requires development headers - * [THRIFT-369] - sets and maps break equality - * [THRIFT-556] - Ruby compiler does not correctly referred to top-level modules when a submodule masks the top-level name - * [THRIFT-481] - indentation of ruby classes is off by a few - -## Improvement - * [THRIFT-1498] - Allow TThreadedPoolServer.Args to pass a ExecutorService - * [THRIFT-1444] - FunctionRunner - add syntactic sugar to create shared_ptrs - * [THRIFT-1443] - define a TProcessor helper class to implement process() - * [THRIFT-1441] - Generate constructor with parameters for exception class to let it update message property automatically. - * [THRIFT-1520] - Embed version number in erlang .app file - * [THRIFT-1480] - python: remove tabs, adjust whitespace and address PEP8 warnings - * [THRIFT-1485] - Performance: pass large and/or refcounted arguments as "const" - * [THRIFT-1484] - Introduce phpunit test suite - * [THRIFT-1532] - The type specifications in the generated Erlang code should include "undefined" where it's used as a default value - * [THRIFT-1534] - Required fields in the Delphi code generator. - * [THRIFT-1469] - Java isset space optimization - * [THRIFT-1465] - Visibility of methods in generated java code - * [THRIFT-1453] - Don't change types of arguments when serializing with thrift php extension - * [THRIFT-1452] - generate a swap() method for all generated structs - * [THRIFT-1451] - FramedTransport: Prevent infinite loop when writing - * [THRIFT-1521] - Two patches for more Performance - * [THRIFT-1555] - Delphi version of the tutorial code - * [THRIFT-1535] - Why thrift don't use wrapped class for optional fields ? - * [THRIFT-1204] - Ruby autogenerated files should require 'thrift' gem - * [THRIFT-1344] - Using the httpc module directly rather than the deprecated http layer - * [THRIFT-1343] - no_auto_import min/2 to avoid compile warning - * [THRIFT-1340] - Add support of ARC to Objective-C - * [THRIFT-1611] - Improved code generation for typedefs - * [THRIFT-1593] - Pass on errors like "connection closed" to the handler module - * [THRIFT-1615] - PHP Namespace - * [THRIFT-1567] - Thrift/cpp: Allow alternate classes to be used for - * [THRIFT-1072] - Missing - (id) initWithSharedProcessor in TSharedProcessorFactory.h - * [THRIFT-1650] - [ruby] Update clean items and svn:ignore entries for OS X artifacts - * [THRIFT-1661] - [PATCH] Add --with-qt4 configure option - * [THRIFT-1675] - Do we have any plan to support scala? - * [THRIFT-1645] - Replace Object#tee with more conventional Object#tap in specs - * [THRIFT-1644] - Upgrade RSpec to 2.10.x and refactor specs as needed - * [THRIFT-1672] - MonoTouch (and Mono for Android) compatibility - * [THRIFT-1702] - a thrift manual - * [THRIFT-1694] - Re-Enable serialization for WP7 Silverlight - * [THRIFT-1691] - Serializer/deserializer support for Delphi - * [THRIFT-1688] - Update IDL page markup - * [THRIFT-1725] - Tutorial web pages for Delphi and C# - * [THRIFT-1714] - [ruby] Explicitly add CWD to Ruby test_suites.rb - * [THRIFT-317] - Issues with Java struct validation - * [THRIFT-164] - Build web tutorial on Incubator web site - * [THRIFT-541] - Cocoa code generator doesn't put keywords before all arguments. - * [THRIFT-681] - The HTML generator does not handle JavaDoc style comments very well - -## New Feature - * [THRIFT-1500] - D programming language support - * [THRIFT-1510] - There should be an implementation of the JsonProtocol for ruby - * [THRIFT-1115] - python TBase class for dynamic (de)serialization, and __slots__ option for memory savings - * [THRIFT-1953] - support for asp.net mvc 3 - -## Question - * [THRIFT-1235] - How could I use THttpServerTransportFactory withTNonBlockingServer - * [THRIFT-1368] - TNonblockingServer usage - * [THRIFT-1061] - Read an invalid frame size of 0. Are you using TFramedTransport on the client side? - * [THRIFT-491] - Ripping raw pthreads out of TFileTransport and associated test issues - -## Sub-task - * [THRIFT-1596] - Delphi: Test clients should have a return codes that reflect whether they succeeded or not - * [THRIFT-982] - javame: add version Info to the library - * [THRIFT-1722] - C# WP7 Assembly addition beaks mono build - * [THRIFT-336] - Compact Protocol in C# - -## Test - * [THRIFT-1613] - Add code back into empty source file ToStringTest.java - * [THRIFT-1718] - Incorrect check in TFileTransportTest - -## Wish - * [THRIFT-1463] - Decouple Thrift IDL from generators - * [THRIFT-1466] - Proper Documentation for Thrift C Glib - * [THRIFT-1539] - Build and distribute the fb303 python libraries along with thrift - * [THRIFT-1685] - Please add "aereo.com" to "Powered by Apache Thrift" list in about page - * [THRIFT-330] - TProcessor - additional method to called when connection is broken - - - -Thrift 0.8 --------------------------------------------------------------------------------- -## Bug - * [THRIFT-1436] - pip install thrift fails on Windows with "Unable to find vcvarsall.bat" - * [THRIFT-1432] - Javascript struct constants declared in the same file as their struct definition will cause an error - * [THRIFT-1428] - shared.thrft does not include namespace for php, so thrift compiler generate incorrect name - * [THRIFT-1426] - Dist package missing files for release 0.8 - * [THRIFT-1425] - The Node package is incompatible with latest node (0.6) & npm (1.0.27) - * [THRIFT-1416] - Python Unit test is broken on ci - * [THRIFT-1419] - AbstractNonBlockingServer does not catch errors when invoking the processor - * [THRIFT-1424] - Ruby specs fail when run with rake - * [THRIFT-1420] - Nonblocking and HsHa server should make sure to close all their socket connections when the selector exits - * [THRIFT-1413] - Generated code does not read MapEnd / ListEnd / SetEnd - * [THRIFT-1409] - Name conflict check does not work properly for exception object(Delphi). - * [THRIFT-1408] - Delphi Test Server: Exception test case fails due to naming conflict with e.message - * [THRIFT-1407] - Typo in Python socket server causes Thrift to fail when we enable a global socket timout - * [THRIFT-1397] - CI server fails during build due to unused parameters in delphi generator - * [THRIFT-1404] - Delphi compiler generates struct reader code with problem. - * [THRIFT-1400] - Ruby native extension aborts with __stack_chk_fail in OSX - * [THRIFT-1399] - One of the TServerImpl.Create CTORs lacks implementation - * [THRIFT-1390] - Debian packages build fix for Squeeze (build from the official 0.7.0 tarball) - * [THRIFT-1393] - TTransportException's thrown from THttpClient contain superfluous slashes in the Exception message - * [THRIFT-1392] - Enabling both namespaces and autoloading in generated PHP code won't work. - * [THRIFT-1406] - Build error after applying THRIFT-1395 - * [THRIFT-1405] - Delphi compiler does not generates container serializer properly. - * [THRIFT-1411] - java generator does not provide type parameter for TBaseProcessor - * [THRIFT-1473] - JSON context stack may be left in an incorrect state when an exception is thrown during read or write operations - * [THRIFT-1331] - Ruby library deserializes an empty map to nil - * [THRIFT-1330] - PHP Namespaces no longer generated - * [THRIFT-1328] - TBaseHelper.toString(...) appends ByteBuffer data outside of valid buffer range - * [THRIFT-1322] - OCaml lib fail to compile: Thrift.ml line 305, int vs int32 mismatch - * [THRIFT-1143] - Build doesn't detect correct architecture type on 64bit osx - * [THRIFT-1205] - port server unduly fragile with arbitrary input - * [THRIFT-1279] - type set is handled incorrectly when writing object - * [THRIFT-1298] - Standard scheme doesn't read or write metadata along with field values - * [THRIFT-1265] - C++ container deserialize - * [THRIFT-1263] - publish ruby client to rubygems - * [THRIFT-1384] - Java help menu missing newline near javame flag - * [THRIFT-1382] - Bundle install doesnot work because thrift crashes - * [THRIFT-1381] - Thrift C++ libs have incorrectly versioned names - * [THRIFT-1350] - Go library code does not build as of r60 (most recent release) - * [THRIFT-1365] - TupleProtocol#writeBitSet unintentionally writes a variable length byte array - * [THRIFT-1359] - --gen-cob cpp:cob_style does not compile anymore - * [THRIFT-1319] - Mismatch between how a union reads and writes a container - * [THRIFT-1309] - libfb303-0.7.0.jar missing in maven repository - * [THRIFT-1238] - Thrift JS client cannot read map of structures - * [THRIFT-1254] - Code can't be compiled against a regular JRE: Object.clone() override has a different return type - * [THRIFT-1367] - Mac OSX build fails with "no such file to load -- spec/rake/spectask" - * [THRIFT-1355] - Running make in lib/rb doesn't build the native extensions - * [THRIFT-1370] - Debian packaging should Build-Depend on libglib2.0-dev - * [THRIFT-1342] - Compilation problem on Windows of fastbinary.c - * [THRIFT-1341] - TProtocol.h endian detection wrong with boost - * [THRIFT-1583] - c_glib leaks memory - * [THRIFT-1582] - Bad includes of nested thrift files in c_glib - * [THRIFT-1578] - C_GLib generated code does not compile - * [THRIFT-1027] - 'make -j 16' fails with "unterminated #ifdef" error - * [THRIFT-1121] - Java server performance regression in 0.6 - * [THRIFT-857] - tests run by "make install" fail if generators are disabled - * [THRIFT-380] - Use setuptools for python build - -## Dependency upgrade - * [THRIFT-1257] - thrift's dependency scope on javax.servlet:servlet-api should be 'provided' - -## Improvement - * [THRIFT-1445] - minor C++ generator variable cleanup - * [THRIFT-1435] - make TException.Message property conformant to the usual expectations - * [THRIFT-1431] - Rename 'sys' module to 'util' - * [THRIFT-1396] - Dephi generator has dependacy on boost 1.42 later. - * [THRIFT-1395] - Patch to prevent warnings for integer types in some cases - * [THRIFT-1275] - thrift: always prefix namespaces with " ::" - * [THRIFT-1274] - thrift: fail compilation if an unexpected token is - * [THRIFT-1271] - thrift: fix missing namespace in generated local - * [THRIFT-1270] - thrift: add --allow-neg-keys argument to allow - * [THRIFT-1345] - Allow building without tests - * [THRIFT-1286] - Modernize the Thrift Ruby Library Dev Environment - * [THRIFT-1284] - thrift: fix processor inheritance - * [THRIFT-1283] - thrift: wrap t_cpp_generator::generate_process_function() to 80 - * [THRIFT-1282] - Upgrade httpclient to 4.1.2 (from 4.0.1) - * [THRIFT-1281] - add @generated to the docblock - * [THRIFT-1280] - Thrift: Improve Monitor exception-free interfaces - * [THRIFT-1278] - javadoc warnings - compilation - * [THRIFT-1227] - Erlang implementation of thrift JSON protocol - * [THRIFT-1295] - Duplicate include in TSocket.cpp - * [THRIFT-1294] - thrift: fix log message typos in TSimpleServer - * [THRIFT-1293] - thrift: improve handling of exceptions thrown by - * [THRIFT-1292] - thrift: silence log spew from TThreadedServer - * [THRIFT-1288] - Allow typedefed exceptions in throws clauses - * [THRIFT-1290] - thrift: TNonblockingServer: clean up state in the - * [THRIFT-1287] - thrift: start refactoring some of the C++ processor - * [THRIFT-1289] - thrift: implement TNonblockingServer::stop() - * [THRIFT-1305] - thrift: make TConnection a private inner class of - * [THRIFT-1304] - TNonblockingServer: pass in the connection context to - * [THRIFT-1302] - thrift: raise an exception if send() times out in - * [THRIFT-1301] - thrift: consolidate common code in TNonblockingServer - * [THRIFT-1377] - abort PHP deserialization on unknown field type - * [THRIFT-1379] - fix uninitialized enum values in thrift C++ objects - * [THRIFT-1376] - Make port specification option in thrift remote - * [THRIFT-1375] - fixed a hex char conversion bug in TJSONProtocol - * [THRIFT-1373] - Fix user-defined exception generation in thrift (python) - * [THRIFT-1361] - Optional replacement of pthread by boost::thread - * [THRIFT-1320] - Consistency of configure generated config.h - * [THRIFT-1317] - Remove copy constructibility from - * [THRIFT-1316] - thrift: update server classes to accept - * [THRIFT-1315] - thrift: generate server interface factory classes - * [THRIFT-1314] - thrift: add TProcessorFactory - * [THRIFT-1335] - Add accept timeout to TServerSocket - * [THRIFT-1334] - Add more info to IllegalStateException - * [THRIFT-1333] - Make RWGuard not copyable - * [THRIFT-1332] - TSSLTransportParameters class uses hard coded value keyManagerType: SunX509 - * [THRIFT-1251] - Generated java code should indicate which fields are required and which are optional - * [THRIFT-1387] - Build MSVC libraries with Boost Threads instead of Pthreads - * [THRIFT-1339] - Extend Tuple Protocol to TUnions - * [THRIFT-1031] - Patch to compile Thrift for vc++ 9.0 and 10.0 - * [THRIFT-1130] - Add the ability to specify symbolic default value for optional boolean - * [THRIFT-1123] - Patch to compile Thrift server and client for vc++ 9.0 and 10.0 - * [THRIFT-386] - Make it possible to build the Python library without the extension - -## New Feature - * [THRIFT-1401] - JSON-protocol for Delphi XE Libraries - * [THRIFT-1167] - Java nonblocking server with more than one thread for select and handling IO - * [THRIFT-1366] - Delphi generator, lirbrary and unit test. - * [THRIFT-1354] - Add rake task to build just the gem file - * [THRIFT-769] - Pluggable Serializers - -## Sub-task - * [THRIFT-1415] - delphi: add version Info to the library - * [THRIFT-1391] - Improved Delphi XE test cases - - - -Thrift 0.7 --------------------------------------------------------------------------------- -## Bug - * [THRIFT-1140] - Framed Transport Client using C (Glib) Library hangs when connecting to Ruby Server - * [THRIFT-1154] - HttpClient does not specify the connection close parameter - * [THRIFT-1153] - HttpClient does not specify the connection close parameter - * [THRIFT-1149] - Nonblocking server fails when client connection is reset - * [THRIFT-1146] - Android Incompatibility : in Android < 2.3 java.io.IOException doesn't support for Throwable parameter in constructor - * [THRIFT-1133] - Java and JavaScript tutorial is broken since we have Java maven deployment - * [THRIFT-1132] - Deserialization error in TApplicationException C# - * [THRIFT-1131] - C# JSON Protocol is unable to decode escaped characters in string - * [THRIFT-1208] - python TCompactProtocol.py writeBool and readBool not follow the compact-proto-spec-2.txt spec for CONTAINER_WRITE, CONTAINER_READ - * [THRIFT-1200] - JS compiler generates code that clobbers existing namespaces - * [THRIFT-1183] - Pure-ruby CompactProtocol raises ArgumentError when deserializing under Ruby 1.9 - * [THRIFT-1182] - Native deserializer segfaults on incorrect list element type - * [THRIFT-1181] - AS3 compiler generates incorrect code for setting default values in constructor - * [THRIFT-1234] - thrift --help is missing doc on py:utf8strings - * [THRIFT-1180] - AS3 compiler generates uncompilable code for binary types. - * [THRIFT-1194] - Java lib does not install artifacts to local dir correctly - * [THRIFT-1193] - Potential infinite loop in nonblocking_server - * [THRIFT-1192] - Typo: TProtocol.h tests for HAVE_SYS_PARAM_H_ - * [THRIFT-1190] - readBufferBytesAllocated in TNonblockingServer.java should be AtomicLong to fix FD leakage and general server malfunction - * [THRIFT-1187] - nonblocking_server shutdown race under Ruby 1.9 - * [THRIFT-1178] - Java: TBase signature should be T extends TBase - * [THRIFT-1164] - Segmentation fault on NULL pointer in t_js_generator::generate_const - * [THRIFT-1171] - Perl write/readDouble assumes little-endian platform - * [THRIFT-1222] - Unhandled exception for TEvhttpServer request - * [THRIFT-1220] - TProcessor::process never returns false - * [THRIFT-1285] - Stable 0.7.0 Windows compiler exe available on the webside is not the good one - * [THRIFT-1218] - c_glib uses wrong name in pkg-config - * [THRIFT-1215] - Undefined property Thirft in lib/js/thrift.js - * [THRIFT-1211] - When using THttpClient, non 200 responses leave the connection open - * [THRIFT-1228] - The php accelerator module calls flush incorrectly - * [THRIFT-1308] - libfb303-0.7.0.jar missing in maven repository - * [THRIFT-1255] - Mismatch of method name between JavaME's lib and generated code (compareTo/compareObjects) - * [THRIFT-1253] - Code generated for maps is not compiling - * [THRIFT-1252] - Segfault in Ruby deserializer - * [THRIFT-1094] - bug in TCompactProto python readMessageEnd method and updated test cases - * [THRIFT-1093] - several bugs in python TCompactProtocol - * [THRIFT-1092] - generated validate() method has wrong indentation - * [THRIFT-1011] - Error generating package imports when using classes from other packages - * [THRIFT-1050] - Declaring an argument named "manager" to a service method produces code that fails compile due to name conflicts with protected ivars in TAsyncClient - * [THRIFT-1074] - .keystore and .truststore are missing from the 0.6.0 distribution - * [THRIFT-1067] - Tons of bugs in php implementation - * [THRIFT-1065] - Unexpected exceptions not proper handled on JS - * [THRIFT-1076] - Erlang Thrift socket server has a bug that causes java thrift client of framed binary client to throw "out of sequence" exception - * [THRIFT-1057] - casts in TBinaryProtocol.tcc causing "dereferencing type-punned pointer will break strict-aliasing rules" warnings from gcc - * [THRIFT-1055] - csharp TServerSocket and TSocket do not disable Nagle via Socket.NoDelay = true like cpp and java do - * [THRIFT-1054] - explicit call to PKG_PROG_PKG_CONFIG is missing and first use of PKG_CHECK_MODULES may not happen, causes mono detection to fail - * [THRIFT-1117] - JavaScript Unit Test does not work anymore because libthrift*.jar where moved by Maven Deployment - * [THRIFT-1111] - The HTML generator does not distinguish between string and binary types - * [THRIFT-1032] - "make dist" fails due to c_glib problem - * [THRIFT-1036] - Auto-generated C++ code fails to compile with "-Werror -Wextra -Wall" g++ compiler flags - * [THRIFT-1041] - TDeserializer holds onto a reference of the array it reads after it is done deserializing - * [THRIFT-1106] - C++ code TAsyncProtocolProcessor.h & TAsyncBufferProcessor.h dont have virtual functions but no virtual destructor. Causes warnings on -Wall - * [THRIFT-1105] - OCaml generator does not prefix methods of included structs with their type - * [THRIFT-1104] - INSTALLDIRS should be included in configure script - * [THRIFT-1102] - typo in configure.ac: "==" operator in 'test' (instead of"'=") - * [THRIFT-1101] - bytebuffer length calculation in TBinaryProtocol writeBinary - * [THRIFT-1098] - Undefined properties in TBinaryProtocolFactory - * [THRIFT-1081] - PHP tests broken and somewhat incomplete - * [THRIFT-1080] - erlang test's 'make' fails on Mac OSX - * [THRIFT-1078] - ThriftTest.thrift generates invalid PHP library - * [THRIFT-1120] - proto.WriteListEnd being called in the wrong place - * [THRIFT-1119] - TJSONProtocol fails to UTF8 decode strings - * [THRIFT-867] - PHP accelerator module's output transport is incompatible with TFramedTransport - * [THRIFT-826] - PHP TSocket Write Timeout - * [THRIFT-835] - Bad AS3 syntax in constructors that set default values - * [THRIFT-788] - thrift_protocol.so: multiget/multiget_slice does not handle more than 17 keys correctly - * [THRIFT-125] - OCaml libraries don't compile with 32-bit ocaml - * [THRIFT-342] - PHP: can't have sets of complex types - * [THRIFT-731] - configure doesn't check for ant >= 1.7 - * [THRIFT-690] - Update TApplicationException codes - * [THRIFT-638] - BufferedTransport + C extensions block until recv timeout is reached on last fread call - -## Dependency upgrade - * [THRIFT-1177] - Update thrift to reflect changes in Go's networking libraries - -## Improvement - * [THRIFT-1155] - Remove log4j dependency from java client - * [THRIFT-1151] - Produce more informative runtime error in case of schema and data mismatch during serialization - * [THRIFT-1207] - Support DESTDIR on "make install" of ruby libs - * [THRIFT-1199] - Union structs should have generated methods to test whether a specific field is currently set - * [THRIFT-1233] - Remove unused include in generated C++ code - * [THRIFT-1189] - Ruby deserializer speed improvements - * [THRIFT-1170] - Thrift Generated Code and Java 5 - * [THRIFT-1174] - Publish as3 client implementation via Maven for use by flex-mojos users - * [THRIFT-1225] - TCompactProtocol for PHP - * [THRIFT-1221] - Remove SimpleCallback.h - * [THRIFT-1217] - Use evutil_socketpair instead of pipe (Windows port) - * [THRIFT-1216] - build Java Library behind a proxy - * [THRIFT-1231] - Remove bogus include - * [THRIFT-1213] - Membuffer should provide a way to get back the buffer - * [THRIFT-1237] - Java fb303 missing some methods - * [THRIFT-1063] - Fix Erlang Tutorial Files - * [THRIFT-1053] - Make remote client's IP address available for all socket related transports - * [THRIFT-1109] - Deploy fb303 along side libthrift to maven repo - * [THRIFT-1107] - improvement for compiler-generated python for 'None' object comparisons - * [THRIFT-1069] - Add command line option to prevent thrift from inserting gen-* directories - * [THRIFT-1049] - Allow for TServerSocket python library to bind to a specific host - * [THRIFT-1126] - Extending struct_info for erlang bindings - * [THRIFT-1100] - python TSSLSocket improvements, including certificate validation - * [THRIFT-994] - Don't try to invoke phpize if we don't have it - * [THRIFT-993] - Some improvements in C++ stubs for oneway operations - * [THRIFT-997] - Using valueOf for base types in getFieldValue - * [THRIFT-418] - Don't do runtime sorting of struct fields - * [THRIFT-151] - TSSLServerSocket and TSSLSocket implementation - * [THRIFT-27] - Generated erlang types don't contain default values for records - * [THRIFT-113] - to-string methods should omit optional null fields from output - * [THRIFT-363] - Maven Deploy - * [THRIFT-447] - Make an abstract base Client class so we can generate less code - * [THRIFT-627] - should c++ have setters for optional fields? - -## New Feature - * [THRIFT-1236] - Erlang Reconnecting Thrift Client - * [THRIFT-1021] - Framed transport support for OCaml - * [THRIFT-1068] - Python SSL Socket Support - * [THRIFT-1103] - TZlibTransport for python, a zlib compressed transport - * [THRIFT-1083] - Preforking python process pool server - * [THRIFT-999] - Add TForkingServer - -## Sub-task - * [THRIFT-1152] - Attributes from private to protected - * [THRIFT-1038] - Generated Java code for structures containing binary fields (or collections thereof) are not serializable (in the Java sense) even though they implement java.io.Serializable - -## Task - * [THRIFT-892] - Refactor erlang build system with rebar - -## Wish - * [THRIFT-625] - Add support for 'Go' - - - -Thrift 0.6.1 --------------------------------------------------------------------------------- -## Bug - * [THRIFT-1133] - Java and JavaScript tutorial is broken since we have Java maven deployment - * [THRIFT-1131] - C# JSON Protocol is unable to decode escaped characters in string - * [THRIFT-1074] - .keystore and .truststore are missing from the 0.6.0 distribution - -## Improvement - * [THRIFT-1109] - Deploy fb303 along side libthrift to maven repo - * [THRIFT-363] - Maven Deploy - -## Question - * [THRIFT-1206] - did the THRIFT 0.6.1 merge THRIFT-563 ? - -## Sub-task - * [THRIFT-1163] - How can i use multi service in one program? - -## Task - * [THRIFT-1112] - Apply THRIFT-363 to 0.6 branch - * [THRIFT-1113] - Apply THRIFT-1074 to 0.6 branch - - - -Thrift 0.6 --------------------------------------------------------------------------------- -## Bug - * [THRIFT-1020] - OCaml compiler generates invalid OCaml - * [THRIFT-1015] - TUnion does not handle ByteBuffer in toString - * [THRIFT-1013] - generated java code may have name clashes with thrift library - * [THRIFT-1009] - TUnion does not correctly deep copy a ByteBuffer - * [THRIFT-1032] - "make dist" fails due to c_glib problem - * [THRIFT-868] - Referencing constant values doesn't work with with typedef types - * [THRIFT-971] - java module can't be compiled without ivy and network connection - * [THRIFT-970] - Under heavy load, THttpClient may fail with "too many open files" - * [THRIFT-969] - Java Tutorial broken, move CalculatorHandler to a separate file - * [THRIFT-807] - JavaScript: Initialization of Base Types with 0 instead of null - * [THRIFT-955] - Thrift compiler for Windows uses lowercase names and directories which is inconsistent with compiling on other platforms - * [THRIFT-992] - Naming convention in C# constructor is not consistent with other fields causes compile errors - * [THRIFT-1008] - byte[] accessors throw NPE on unset field - * [THRIFT-1006] - Impossible to correctly qualify an enum constant in an external thrift file - * [THRIFT-950] - Haskell bindings treat 'byte' as unsigned 8-bit int (Data.Word.Word8), java/cpp as signed (byte/int8_t). - * [THRIFT-975] - lib/c_glib/README is missing => breaks make dist - * [THRIFT-944] - Support all version-4s of base - * [THRIFT-939] - optional binary fields throw NPE on default byte[] getters - * [THRIFT-935] - PHP Extension aborts the build if php-config is not installed - * [THRIFT-933] - Haskell's Thrift.cabal has warnings - * [THRIFT-932] - Haskell tests need to be run through 'make check' (and probably 'cabal check') too - * [THRIFT-904] - C# TSocket should disable nagle and linger - * [THRIFT-941] - Make PHP C Extension use the defined Protocol writeMessageBegin function - * [THRIFT-940] - 'make check' fails if boost is not in the std include and link paths - * [THRIFT-924] - Fix generated php structure constants - * [THRIFT-979] - ruby bindings used to work on jruby - * [THRIFT-977] - Hex Conversion Bug in C++ TJSONProtocol - * [THRIFT-347] - PHP TSocket Timeout Issues - * [THRIFT-517] - TExceptions thrown by server result in cryptic error message on client - Tried to read 4 bytes, but only got 0 bytes - -## Improvement - * [THRIFT-1024] - Add Python Twisted example to the Tutorial - * [THRIFT-958] - Change accessmodifer on trans_ field in the FrameBuffer class to public. - * [THRIFT-957] - THsHaServer: Change access modifier of the invoker field. - * [THRIFT-1002] - CodeStyle: t_c_glib_generator.cc - * [THRIFT-1005] - Give unions byte[] signature methods to go along with their ByteBuffer counterparts - * [THRIFT-951] - Add a new isServing() method to TServer - * [THRIFT-943] - Silly readme typo fix. - * [THRIFT-961] - JavaScript TestSuite using ant/ivy and Java's ServerTestBase Handler - * [THRIFT-960] - add TestServer, TestNonblockingServer and TestClient again - * [THRIFT-949] - Modify the TEnum interface so it defines a method similar to findByValue - * [THRIFT-946] - Augment FieldValueMetaData so it differentiates 'string' and 'binary' fields. - * [THRIFT-903] - custom ThreadFactory in THsHaServer - * [THRIFT-913] - Test Case for Url encoded strings + simple enhancement to lib/js/test/RunTestServer.sh - * [THRIFT-926] - Miscellaneous C++ improvements - * [THRIFT-929] - Improvements to the C++ test suite - * [THRIFT-893] - add JavaScript to the tutorial examples - * [THRIFT-1003] - Polishing c_glib code - * [THRIFT-71] - Debian packaging for thrift - -## New Feature - * [THRIFT-1033] - Node.js language target - * [THRIFT-947] - Provide a helper method to determine the TProtocol used to serialize some data. - * [THRIFT-928] - Make more statistics available in C++ servers - * [THRIFT-922] - Templatized [de]serialization code for C++ - * [THRIFT-923] - Event-driven client and server support for C++ - * [THRIFT-925] - Provide name<->value map for enums in C++ - * [THRIFT-927] - Add option to modify the PHP include path - * [THRIFT-377] - TFileTransport port in Java - * [THRIFT-106] - TSSLServerSocket - * [THRIFT-582] - C implementation of Thrift - * [THRIFT-745] - Make it easier to instantiate servers - -## Sub-task - * [THRIFT-1038] - Generated Java code for structures containing binary fields (or collections thereof) are not serializable (in the Java sense) even though they implement java.io.Serializable - -## Task - * [THRIFT-862] - Async client issues / improvements - -## Test - * [THRIFT-581] - Add a testsuite for txThrift (Twisted) - - - -Thrift 0.5.0 - Incubating --------------------------------------------------------------------------------- -THRIFT-505 Build Make configure give a summary of the enabled components (David Reiss) -THRIFT-506 Build Allow Thrift to be built without the C++ library (David Reiss) -THRIFT-844 Build Build Requirements state autoconf 2.59+ is required, but 2.60+ is needed (Harlan Lieberman-Berg) -THRIFT-850 Build Perl runtime requires Bit::Vector which may not be installed by default, but configure does not fail (Michael Lum) -THRIFT-854 Build Provide configure option and make rules to build/install php extension (Anthony Molinaro) -THRIFT-858 Build Have bootstrap.sh check for a suitable autoconf version before running (David Reiss) -THRIFT-871 Build Thrift compiler for WIndows (binary distribution) (David Reiss) -THRIFT-323 C# TJSONProtocol (Roger Meier) -THRIFT-634 C# C# Compiler Generates Incorrect Code For Fields which begin with an uppercase letter (Jon S Akhtar) -THRIFT-881 C# add csharp to the tutorial (Roger Meier) -THRIFT-856 C++ Building cpp library fails on OS X with malloc and free not being declared in scope (James Clarke) -THRIFT-865 C++ C++ compiler build depends on libfl even when flex/lex not detected (David Reiss) -THRIFT-900 C++ Unix domain socket (Roger Meier) -THRIFT-920 C++ C++ Test and Tutorial does not compile anymore due to the change within Enum handling (Roger Meier) -THRIFT-567 C++ Can't immediately stop a TSimpleServer thread that is idle (Rush Manbert) -THRIFT-756 C++ Exposing TSocket(int) constructor to public (Rajat Goel) -THRIFT-798 C++ TNonblockingServer leaks resources when destroyed (David Reiss) -THRIFT-812 C++, Python Demo of Thrift over ZeroMQ (David Reiss) -THRIFT-629 Cocoa Unused Field In TSocketServer Appears To Break iPhone Build (Jon S Akhtar) -THRIFT-838 Cocoa Generated Cocoa classes have useless @dynamic declarations (Kevin Ballard) -THRIFT-805 Cocoa Don't generate process_XXXX methods for oneway methods (Brad Taylor) -THRIFT-507 Compiler Remove the compiler's dependency on Boost (David Reiss) -THRIFT-895 Compiler (General) Thrift compiler does not allow two different enumerations to have the same key name for one of the enum values (David Reiss) -THRIFT-852 Compiler (General) Missing newline causes many compiler warnings (Anthony Molinaro) -THRIFT-877 Compiler (General) smalltalk namespace doesn't work (Bruce Lowekamp) -THRIFT-897 Compiler (General) Don't allow unqualified constant access to enum values (Bryan Duxbury) -THRIFT-9 Compiler (General) Add a default namespace declaration for all languages (David Reiss) -THRIFT-599 Erlang Don't use unnecessary processes in the Erlang transports and clients (David Reiss) -THRIFT-646 Erlang Erlang library is missing install target (David Reiss) -THRIFT-698 Erlang Generated module list should contain atoms, not strings (Anthony Molinaro) -THRIFT-866 Erlang term() in spec definitions seems to not work in erlang R12 (Anthony Molinaro) -THRIFT-886 Erlang Dialyzer warning (Anthony Molinaro) -THRIFT-785 Erlang Framed transport server problems (Anthony Molinaro) -THRIFT-884 HTML HTML Generator: add Key attribute to the Data Types Tables (Roger Meier) -THRIFT-652 Haskell Generated field name for strut is not capitalized correctly (Christian Lavoie) -THRIFT-743 Haskell compile error with GHC 6.12.1 (Christian Lavoie) -THRIFT-901 Haskell Allow the bindings to compile without -fglasgow-exts and with -Wall -Werror (Christian Lavoie) -THRIFT-905 Haskell Make haskell thrift bindings use automake to compile and install (Christian Lavoie) -THRIFT-906 Haskell Improve type mappings (Christian Lavoie) -THRIFT-914 Haskell Make haskell bindings 'easily' compilable (Christian Lavoie) -THRIFT-918 Haskell Make haskell tests run again (Christian Lavoie) -THRIFT-919 Haskell Update Haskell bindings README (Christian Lavoie) -THRIFT-787 Haskell Enums are not read correctly (Christian Lavoie) -THRIFT-250 Java ExecutorService as a constructor parameter for TServer (Ed Ceaser) -THRIFT-693 Java Thrift compiler generated java code that throws compiler warnings about deprecated methods. (Bryan Duxbury) -THRIFT-843 Java TNonblockingSocket connects without a timeout (Bryan Duxbury) -THRIFT-845 Java async client does not respect timeout (Ning Liang) -THRIFT-870 Java Java constants don't get Javadoc comments (Bryan Duxbury) -THRIFT-873 Java Java tests fail due to Too many open files (Todd Lipcon) -THRIFT-876 Java Add SASL support (Aaron T. Myers) -THRIFT-879 Java Remove @Override from TUnion.clear (Dave Engberg) -THRIFT-882 Java deep copy of binary fields does not copy ByteBuffer characteristics (arrayOffset, position) (Bryan Duxbury) -THRIFT-888 Java async client should also have nonblocking connect (Eric Jensen) -THRIFT-890 Java Java tutorial doesn't work (Todd Lipcon) -THRIFT-894 Java Make default accessors for binary fields return byte[]; provide new accessors to get ByteBuffer version (Bryan Duxbury) -THRIFT-896 Java TNonblockingSocket.isOpen() returns true even after close() (Eric Jensen) -THRIFT-907 Java libfb303 doesn't compile in 0.4.0 (Todd Lipcon) -THRIFT-912 Java Improvements and bug fixes to SASL implementation (Todd Lipcon) -THRIFT-917 Java THsHaServer should not accept an ExecutorService without catching RejectedExecutionException (Ed Ceaser) -THRIFT-931 Java Use log4j for Java tests (Todd Lipcon) -THRIFT-880 JavaME JavaME code generator and runtime library (Dave Engberg) -THRIFT-846 JavaScript JavaScript Test Framwork: extended Testcases (Roger Meier) -THRIFT-885 JavaScript Url encoded strings never get decoded? How do we fix this? (T Jake Luciani) -THRIFT-911 JavaScript (JavaScript compiler) Const structs, maps, sets, and lists generate a trailing comma (T Jake Luciani) -THRIFT-860 OCaml copy method and reset method (Lev Walkin) -THRIFT-682 PHP PHP extension doesn't compile on Mac OS X (Bryan Duxbury) -THRIFT-851 PHP php extension fails to compile on centos 5.x (Todd Lipcon) -THRIFT-840 Perl Perl protocol handler could be more robust against unrecognised types (Conrad Hughes) -THRIFT-758 Perl incorrect deference in exception handling (Yann Kerherve) -THRIFT-257 Python Support validation of required fields (Esteve Fernandez) -THRIFT-335 Python Compact Protocol for Python (David Reiss) -THRIFT-596 Python Make Python's TBufferedTransport use a configurable input buffer (David Reiss) -THRIFT-597 Python Python THttpServer performance improvements (David Reiss) -THRIFT-598 Python Allow Python's threading servers to use daemon threads (David Reiss) -THRIFT-666 Python Allow the handler to override HTTP responses in THttpServer (David Reiss) -THRIFT-673 Python Generated Python code has whitespace issues (Ian Eure) -THRIFT-721 Python THttpClient ignores url parameters (Thomas Kho) -THRIFT-824 Python TApplicationException.__str__() refers to class constants as globals (Peter Schuller) -THRIFT-855 Python Include optimized compiled python objects in install (Anthony Molinaro) -THRIFT-859 Python Allow py:twisted to be generated in different namespace than py (Bruce Lowekamp) -THRIFT-869 Python TSocket.py on Mac (and FreeBSD) doesn't handle ECONNRESET from recv() (Steven Knight) -THRIFT-875 Python Include python setup.cfg in dist (Anthony Molinaro) -THRIFT-610 Ruby binary_protocol.rb segfaults [line 86] (Unassigned) -THRIFT-899 Ruby Ruby read timeouts can sometimes be 2x what they should be (Ryan King) -THRIFT-909 Ruby allow block argument to struct constructor (Michael Stockton) -THRIFT-456 Test Suite Bad IP address string in test/cpp/src/main.cpp (Rush Manbert) - - -Thrift 0.4.0 - Incubating --------------------------------------------------------------------------------- -THRIFT-650 Build Make Check fails on Centos/OSX with 0.2.0 tarball (Anthony Molinaro) -THRIFT-770 Build Get 'make dist' to work without first compiling source code (Anthony Molinaro) -THRIFT-160 C# Created THttpTransport for the C# library based on WebHttpRequest (Michael Greene) -THRIFT-834 C# THttpClient resends contents of message after transport errors (Anatoly Fayngelerin) -THRIFT-247 C++ THttpServer Transport (Unassigned) -THRIFT-676 C++ Change C++ code generator so that generated classes can be wrapped with SWIG (Unassigned) -THRIFT-570 Compiler Thrift compiler does not error when duplicate method names are present (Bruce Simpson) -THRIFT-808 Compiler Segfault when constant declaration references a struct field that doesn't exist (Bryan Duxbury) -THRIFT-646 Erlang Erlang library is missing install target (Anthony Molinaro) -THRIFT-544 General multiple enums with the same key generate invalid code (Ben Taitelbaum) -THRIFT-434 General ruby compiler should warn when a reserved word is used (Michael Stockton) -THRIFT-799 General Files missing proper Apache license header (Bryan Duxbury) -THRIFT-832 HTML HTML generator shows unspecified struct fields as 'required' (Bryan Duxbury) -THRIFT-226 Java Collections with binary keys or values break equals() (Bryan Duxbury) -THRIFT-484 Java Ability to use a slice of a buffer instead of a direct byte[] for binary fields (Bryan Duxbury) -THRIFT-714 Java maxWorkerThreads parameter to THsHaServer has no effect (Bryan Duxbury) -THRIFT-751 Java Add clear() method to TBase (Bryan Duxbury) -THRIFT-765 Java Improved string encoding and decoding performance (Bryan Duxbury) -THRIFT-768 Java Async client for Java (Bryan Duxbury) -THRIFT-774 Java TDeserializer should provide a partialDeserialize method for primitive types (Piotr Kozikowski) -THRIFT-783 Java .equals java method is broken on structs containing binary-type fields (Unassigned) -THRIFT-804 Java CompareTo is broken for unions set to map, set, or list (Bryan Duxbury) -THRIFT-814 Java Include a TServlet in the standard Thrift distribution (Mathias Herberts) -THRIFT-818 Java Async client doesn't send method args (Bryan Duxbury) -THRIFT-830 Java Switch binary field implementation from byte[] to ByteBuffer (Bryan Duxbury) -THRIFT-831 Java FramedTransport implementation that reuses its buffers (Bryan Duxbury) -THRIFT-833 Java build.xml in lib/java is missing a classpathref attribute for the javadoc task (Bryan Duxbury) -THRIFT-836 Java Race condition causes CancelledKeyException in TAsyncClientManager (Bryan Duxbury) -THRIFT-842 Java Upgrade to current version of commons-lang (2.5 instead of 2.4) and/or change dependency in ivy.xml to not be exact (Bryan Duxbury) -THRIFT-815 JavaScript Deserialization of lists is critically broken. (T Jake Luciani) -THRIFT-827 OCaml OCaml generator to take default values into account (Lev Walkin) -THRIFT-647 PHP PHP library is missing install target (Anthony Molinaro) -THRIFT-682 PHP PHP extension doesn't compile on Mac OS X (Bryan Duxbury) -THRIFT-718 PHP Thrift PHP library includes closing tags and extraneous whitespace (Nicholas Telford) -THRIFT-778 PHP PHP socket listening server (Nick Jones) -THRIFT-780 PHP PHP extension sometimes causes an abort with two exceptions at the same time (David Reiss) -THRIFT-837 PHP PHP accelerator bug for writes > 8k (Thomas Kho) -THRIFT-782 Perl Perl code for writing containers doesn't count length of write*Begin or write*End (Conrad Hughes) -THRIFT-395 Python Python library + compiler does not support unicode strings (Unassigned) -THRIFT-133 Ruby 'namespace ruby' should error out, or be an alias to 'namespace rb' (Bryan Duxbury) -THRIFT-664 Ruby Ruby extension fails to build with Ruby 1.9.1 (Rajesh Malepati) -THRIFT-699 Ruby Excise unused "native protocol method table" stuff from thrift_native (Bryan Duxbury) -THRIFT-767 Ruby ruby compiler does not keep comments for enum values (Bryan Duxbury) -THRIFT-811 Ruby http_client_transport.rb: allow custom http headers (Tony Kamenick) -THRIFT-459 Ruby Ruby installation always tries to write to /Library/Ruby/site (Matthieu Imbert) - - -Thrift 0.1.0 - Incubating (not released) --------------------------------------------------------------------------------- -Compatibility Breaking Changes: - C++: - * It's quite possible that regenerating code and rebuilding will be - required. Make sure your headers match your libs! - - Java: - - Python: - - Ruby: - * Generated files now have underscored names [THRIFT-421] - * The library has been rearranged to be more Ruby-like [THRIFT-276] - - Erlang: - * Generated code will have to be regenerated, and the new code will - have to be deployed atomically with the new library code [THRIFT-136] - -New Features and Bug Fixes: - C++: - * Support for TCompactProtocol [THRIFT-333] - - Java: - * Support for TCompactProtocol [THRIFT-110] - - Python: - * Support for Twisted [THRIFT-148] - - Ruby: - * Support for TCompactProtocol [THRIFT-332] - diff --git a/CHANGES.md b/CHANGES.md new file mode 100644 index 00000000000..eddc05fd90e --- /dev/null +++ b/CHANGES.md @@ -0,0 +1,3543 @@ +# Apache Thrift Changelog + +## 0.14.0 + +### Deprecated Languages + +- [THRIFT-5229](https://issues.apache.org/jira/browse/THRIFT-5229) - Deprecate ActionScript 3 support + +### Removed Languages + +- [THRIFT-4980](https://issues.apache.org/jira/browse/THRIFT-4980) - Remove deprecated C# and netcore bindings from the code base +- [THRIFT-4981](https://issues.apache.org/jira/browse/THRIFT-4981) - Remove deprecated netcore bindings from the code base +- [THRIFT-4982](https://issues.apache.org/jira/browse/THRIFT-4982) - Remove deprecated C# bindings from the code base + +### Breaking Changes + +- [THRIFT-4981](https://issues.apache.org/jira/browse/THRIFT-4981) - Remove deprecated netcore bindings from the code base +- [THRIFT-4982](https://issues.apache.org/jira/browse/THRIFT-4982) - Remove deprecated csharp bindings from the code base +- [THRIFT-4990](https://issues.apache.org/jira/browse/THRIFT-4990) - Upgrade to .NET Core 3.1 (LTS) +- [THRIFT-5006](https://issues.apache.org/jira/browse/THRIFT-5006) - Implement DEFAULT_MAX_LENGTH at TFramedTransport +- [THRIFT-5069](https://issues.apache.org/jira/browse/THRIFT-5069) - In Go library TDeserializer.Transport is now typed \*TMemoryBuffer instead of TTransport +- [THRIFT-5072](https://issues.apache.org/jira/browse/THRIFT-5072) - Haskell generator fails to distinguish between multiple enum types with conflicting enum identifiers +- [THRIFT-5116](https://issues.apache.org/jira/browse/THRIFT-5116) - Upgrade NodeJS to 10.x +- [THRIFT-5138](https://issues.apache.org/jira/browse/THRIFT-5138) - Swift generator does not escape keywords properly +- [THRIFT-5164](https://issues.apache.org/jira/browse/THRIFT-5164) - In Go library TProcessor interface now includes ProcessorMap and AddToProcessorMap functions. +- [THRIFT-5186](https://issues.apache.org/jira/browse/THRIFT-5186) - cpp: use all getaddrinfo() results when retrying failed bind() in T{Nonblocking,}ServerSocket +- [THRIFT-5233](https://issues.apache.org/jira/browse/THRIFT-5233) - go: Now all Read*, Write* and Skip functions in TProtocol accept context arg +- [THRIFT-5152](https://issues.apache.org/jira/browse/THRIFT-5152) - go: TSocket and TSSLSocket now have separated connect timeout and socket timeout +- c++: dropped support for Windows XP +- [THRIFT-5326](https://issues.apache.org/jira/browse/THRIFT-5326) - go: TException interface now has a new function: TExceptionType +- [THRIFT-4914](https://issues.apache.org/jira/browse/THRIFT-4914) - go: TClient.Call now returns ResponseMeta in addition to error + +### Known Open Issues (Blocker or Critical) + +- [THRIFT-3877](https://issues.apache.org/jira/browse/THRIFT-3877) - C++: library don't work with HTTP (csharp server, cpp client; need cross test enhancement) +- [THRIFT-5098](https://issues.apache.org/jira/browse/THRIFT-5098) - Deprecated: "The high level Network interface is no longer supported. Please use Network.Socket." and other Haskell issues +- [THRIFT-5245](https://issues.apache.org/jira/browse/THRIFT-5245) - NPE when the value of map's key is null +- [THRIFT-4687](https://issues.apache.org/jira/browse/THRIFT-4687) - Add thrift 0.12.0 to pypi and/or enable more maintainers + +### Build Process + +- [THRIFT-4976](https://issues.apache.org/jira/browse/THRIFT-4976) - Docker build: Test failure for `StalenessCheckTest` on MacOS +- [THRIFT-5087](https://issues.apache.org/jira/browse/THRIFT-5087) - test/test.py fails with "AssertionError: Python 3.3 or later is required for proper operation." +- [THRIFT-5097](https://issues.apache.org/jira/browse/THRIFT-5097) - Incorrect THRIFT_VERSION in ThriftConfig.cmake +- [THRIFT-5109](https://issues.apache.org/jira/browse/THRIFT-5109) - Misc CMake improvements +- [THRIFT-5147](https://issues.apache.org/jira/browse/THRIFT-5147) - Add uninstall function +- [THRIFT-5218](https://issues.apache.org/jira/browse/THRIFT-5218) - Automated Github release artifacts do not match checksums provided +- [THRIFT-5249](https://issues.apache.org/jira/browse/THRIFT-5249) - travis-ci : Failed to run FastbinaryTest.py + +### C glib + +- [THRIFT-4873](https://issues.apache.org/jira/browse/THRIFT-4873) - Memory leak in c_glib +- [THRIFT-5118](https://issues.apache.org/jira/browse/THRIFT-5118) - Fix memory leak when the handler method return a exception +- [THRIFT-5134](https://issues.apache.org/jira/browse/THRIFT-5134) - Fix memory leak when the handler method return FALSE +- [THRIFT-5144](https://issues.apache.org/jira/browse/THRIFT-5144) - Fix memory leak when generate deserialize list element +- [THRIFT-4272](https://issues.apache.org/jira/browse/THRIFT-4272) - warnings in glibc library +- [THRIFT-4952](https://issues.apache.org/jira/browse/THRIFT-4952) - Modified ssl_read feedback value break all the time error. +- [THRIFT-5076](https://issues.apache.org/jira/browse/THRIFT-5076) - Improve CMake OpenSSL usage +- [THRIFT-5094](https://issues.apache.org/jira/browse/THRIFT-5094) - Fix memory leak in thrift_server_set_property() +- [THRIFT-5101](https://issues.apache.org/jira/browse/THRIFT-5101) - Return NULL install of FALSE for thrift_server_socket_accept() +- [THRIFT-5102](https://issues.apache.org/jira/browse/THRIFT-5102) - Fix memory leak in thrift_simple_server_serve() +- [THRIFT-5136](https://issues.apache.org/jira/browse/THRIFT-5136) - Fix memory leak in thrift_multiplexed_processor_process_impl() +- [THRIFT-5221](https://issues.apache.org/jira/browse/THRIFT-5221) - Fix stack overflow when reading buffer +- [THRIFT-5237](https://issues.apache.org/jira/browse/THRIFT-5237) - Implement MAX_MESSAGE_SIZE and consolidate limits into a TConfiguration class +- [THRIFT-5255](https://issues.apache.org/jira/browse/THRIFT-5255) - Fix stack overflow in framed transport +- [THRIFT-5256](https://issues.apache.org/jira/browse/THRIFT-5256) - Fix some compile warnings +- [THRIFT-5268](https://issues.apache.org/jira/browse/THRIFT-5268) - Fix some file loss ')' in define + +### C++ + +- [THRIFT-1513](https://issues.apache.org/jira/browse/THRIFT-1513) - Thrift compiler generates inconsistent code with some complex values (causing g++ to error: "has no member named '__isset') +- [THRIFT-5168](https://issues.apache.org/jira/browse/THRIFT-5168) - Useless generated code when .thrift file only has service type +- [THRIFT-5179](https://issues.apache.org/jira/browse/THRIFT-5179) - Thrift compiler will generate wrong code if IDL struct's name is 'a' or 'b' +- [THRIFT-5200](https://issues.apache.org/jira/browse/THRIFT-5200) - Thrift compiler will generate incorrect code when add 'cob_style' option. +- [THRIFT-4282](https://issues.apache.org/jira/browse/THRIFT-4282) - StressTestNonBlocking is disabled in Appveyor as it is unstable on Windows in general +- [THRIFT-4682](https://issues.apache.org/jira/browse/THRIFT-4682) - C++ TBinaryProtocol crashes on port scan +- [THRIFT-4963](https://issues.apache.org/jira/browse/THRIFT-4963) - TNonblockingServer blocked int addTask(IOThread) and notify(workerThread) +- [THRIFT-5047](https://issues.apache.org/jira/browse/THRIFT-5047) - fix cmake support to build cpp server without OPENSSL +- [THRIFT-5076](https://issues.apache.org/jira/browse/THRIFT-5076) - Improve CMake OpenSSL usage +- [THRIFT-5078](https://issues.apache.org/jira/browse/THRIFT-5078) - Handle named pipe clients quickly disconnecting +- [THRIFT-5086](https://issues.apache.org/jira/browse/THRIFT-5086) - CMake target thrift::thrift has no INTERFACE_INCLUDE_DIRECTORIES property +- [THRIFT-5110](https://issues.apache.org/jira/browse/THRIFT-5110) - Added a number of required libs for using static OpenSSL +- [THRIFT-5114](https://issues.apache.org/jira/browse/THRIFT-5114) - Simplify the computation of the size of TMemoryBuffer +- [THRIFT-5177](https://issues.apache.org/jira/browse/THRIFT-5177) - getaddrinfo() should not be used for Unix sockets +- [THRIFT-5178](https://issues.apache.org/jira/browse/THRIFT-5178) - THttpClient should work without specifying host +- [THRIFT-5185](https://issues.apache.org/jira/browse/THRIFT-5185) - C++: Add WebSocket Server Transport +- [THRIFT-5186](https://issues.apache.org/jira/browse/THRIFT-5186) - AI_ADDRCONFIG: Thrift libraries crash with localhost-only network. +- [THRIFT-5215](https://issues.apache.org/jira/browse/THRIFT-5215) - C++: Remove portable_endian.h +- [THRIFT-5217](https://issues.apache.org/jira/browse/THRIFT-5217) - Deprecated boost header +- [THRIFT-5237](https://issues.apache.org/jira/browse/THRIFT-5237) - Implement MAX_MESSAGE_SIZE and consolidate limits into a TConfiguration class +- [THRIFT-5290](https://issues.apache.org/jira/browse/THRIFT-5290) - Adjusting cpp *.cproj msvcrt options according to LEGAL-538 +- [THRIFT-5295](https://issues.apache.org/jira/browse/THRIFT-5295) - Thread and ThreadFactory should be extensible +- [THRIFT-5344](https://issues.apache.org/jira/browse/THRIFT-5344) - TTransport may throw raw pointer exceptions + +### Compiler (General) + +- [THRIFT-4173](https://issues.apache.org/jira/browse/THRIFT-4173) - Go: thrift compiler generates wrong code for list of aliased type +- [THRIFT-4938](https://issues.apache.org/jira/browse/THRIFT-4938) - Issues with version.h treatment +- [THRIFT-4973](https://issues.apache.org/jira/browse/THRIFT-4973) - Add deprecation messages for csharp and netcore +- [THRIFT-4980](https://issues.apache.org/jira/browse/THRIFT-4980) - Remove deprecated C# and netcore bindings from the code base +- [THRIFT-4982](https://issues.apache.org/jira/browse/THRIFT-4982) - Remove deprecated C# bindings from the code baseï…‚ +- [THRIFT-5153](https://issues.apache.org/jira/browse/THRIFT-5153) - Deprecate byte +- [THRIFT-5225](https://issues.apache.org/jira/browse/THRIFT-5225) - Use nullptr instead of NULL +- [THRIFT-5302](https://issues.apache.org/jira/browse/THRIFT-5302) - Add recursive function name uniqueness check + +### D + +- [THRIFT-5059](https://issues.apache.org/jira/browse/THRIFT-5059) - Add cross tests for TZlibTransport in D +- [THRIFT-5156](https://issues.apache.org/jira/browse/THRIFT-5156) - D: Fix library compilation on Windows and compiler warnings +- [THRIFT-5166](https://issues.apache.org/jira/browse/THRIFT-5166) - Add WebSocket Server Transport +- [THRIFT-5184](https://issues.apache.org/jira/browse/THRIFT-5184) - D: WebSocket Server Transport Fix for Firefox + +### Delphi + +- [THRIFT-5044](https://issues.apache.org/jira/browse/THRIFT-5044) - Improve serialization support for TApplicationExceptions and custom exceptions +- [THRIFT-5154](https://issues.apache.org/jira/browse/THRIFT-5154) - Generate interface IDs (IID) for Windows platforms +- [THRIFT-5235](https://issues.apache.org/jira/browse/THRIFT-5235) - Add property setter for isset flags +- [THRIFT-5261](https://issues.apache.org/jira/browse/THRIFT-5261) - Support for deprecated methods (via annotation) +- [THRIFT-5004](https://issues.apache.org/jira/browse/THRIFT-5004) - Make exception implementations more consistent +- [THRIFT-5005](https://issues.apache.org/jira/browse/THRIFT-5005) - Refactoring of the Delphi libs +- [THRIFT-5006](https://issues.apache.org/jira/browse/THRIFT-5006) - Implement DEFAULT_MAX_LENGTH at TFramedTransport +- [THRIFT-5007](https://issues.apache.org/jira/browse/THRIFT-5007) - Implement MAX_MESSAGE_SIZE and remaining read bytes control +- [THRIFT-5009](https://issues.apache.org/jira/browse/THRIFT-5009) - Serializer implemtation lacks support for layered transports +- [THRIFT-5012](https://issues.apache.org/jira/browse/THRIFT-5012) - Centralize configuration aspects into a commonly used configuration object +- [THRIFT-5015](https://issues.apache.org/jira/browse/THRIFT-5015) - WinHTTP QueryDataAvailable cannot be used to retrieve total response size +- [THRIFT-5036](https://issues.apache.org/jira/browse/THRIFT-5036) - buffered transport over sockets may run into unexpected timeouts +- [THRIFT-5048](https://issues.apache.org/jira/browse/THRIFT-5048) - EnumUtils.ToString() throws for elements not known to the receiving end +- [THRIFT-5088](https://issues.apache.org/jira/browse/THRIFT-5088) - Memory leak in TEndpointTransportBase +- [THRIFT-5123](https://issues.apache.org/jira/browse/THRIFT-5123) - add possibility to query HTTP status code with WinHTTP +- [THRIFT-5146](https://issues.apache.org/jira/browse/THRIFT-5146) - Align Delphi to the test suite arguments rules (its "--switch=value", not "--switch value") +- [THRIFT-5186](https://issues.apache.org/jira/browse/THRIFT-5186) - AI_ADDRCONFIG: Thrift libraries crash with localhost-only network. +- [THRIFT-5188](https://issues.apache.org/jira/browse/THRIFT-5188) - Occasional ERROR_INSUFFICIENT_BUFFER at WinHttpQueryHeaders() +- [THRIFT-5251](https://issues.apache.org/jira/browse/THRIFT-5251) - StringUtils.ToString() raises an exception for enum values outside range +- [THRIFT-5304](https://issues.apache.org/jira/browse/THRIFT-5304) - TWinHTTPClientImpl may incorrectly report that the message size is reached + +### Documentation + +- [THRIFT-5037](https://issues.apache.org/jira/browse/THRIFT-5037) - Documentation for TConfiguration +- [THRIFT-5065](https://issues.apache.org/jira/browse/THRIFT-5065) - Fix broken links in the IDL document +- [THRIFT-5074](https://issues.apache.org/jira/browse/THRIFT-5074) - Cleanup test suite command line options + +### Go + +- [THRIFT-4914](https://issues.apache.org/jira/browse/THRIFT-4914) - Compiler generated service clients now provide a new function, LastResponseMeta_(), to get the response metadata (e.g. headers from THeader) from the last client call. +- [THRIFT-4984](https://issues.apache.org/jira/browse/THRIFT-4984) - Scary and spammy "error processing request: EOF" logs from TSimpleServer +- [THRIFT-4985](https://issues.apache.org/jira/browse/THRIFT-4985) - Clean up logging in go library +- [THRIFT-5002](https://issues.apache.org/jira/browse/THRIFT-5002) - remote client fails to compile when extending services +- [THRIFT-5019](https://issues.apache.org/jira/browse/THRIFT-5019) - Multiple import same namespace for go included files +- [THRIFT-5046](https://issues.apache.org/jira/browse/THRIFT-5046) - Custom tags remove db and json tags +- [THRIFT-5069](https://issues.apache.org/jira/browse/THRIFT-5069) - Add TSerializerPool and TDeserializerPool, which are thread-safe versions of TSerializer and TDeserializer. +- [THRIFT-5092](https://issues.apache.org/jira/browse/THRIFT-5092) - Panic on nil buffer writes +- [THRIFT-5152](https://issues.apache.org/jira/browse/THRIFT-5152) - Separate timeout in TSocket +- [THRIFT-5164](https://issues.apache.org/jira/browse/THRIFT-5164) - Go middleware support +- [THRIFT-5214](https://issues.apache.org/jira/browse/THRIFT-5214) - go: Implement connection check in TSocket +- [THRIFT-5233](https://issues.apache.org/jira/browse/THRIFT-5233) - I/O timeout handling in go library +- [THRIFT-5240](https://issues.apache.org/jira/browse/THRIFT-5240) - The context passed into server handler implementations will be canceled when we detected that the client closed the connection. +- [THRIFT-5257](https://issues.apache.org/jira/browse/THRIFT-5257) - Go THeader implementation doesn't handle endOfFrame correctly +- [THRIFT-5270](https://issues.apache.org/jira/browse/THRIFT-5270) - Go library unit test is broken in go 1.15 +- [THRIFT-5278](https://issues.apache.org/jira/browse/THRIFT-5278) - Expose API to use THeader+TCompactProtocol in go library client code +- [THRIFT-5279](https://issues.apache.org/jira/browse/THRIFT-5279) - Cleanups/small optimizations for go's serializer/deserializer code +- [THRIFT-5294](https://issues.apache.org/jira/browse/THRIFT-5294) - Go: TSimpleJSONProtocol could panic on WriteMessageEnd without matching WriteMessageBegin +- [THRIFT-5322](https://issues.apache.org/jira/browse/THRIFT-5322) - Add support to TConfiguration, and also fix a bug that could cause excessive memory usage when reading malformed messages from TCompactProtocol. +- [THRIFT-5338](https://issues.apache.org/jira/browse/THRIFT-5338) - Proposal: Raise minimal supported Go version with upcoming 0.14.0 release + +### Haskell + +- [THRIFT-5072](https://issues.apache.org/jira/browse/THRIFT-5072) - Haskell generator fails to distinguish between multiple enum types with conflicting enum identifiers +- [THRIFT-4959](https://issues.apache.org/jira/browse/THRIFT-4959) - cabal.exe: --enable-tests was specified, but tests can't be enabled in a remote package +- [THRIFT-5211](https://issues.apache.org/jira/browse/THRIFT-5211) - Handle incomplete reads correctly + +### Java + +- [THRIFT-4252](https://issues.apache.org/jira/browse/THRIFT-4252) - Cannot shutdown Java server when clients are still connected +- [THRIFT-4889](https://issues.apache.org/jira/browse/THRIFT-4889) - Add SASL support for non-blocking server +- [THRIFT-4937](https://issues.apache.org/jira/browse/THRIFT-4937) - Apache HttpCore 4.4.1 reached EoS +- [THRIFT-4949](https://issues.apache.org/jira/browse/THRIFT-4949) - improve HTTP/1 server test case +- [THRIFT-5008](https://issues.apache.org/jira/browse/THRIFT-5008) - Add a logger line in case of failing to dispose sasl +- [THRIFT-5013](https://issues.apache.org/jira/browse/THRIFT-5013) - Use Java Objects RequireNonNull +- [THRIFT-5016](https://issues.apache.org/jira/browse/THRIFT-5016) - Do Not Check 'other' For Null in Equals +- [THRIFT-5022](https://issues.apache.org/jira/browse/THRIFT-5022) - TIOStreamTransport.isOpen returns true for one-sided transports (see THRIFT-2530). +- [THRIFT-5031](https://issues.apache.org/jira/browse/THRIFT-5031) - Fix javadoc of TIOStreamTransport +- [THRIFT-5115](https://issues.apache.org/jira/browse/THRIFT-5115) - PR #2022 Updated gradle to 6.2 broke CI +- [THRIFT-5190](https://issues.apache.org/jira/browse/THRIFT-5190) - StringUtils haven't take `(offset + length) > bytes.length` into account +- [THRIFT-5197](https://issues.apache.org/jira/browse/THRIFT-5197) - TSSLTransportFactory Do Not Wrap NOT_OPEN Exception Type for Client +- [THRIFT-5201](https://issues.apache.org/jira/browse/THRIFT-5201) - Use Apache Parent Pom for Thrift Maven Plugin +- [THRIFT-5202](https://issues.apache.org/jira/browse/THRIFT-5202) - TNonblockingMultiFetchClient Use SLF4J Parameterized Logging +- [THRIFT-5203](https://issues.apache.org/jira/browse/THRIFT-5203) - Remove Unused toString Method in TSerializer +- [THRIFT-5237](https://issues.apache.org/jira/browse/THRIFT-5237) - Implement MAX_MESSAGE_SIZE and consolidate limits into a TConfiguration class +- [THRIFT-5247](https://issues.apache.org/jira/browse/THRIFT-5247) - Avoiding meaningless System.copy +- [THRIFT-5274](https://issues.apache.org/jira/browse/THRIFT-5274) - Thrift 0.13.0 does not work with JDK8 +- [THRIFT-5287](https://issues.apache.org/jira/browse/THRIFT-5287) - Log When Client Connections are Dropped +- [THRIFT-5288](https://issues.apache.org/jira/browse/THRIFT-5288) - Move Support for ByteBuffer into TTransport + +### JavaScript + +- [THRIFT-5234](https://issues.apache.org/jira/browse/THRIFT-5234) - Fix a number of js/ts generation issues + +### Lua + +- [THRIFT-5106](https://issues.apache.org/jira/browse/THRIFT-5106) - Fix various Lua library and compiler issues +- [THRIFT-5260](https://issues.apache.org/jira/browse/THRIFT-5260) - Fix the thrift compiler generate problematic lua code for the oneway method +- [THRIFT-4992](https://issues.apache.org/jira/browse/THRIFT-4992) - thrift lua TcompactProtocol bug fix #1881 +- [THRIFT-5262](https://issues.apache.org/jira/browse/THRIFT-5262) - Fix a encoding struct bug in the compact protocol implementation to lua +- [THRIFT-5282](https://issues.apache.org/jira/browse/THRIFT-5282) - Add IPv6 client support to Lua library +- [THRIFT-5286](https://issues.apache.org/jira/browse/THRIFT-5286) - Fix Lua library readBool() in TCompactProtocol +- [THRIFT-5325](https://issues.apache.org/jira/browse/THRIFT-5325) - Fix Lua library writeStructEnd() in TCompactProtocol + +### Markdown + +- [THRIFT-5289](https://issues.apache.org/jira/browse/THRIFT-5289) - Add markdown compiler + +### netstd + +- [THRIFT-5032](https://issues.apache.org/jira/browse/THRIFT-5032) - Allows PascalCase properties for netstd +- [THRIFT-5091](https://issues.apache.org/jira/browse/THRIFT-5091) - Netstd generator produces uncompileable code for struct names ending with "_result" or "_args" +- [THRIFT-5095](https://issues.apache.org/jira/browse/THRIFT-5095) - ToString() should print entire structure, not just the top-level data +- [THRIFT-5198](https://issues.apache.org/jira/browse/THRIFT-5198) - Fix certain Visual Studio hints in generated netstd code +- [THRIFT-5216](https://issues.apache.org/jira/browse/THRIFT-5216) - generate DeepCopy methods +- [THRIFT-5220](https://issues.apache.org/jira/browse/THRIFT-5220) - DeepCopy() extension methods not generated when the IDL contains no service +- [THRIFT-5238](https://issues.apache.org/jira/browse/THRIFT-5238) - GetHashCode can throw NullReferenceException +- [THRIFT-5253](https://issues.apache.org/jira/browse/THRIFT-5253) - using Result in result name generates wrong IAsync interface +- [THRIFT-5254](https://issues.apache.org/jira/browse/THRIFT-5254) - Member name cannot be Isset (unless it is an "required" member) +- [THRIFT-5316](https://issues.apache.org/jira/browse/THRIFT-5316) - Netstd compiler generates wrong ToString() method: .ToString(sb) +- [THRIFT-5317](https://issues.apache.org/jira/browse/THRIFT-5317) - netstd compiler does not escape keywords +- [THRIFT-5320](https://issues.apache.org/jira/browse/THRIFT-5320) - Usage of "Task" as IDL identifier generates uncompileable code +- [THRIFT-4990](https://issues.apache.org/jira/browse/THRIFT-4990) - Upgrade to .NET Core 3.1 (LTS) +- [THRIFT-5010](https://issues.apache.org/jira/browse/THRIFT-5010) - BinaryPrimitives.Read/WriteInt32BigEndian should be used to convert to/from network byte order +- [THRIFT-5020](https://issues.apache.org/jira/browse/THRIFT-5020) - Refactoring & minor fixes for netstd library +- [THRIFT-5021](https://issues.apache.org/jira/browse/THRIFT-5021) - Implement MAX_MESSAGE_SIZE and consolidate limits into a TConfiguration class +- [THRIFT-5026](https://issues.apache.org/jira/browse/THRIFT-5026) - TestClient/Server ignores first cmdline argument +- [THRIFT-5027](https://issues.apache.org/jira/browse/THRIFT-5027) - Implement remaining read bytes checks +- [THRIFT-5053](https://issues.apache.org/jira/browse/THRIFT-5053) - Fix the netstd tutorial console logging and README +- [THRIFT-5083](https://issues.apache.org/jira/browse/THRIFT-5083) - NetStd JSON Protocol left in incorrect state +- [THRIFT-5133](https://issues.apache.org/jira/browse/THRIFT-5133) - TCompactProtocol string allocation improvement +- [THRIFT-5172](https://issues.apache.org/jira/browse/THRIFT-5172) - NetStd TBaseClient open output transport multiple times +- [THRIFT-5210](https://issues.apache.org/jira/browse/THRIFT-5210) - further performance optimizations +- [THRIFT-5239](https://issues.apache.org/jira/browse/THRIFT-5239) - THttpTransport should support passing in an HttpClient +- [THRIFT-5252](https://issues.apache.org/jira/browse/THRIFT-5252) - Make CreateHttpClientHandler() method virtual +- [THRIFT-5275](https://issues.apache.org/jira/browse/THRIFT-5275) - Compilation error with Thrift when used in .Net Framework 4.6.1 or above +- [THRIFT-5343](https://issues.apache.org/jira/browse/THRIFT-5343) - TTlsSocketTransport does not resolve IPv4 addresses or validate hostnames correctly + +### Node.js + +- [THRIFT-3356](https://issues.apache.org/jira/browse/THRIFT-3356) - TypeError: 'undefined' is not a function (evaluating 'Error.captureStackTrace(this, this.constructor)') +- [THRIFT-4994](https://issues.apache.org/jira/browse/THRIFT-4994) - TWebSocketTransport false scope in forEach in browser +- [THRIFT-5003](https://issues.apache.org/jira/browse/THRIFT-5003) - Websocket Connection in Browsers with nodejs code +- [THRIFT-5116](https://issues.apache.org/jira/browse/THRIFT-5116) - Ubuntu xenial NodeJS 6.x is too old, 10.x required +- [THRIFT-5163](https://issues.apache.org/jira/browse/THRIFT-5163) - adds Q to exports for browserify + +### Perl + +- [THRIFT-5050](https://issues.apache.org/jira/browse/THRIFT-5050) - Fix MemoryBuffer.pm to raise a proper exception if no data is available +- [THRIFT-5066](https://issues.apache.org/jira/browse/THRIFT-5066) - Implement testBinary invocation in TestClient.pl + +### PHP + +- [THRIFT-4942](https://issues.apache.org/jira/browse/THRIFT-4942) - Set PHP struct generated field values as private with getters and setters +- [THRIFT-5082](https://issues.apache.org/jira/browse/THRIFT-5082) - Add a Class reference for PHP enum $_TSPEC +- [THRIFT-5103](https://issues.apache.org/jira/browse/THRIFT-5103) - PHP 7.4 THttpClient deprecated error +- [THRIFT-5130](https://issues.apache.org/jira/browse/THRIFT-5130) - Use Apcu instead of APC +- [THRIFT-5132](https://issues.apache.org/jira/browse/THRIFT-5132) - Warning in TSocket when using ssl connection +- [THRIFT-5199](https://issues.apache.org/jira/browse/THRIFT-5199) - Infinite loop in PHP TSocket::write when peer closes connection +- [THRIFT-5336](https://issues.apache.org/jira/browse/THRIFT-5336) - Add possibility to setup connection timeout in TCurlClient + +### Python + +- [THRIFT-2087](https://issues.apache.org/jira/browse/THRIFT-2087) - unicode decode errors +- [THRIFT-4002](https://issues.apache.org/jira/browse/THRIFT-4002) - Thrift exceptions are not hashable in Python 3 +- [THRIFT-5107](https://issues.apache.org/jira/browse/THRIFT-5107) - Travis build fails with missing Python 3.3 or newer? +- [THRIFT-5165](https://issues.apache.org/jira/browse/THRIFT-5165) - Python THttpClient saves cookie when Set-Cookie response header is present +- [THRIFT-5186](https://issues.apache.org/jira/browse/THRIFT-5186) - AI_ADDRCONFIG: Thrift libraries crash with localhost-only network. +- [THRIFT-5248](https://issues.apache.org/jira/browse/THRIFT-5248) - Python: Make TSocket.isOpen check if the other end is still connected +- [THRIFT-5303](https://issues.apache.org/jira/browse/THRIFT-5303) - Unicode decode errors in _fast_decode +- [THRIFT-5331](https://issues.apache.org/jira/browse/THRIFT-5331) - Python: allow THeaderProtocol to choose which subprotocol to use for outbound connections + +### Ruby + +- [THRIFT-5281](https://issues.apache.org/jira/browse/THRIFT-5281) - Some warning messages need to be fixed +- [THRIFT-4707](https://issues.apache.org/jira/browse/THRIFT-4707) - Enable maintainers to upload newer versions of Ruby Gem of Thrift +- [THRIFT-5061](https://issues.apache.org/jira/browse/THRIFT-5061) - Pin Ruby's rack version to 2.0.8 +- [THRIFT-5100](https://issues.apache.org/jira/browse/THRIFT-5100) - Gem::InstallError: byebug requires Ruby version >= 2.4.0. +- [THRIFT-5266](https://issues.apache.org/jira/browse/THRIFT-5266) - release ruby library thrift 0.13.0 + +### Rust + +- [THRIFT-4764](https://issues.apache.org/jira/browse/THRIFT-4764) - Rust frontend emits deprecated clippy suppression attributes +- [THRIFT-5071](https://issues.apache.org/jira/browse/THRIFT-5071) - Rust: rust tutorial can not be compiled with rust edition 2018 +- [THRIFT-5158](https://issues.apache.org/jira/browse/THRIFT-5158) - Update Rust Compiler to generate 2018 edition code only +- [THRIFT-5307](https://issues.apache.org/jira/browse/THRIFT-5307) - Rust generated code should compile cleanly with clippy +- [THRIFT-4915](https://issues.apache.org/jira/browse/THRIFT-4915) - Deserializing double into OrderedFloat always returns zero when using TCompactProtocol +- [THRIFT-4995](https://issues.apache.org/jira/browse/THRIFT-4995) - [Rust] Use `ToSocketAddrs` for expressing network addresses +- [THRIFT-5042](https://issues.apache.org/jira/browse/THRIFT-5042) - Fix failing cargo tests +- [THRIFT-5043](https://issues.apache.org/jira/browse/THRIFT-5043) - Make TBufferChannel clonable +- [THRIFT-5111](https://issues.apache.org/jira/browse/THRIFT-5111) - CI fails with error[E0721]: `await` is a keyword in the 2018 edition +- [THRIFT-5131](https://issues.apache.org/jira/browse/THRIFT-5131) - i64 maxint decoding panics with integer-encoding >= 1.1.0 +- [THRIFT-5306](https://issues.apache.org/jira/browse/THRIFT-5306) - Rust library, tutorial, test, cross-test code should not throw any clippy errors + +### Swift + +- [THRIFT-4989](https://issues.apache.org/jira/browse/THRIFT-4989) - Run time exception when using TCompactProtocol +- [THRIFT-5128](https://issues.apache.org/jira/browse/THRIFT-5128) - Swift TFramedTransport does not work using present code +- [THRIFT-5138](https://issues.apache.org/jira/browse/THRIFT-5138) - Swift generator does not escape keywords properly +- [THRIFT-5155](https://issues.apache.org/jira/browse/THRIFT-5155) - Swift 5.1 support +- [THRIFT-5070](https://issues.apache.org/jira/browse/THRIFT-5070) - Swift: Hashable.hashValue is deprecated as a protocol requirement +- [THRIFT-5084](https://issues.apache.org/jira/browse/THRIFT-5084) - Swift: Server-side support for Multiplexing Services +- [THRIFT-5121](https://issues.apache.org/jira/browse/THRIFT-5121) - Logic bug in TMultiplexedProcessor – Swift +- [THRIFT-5125](https://issues.apache.org/jira/browse/THRIFT-5125) - Swift server does not work using present code. +- [THRIFT-5129](https://issues.apache.org/jira/browse/THRIFT-5129) - Swift TSocketTransport cannot be used to connect to client +- [THRIFT-5150](https://issues.apache.org/jira/browse/THRIFT-5150) - TSet does not compile with Swift 5.2 + +### Test Suite + +- [THRIFT-4974](https://issues.apache.org/jira/browse/THRIFT-4974) - Add cross test for Python's Unix domain socket transport +- [THRIFT-5145](https://issues.apache.org/jira/browse/THRIFT-5145) - Streamline --pipe and --named-pipe options in the code base +- [THRIFT-5171](https://issues.apache.org/jira/browse/THRIFT-5171) - Fix maven-ant-tasks to use HTTPS instead of HTTP + +### TypeScript - Library + +- [THRIFT-5003](https://issues.apache.org/jira/browse/THRIFT-5003) - Websocket Connection in Browsers with nodejs code + +### Tutorial + +- [THRIFT-4972](https://issues.apache.org/jira/browse/THRIFT-4972) - Add Makefile.am to the Perl tutorial +- [THRIFT-4975](https://issues.apache.org/jira/browse/THRIFT-4975) - Add Makefile.am to the PHP tutorial +- [THRIFT-5051](https://issues.apache.org/jira/browse/THRIFT-5051) - Fix Python tutorials to address THRIFT-4002 +- [THRIFT-5052](https://issues.apache.org/jira/browse/THRIFT-5052) - Make the Go tutorial executable to the end +- [THRIFT-5122](https://issues.apache.org/jira/browse/THRIFT-5122) - Fix memory leak in c_glib tutorial server + + +## 0.13.0 + +### New Languages + +- (none) + +### Deprecated Languages + +- [THRIFT-4723](https://issues.apache.org/jira/browse/THRIFT-4723) - CSharp and Netcore targets are deprecated and will be removed with the next release - use NetStd instead. + +### Removed Languages + +- [THRIFT-4719](https://issues.apache.org/jira/browse/THRIFT-4719) - Cocoa language was removed - use swift instead. + +### Breaking Changes + +- [THRIFT-4743](https://issues.apache.org/jira/browse/THRIFT-4743) - compiler: removed the plug-in mechanism +- [THRIFT-4720](https://issues.apache.org/jira/browse/THRIFT-4720) - cpp: C++03/C++98 support has been removed; also removed boost as a runtime dependency +- [THRIFT-4730](https://issues.apache.org/jira/browse/THRIFT-4730) - cpp: BoostThreadFactory, PosixThreadFactory, StdThreadFactory removed +- [THRIFT-4732](https://issues.apache.org/jira/browse/THRIFT-4732) - cpp: CMake build changed to use BUILD_SHARED_LIBS +- [THRIFT-4735](https://issues.apache.org/jira/browse/THRIFT-4735) - cpp: Removed Qt4 support +- [THRIFT-4740](https://issues.apache.org/jira/browse/THRIFT-4740) - cpp: Use std::chrono::duration for timeouts +- [THRIFT-4762](https://issues.apache.org/jira/browse/THRIFT-4762) - cpp: TTransport::getOrigin() is now const +- [THRIFT-4702](https://issues.apache.org/jira/browse/THRIFT-4702) - java: class org.apache.thrift.AutoExpandingBuffer is no longer public +- [THRIFT-4709](https://issues.apache.org/jira/browse/THRIFT-4709) - java: changes to UTF-8 handling require JDK 1.7 at a minimum +- [THRIFT-4712](https://issues.apache.org/jira/browse/THRIFT-4712) - java: class org.apache.thrift.ShortStack is no longer public +- [THRIFT-4725](https://issues.apache.org/jira/browse/THRIFT-4725) - java: change return type signature of 'process' methods +- [THRIFT-4805](https://issues.apache.org/jira/browse/THRIFT-4805) - java: replaced TSaslTransportException with TTransportException +- [THRIFT-2530](https://issues.apache.org/jira/browse/THRIFT-2530) - java: TIOStreamTransport's "isOpen" now returns false after "close" is called +- [THRIFT-4675](https://issues.apache.org/jira/browse/THRIFT-4675) - js: now uses node-int64 for 64 bit integer constants +- [THRIFT-4841](https://issues.apache.org/jira/browse/THRIFT-4841) - delphi: old THTTPTransport is now TMsxmlHTTPTransport +- [THRIFT-4536](https://issues.apache.org/jira/browse/THRIFT-4536) - rust: convert from try-from crate to rust stable (1.34+), re-export ordered-float + +### Known Issues (Blocker or Critical) + +- [THRIFT-3877](https://issues.apache.org/jira/browse/THRIFT-3877) - C++: library don't work with HTTP (csharp server, cpp client; need cross test enhancement) + +### As3 + +- [THRIFT-4784](https://issues.apache.org/jira/browse/THRIFT-4784) - Thrift should throw when skipping over unexpected data + +### Build Process + +- [THRIFT-2333](https://issues.apache.org/jira/browse/THRIFT-2333) - RPMBUILD: Abort build if user did not disable ruby but ruby build will fail later on +- [THRIFT-4689](https://issues.apache.org/jira/browse/THRIFT-4689) - Pull changes from 0.12.0 release branch into master +- [THRIFT-4690](https://issues.apache.org/jira/browse/THRIFT-4690) - Update dlang deimos for OpenSSL 1.1 (use 1.1.0h tagged release instead of master) +- [THRIFT-4694](https://issues.apache.org/jira/browse/THRIFT-4694) - Upgrade Java to Java 1.8 +- [THRIFT-4716](https://issues.apache.org/jira/browse/THRIFT-4716) - Create a version alignment tool to make releases easier +- [THRIFT-4760](https://issues.apache.org/jira/browse/THRIFT-4760) - Install pkgconfig when using cmake +- [THRIFT-4769](https://issues.apache.org/jira/browse/THRIFT-4769) - Change NuGet package to use netstd artifact +- [THRIFT-4811](https://issues.apache.org/jira/browse/THRIFT-4811) - Add cmake config module +- [THRIFT-4855](https://issues.apache.org/jira/browse/THRIFT-4855) - go CI fails with "cannot find package "golang.org/x/tools/go/packages" in any of ..." +- [THRIFT-4864](https://issues.apache.org/jira/browse/THRIFT-4864) - CI fails at netstd +- [THRIFT-4874](https://issues.apache.org/jira/browse/THRIFT-4874) - Thrift 0.12.0 Source Distribution (.tar.gz) Contains Hardlinks - Extract Fails +- [THRIFT-4896](https://issues.apache.org/jira/browse/THRIFT-4896) - cpp and c_glib include paths are added to source files when building +- [THRIFT-4966](https://issues.apache.org/jira/browse/THRIFT-4966) - Git ignore files generated by the build + +### C glib + +- [THRIFT-4842](https://issues.apache.org/jira/browse/THRIFT-4842) - Multiplexed protocol has a memory leak in set c_glib +- [THRIFT-4878](https://issues.apache.org/jira/browse/THRIFT-4878) - c_glib ThriftSocket support for unix domain sockets +- [THRIFT-4950](https://issues.apache.org/jira/browse/THRIFT-4950) - fix bind print error and Macro call errors thrift_server_socket + +### C# + +- [THRIFT-3587](https://issues.apache.org/jira/browse/THRIFT-3587) - C# TTLSSocket does not use timeout for opening the socket +- [THRIFT-4024](https://issues.apache.org/jira/browse/THRIFT-4024) - Skip() should throw on unknown data types +- [THRIFT-4684](https://issues.apache.org/jira/browse/THRIFT-4684) - Missing namespace and un-used private fields in WCF fault classes when enable WCF in C# code generation +- [THRIFT-4715](https://issues.apache.org/jira/browse/THRIFT-4715) - C# union "data" should be strongly-typed +- [THRIFT-4723](https://issues.apache.org/jira/browse/THRIFT-4723) - Consolidate C# and netcore into new netstd language target (and finally deprecate both C# and netcore bindings) +- [THRIFT-4741](https://issues.apache.org/jira/browse/THRIFT-4741) - Missing "inner" argument from one CTOR +- [THRIFT-4769](https://issues.apache.org/jira/browse/THRIFT-4769) - Change NuGet package to use netstd artifact +- [THRIFT-4859](https://issues.apache.org/jira/browse/THRIFT-4859) - Enables changing 'UserAgent' +- [THRIFT-4907](https://issues.apache.org/jira/browse/THRIFT-4907) - strong named assemblies wanted + +### C++ + +- [THRIFT-4384](https://issues.apache.org/jira/browse/THRIFT-4384) - Using a concurrent client with cpp async is not safe. +- [THRIFT-4441](https://issues.apache.org/jira/browse/THRIFT-4441) - C++: support building lib without Boost +- [THRIFT-4487](https://issues.apache.org/jira/browse/THRIFT-4487) - gettimeofday: windows implementation not quoting source, applying license to foreign code +- [THRIFT-4593](https://issues.apache.org/jira/browse/THRIFT-4593) - Unit Tests failing on Alpine Linux due to non-portable mutex initializers +- [THRIFT-4678](https://issues.apache.org/jira/browse/THRIFT-4678) - add noexcept cpp generator option +- [THRIFT-4720](https://issues.apache.org/jira/browse/THRIFT-4720) - Drop support for C++03/C++98 and begin refactoring +- [THRIFT-4730](https://issues.apache.org/jira/browse/THRIFT-4730) - Remove pthread and boost::thread library support and use std::thread for C++11 +- [THRIFT-4735](https://issues.apache.org/jira/browse/THRIFT-4735) - Remove C++ Qt4 support (leave Qt5) - Qt4 LTS ended in 2014 +- [THRIFT-4739](https://issues.apache.org/jira/browse/THRIFT-4739) - Good old concurrency_test failing on windows builds again with some regularity +- [THRIFT-4740](https://issues.apache.org/jira/browse/THRIFT-4740) - Use std::chrono for timeout and remove old structures. +- [THRIFT-4762](https://issues.apache.org/jira/browse/THRIFT-4762) - C++: Applied some C++11 refactorings to the runtime library and compiler +- [THRIFT-4776](https://issues.apache.org/jira/browse/THRIFT-4776) - Modernize c++11 code by clang-tidy +- [THRIFT-4830](https://issues.apache.org/jira/browse/THRIFT-4830) - Add to_string function for enum in C++ file generate +- [THRIFT-4861](https://issues.apache.org/jira/browse/THRIFT-4861) - Fix use of deprecated boost endian header; move to minimum boost 1.56.0 +- [THRIFT-4936](https://issues.apache.org/jira/browse/THRIFT-4936) - add depth limit type exception description +- [THRIFT-4962](https://issues.apache.org/jira/browse/THRIFT-4962) - Deadlock in TimerManager::stop + +### cocoa + +- [THRIFT-4719](https://issues.apache.org/jira/browse/THRIFT-4719) - Remove cocoa language support + +## Compiler (General) + +- [THRIFT-4743](https://issues.apache.org/jira/browse/THRIFT-4743) - Remove the compiler plug-in mode + +### contributed + +- [THRIFT-4897](https://issues.apache.org/jira/browse/THRIFT-4897) - UT of thrift-maven-plugin failed + +## D language + +- [THRIFT-4690](https://issues.apache.org/jira/browse/THRIFT-4690) - Update dlang deimos for OpenSSL 1.1 (use 1.1.0h tagged release instead of master) +- [THRIFT-4724](https://issues.apache.org/jira/browse/THRIFT-4724) - dlang dub.json dependency for openssl is too restrictive +- [THRIFT-4918](https://issues.apache.org/jira/browse/THRIFT-4918) - dlang name conflict + +### dart + +- [THRIFT-4654](https://issues.apache.org/jira/browse/THRIFT-4654) - Thrift Dart port is not compatible with Dart 2 + +### Delphi + +- [THRIFT-4024](https://issues.apache.org/jira/browse/THRIFT-4024) - Skip() should throw on unknown data types +- [THRIFT-4841](https://issues.apache.org/jira/browse/THRIFT-4841) - THTTPTransport relies on activeX component +- [THRIFT-4843](https://issues.apache.org/jira/browse/THRIFT-4843) - http:// and https:// schemes are switched in test client +- [THRIFT-4862](https://issues.apache.org/jira/browse/THRIFT-4862) - better ToString() support for enums and container types +- [THRIFT-4863](https://issues.apache.org/jira/browse/THRIFT-4863) - better indication of WinHTTP errors +- [THRIFT-4881](https://issues.apache.org/jira/browse/THRIFT-4881) - Allow TLS1.1 and TLS1.2 over WinHTTP even when not configured as systemwide default +- [THRIFT-4882](https://issues.apache.org/jira/browse/THRIFT-4882) - Autodetect proxy settings with WinHTTP +- [THRIFT-4884](https://issues.apache.org/jira/browse/THRIFT-4884) - Add serialisation performance test for Delphi +- [THRIFT-4886](https://issues.apache.org/jira/browse/THRIFT-4886) - More detailed error information for WinHTTP transport +- [THRIFT-4894](https://issues.apache.org/jira/browse/THRIFT-4894) - Enable automatic content encoding handling for gzip,deflate in the WinHTTP client +- [THRIFT-4939](https://issues.apache.org/jira/browse/THRIFT-4939) - TThriftListImpl.Sort() does not use comparer +- [THRIFT-4944](https://issues.apache.org/jira/browse/THRIFT-4944) - Field IDs > 255 fail with compact protocol + +### Documentation + +- [THRIFT-4697](https://issues.apache.org/jira/browse/THRIFT-4697) - Create updated release procedures +- [THRIFT-4808](https://issues.apache.org/jira/browse/THRIFT-4808) - Update LANGUAGES.md on master to reflect master +- [THRIFT-4933](https://issues.apache.org/jira/browse/THRIFT-4933) - Incorrect description in the 0.12.0 version of the documentation + +### Erlang + +- [THRIFT-4583](https://issues.apache.org/jira/browse/THRIFT-4583) - Support rebar3 for erlang builds +- [THRIFT-4744](https://issues.apache.org/jira/browse/THRIFT-4744) - Erlang help intendation not aligned + +### Go + +- [THRIFT-4024](https://issues.apache.org/jira/browse/THRIFT-4024) - Skip() should throw on unknown data types +- [THRIFT-4612](https://issues.apache.org/jira/browse/THRIFT-4612) - Add THeader for Go +- [THRIFT-4747](https://issues.apache.org/jira/browse/THRIFT-4747) - The 'omitempty' tag should not be appended to optional fields that have a default value +- [THRIFT-4797](https://issues.apache.org/jira/browse/THRIFT-4797) - Generated Go code produces name collisions on imports +- [THRIFT-4908](https://issues.apache.org/jira/browse/THRIFT-4908) - reader&writer in golang's TBinaryProtocol is not necessary and misleading + +### haskell + +- [THRIFT-4834](https://issues.apache.org/jira/browse/THRIFT-4834) - CI error at Haskell: Failed to load interface for `Network' +- [THRIFT-4955](https://issues.apache.org/jira/browse/THRIFT-4955) - Haskell test broken due to extension to CompactProtoTestStruct +- [THRIFT-4956](https://issues.apache.org/jira/browse/THRIFT-4956) - DebugProtoTest_Main.hs: Invalid ThriftType 128 + +### haxe + +- [THRIFT-4024](https://issues.apache.org/jira/browse/THRIFT-4024) - Skip() should throw on unknown data types +- [THRIFT-4812](https://issues.apache.org/jira/browse/THRIFT-4812) - haxelib readme still points to old ASF git repo + +### HTML + +- [THRIFT-4763](https://issues.apache.org/jira/browse/THRIFT-4763) - HTML compiler produces invalid HTML document + +### Java + +- [THRIFT-2530](https://issues.apache.org/jira/browse/THRIFT-2530) - TIOStreamTransport's isOpen() always be true even if close() was called. +- [THRIFT-4368](https://issues.apache.org/jira/browse/THRIFT-4368) - Guaranteed NPE in TBaseAsyncProcessor.java +- [THRIFT-4469](https://issues.apache.org/jira/browse/THRIFT-4469) - isServing is not thread safe +- [THRIFT-4481](https://issues.apache.org/jira/browse/THRIFT-4481) - TBinaryProtocol.writeMessageEnd isn't throwable exception +- [THRIFT-4695](https://issues.apache.org/jira/browse/THRIFT-4695) - Pre-Size Java Collections in Union +- [THRIFT-4696](https://issues.apache.org/jira/browse/THRIFT-4696) - NonBlocking Server: Use case-switch Statement Instead of if-else Clauses +- [THRIFT-4702](https://issues.apache.org/jira/browse/THRIFT-4702) - Improve AutoExpandingBuffer +- [THRIFT-4704](https://issues.apache.org/jira/browse/THRIFT-4704) - Streamline TDeserializer Implementation +- [THRIFT-4709](https://issues.apache.org/jira/browse/THRIFT-4709) - Use StandardCharset UTF-8 +- [THRIFT-4711](https://issues.apache.org/jira/browse/THRIFT-4711) - Improve Immutable None Type Instantiation +- [THRIFT-4712](https://issues.apache.org/jira/browse/THRIFT-4712) - Improve Performance of ShortStack +- [THRIFT-4713](https://issues.apache.org/jira/browse/THRIFT-4713) - Review of TBaseHelper.java +- [THRIFT-4714](https://issues.apache.org/jira/browse/THRIFT-4714) - Java TFramedTransport calls write twice for each flush +- [THRIFT-4725](https://issues.apache.org/jira/browse/THRIFT-4725) - Change Return Type Signature of Process Methods +- [THRIFT-4726](https://issues.apache.org/jira/browse/THRIFT-4726) - Remove SLF4J Logging Guards +- [THRIFT-4748](https://issues.apache.org/jira/browse/THRIFT-4748) - Add Jitpack support +- [THRIFT-4766](https://issues.apache.org/jira/browse/THRIFT-4766) - JDK9+ fails on missing annotations +- [THRIFT-4773](https://issues.apache.org/jira/browse/THRIFT-4773) - TSaslTransport should relay underlying TTransportException to TSaslTransportException +- [THRIFT-4805](https://issues.apache.org/jira/browse/THRIFT-4805) - Suppress excessive logging of SASL TTransportExceptions in case of END_OF_FILE +- [THRIFT-4849](https://issues.apache.org/jira/browse/THRIFT-4849) - Do not Ignore InterruptedException +- [THRIFT-4851](https://issues.apache.org/jira/browse/THRIFT-4851) - Remove All Calls To printStackTrace +- [THRIFT-4857](https://issues.apache.org/jira/browse/THRIFT-4857) - Java field hash code implementation inconsistent with equals. +- [THRIFT-4858](https://issues.apache.org/jira/browse/THRIFT-4858) - Java TThreadPoolServer: confusing error message on closed socket +- [THRIFT-4865](https://issues.apache.org/jira/browse/THRIFT-4865) - warning: [deprecation] UTF_8 in Charsets has been deprecated +- [THRIFT-4899](https://issues.apache.org/jira/browse/THRIFT-4899) - Generated TypeScript declarations incorrectly references types when there is more than 1 include +- [THRIFT-4945](https://issues.apache.org/jira/browse/THRIFT-4945) - Log output mode is not standardized +- [THRIFT-4957](https://issues.apache.org/jira/browse/THRIFT-4957) - testSanePartsOfCompactProtoTestStruct FAILED + +### JavaScript + +- [THRIFT-4675](https://issues.apache.org/jira/browse/THRIFT-4675) - JS code generators not handling int64 type properly for constants and for TypeScript type mappings +- [THRIFT-4728](https://issues.apache.org/jira/browse/THRIFT-4728) - Cleanup for the double rendering test in JS +- [THRIFT-4737](https://issues.apache.org/jira/browse/THRIFT-4737) - thrift.js does not use customHeaders in jqRequest +- [THRIFT-4745](https://issues.apache.org/jira/browse/THRIFT-4745) - warning C4305: 'initializing' : truncation from '"__int64' to 'long' +- [THRIFT-4757](https://issues.apache.org/jira/browse/THRIFT-4757) - grunt-shell-spawn drags in sync-exec which has a security notice + +### netcore + +- [THRIFT-4024](https://issues.apache.org/jira/browse/THRIFT-4024) - Skip() should throw on unknown data types +- [THRIFT-4722](https://issues.apache.org/jira/browse/THRIFT-4722) - Netcore union "data" should be strongly-typed +- [THRIFT-4723](https://issues.apache.org/jira/browse/THRIFT-4723) - Consolidate C# and netcore into new netstd language target (and finally deprecate both C# and netcore bindings) +- [THRIFT-4742](https://issues.apache.org/jira/browse/THRIFT-4742) - Typo "cannot read from null input stream" on write +- [THRIFT-4769](https://issues.apache.org/jira/browse/THRIFT-4769) - Change NuGet package to use netstd artifact +- [THRIFT-4919](https://issues.apache.org/jira/browse/THRIFT-4919) - THttpTransport.cs (netstd) and THttpClientTransport (netcore) have bad timeout code + +### netstd + +- [THRIFT-4768](https://issues.apache.org/jira/browse/THRIFT-4768) - Remove "nullable" option from the code base (netstd ONLY) +- [THRIFT-4772](https://issues.apache.org/jira/browse/THRIFT-4772) - fully enable server-side usage of framed/buffered transports +- [THRIFT-4813](https://issues.apache.org/jira/browse/THRIFT-4813) - NamedPipes may not work in all cases +- [THRIFT-4816](https://issues.apache.org/jira/browse/THRIFT-4816) - JSONTransports Context.WriteAsync/ReadAsync are badly named +- [THRIFT-4817](https://issues.apache.org/jira/browse/THRIFT-4817) - Add string CTOR to TTlsSocketTransport +- [THRIFT-4818](https://issues.apache.org/jira/browse/THRIFT-4818) - Test client should use cancellation token +- [THRIFT-4821](https://issues.apache.org/jira/browse/THRIFT-4821) - Normalize TServerSocketTransport constructors in netstd +- [THRIFT-4822](https://issues.apache.org/jira/browse/THRIFT-4822) - Refactor bool CTOR flags into enum type +- [THRIFT-4824](https://issues.apache.org/jira/browse/THRIFT-4824) - Logger deprecation warnings in tutorial +- [THRIFT-4825](https://issues.apache.org/jira/browse/THRIFT-4825) - Align TTlsServerSocketTransport constructors with TServerSocketTransport - Breaking Change +- [THRIFT-4829](https://issues.apache.org/jira/browse/THRIFT-4829) - HTTP server transport lacks TransportFactory arguments +- [THRIFT-4831](https://issues.apache.org/jira/browse/THRIFT-4831) - interface ITProtocolFactory should be class TProtocolFactory again +- [THRIFT-4832](https://issues.apache.org/jira/browse/THRIFT-4832) - superfluous backing field causes CS0169 "field never used" +- [THRIFT-4839](https://issues.apache.org/jira/browse/THRIFT-4839) - Remove embedded buffering/framed options from TCP transports +- [THRIFT-4840](https://issues.apache.org/jira/browse/THRIFT-4840) - Update the README in the netstd tutorial to include references to the new buffering arguments +- [THRIFT-4848](https://issues.apache.org/jira/browse/THRIFT-4848) - Add ability to set Content-Type,Accept headers in HTTP client +- [THRIFT-4853](https://issues.apache.org/jira/browse/THRIFT-4853) - TServerFramedTransport is now obsolete and can be removed +- [THRIFT-4854](https://issues.apache.org/jira/browse/THRIFT-4854) - oneway calls do not work over HTTP +- [THRIFT-4860](https://issues.apache.org/jira/browse/THRIFT-4860) - Allow changing "User-Agent" +- [THRIFT-4879](https://issues.apache.org/jira/browse/THRIFT-4879) - general performance improvements for netstd library +- [THRIFT-4891](https://issues.apache.org/jira/browse/THRIFT-4891) - Align HTTP test client with all other variants +- [THRIFT-4893](https://issues.apache.org/jira/browse/THRIFT-4893) - Enable automatic content encoding handling for gzip,deflate in the HTTP client +- [THRIFT-4898](https://issues.apache.org/jira/browse/THRIFT-4898) - Pipe write operations across a network are limited to 65,535 bytes per write. +- [THRIFT-4919](https://issues.apache.org/jira/browse/THRIFT-4919) - THttpTransport.cs (netstd) and THttpClientTransport (netcore) have bad timeout code + +### node.js + +- [THRIFT-3060](https://issues.apache.org/jira/browse/THRIFT-3060) - Node.js client retry logic doesn't flush offline queue on reconnect +- [THRIFT-4675](https://issues.apache.org/jira/browse/THRIFT-4675) - JS code generators not handling int64 type properly for constants and for TypeScript type mappings +- [THRIFT-4738](https://issues.apache.org/jira/browse/THRIFT-4738) - Generated typescript type definition files are incorrect +- [THRIFT-4771](https://issues.apache.org/jira/browse/THRIFT-4771) - THeader for node.js +- [THRIFT-4809](https://issues.apache.org/jira/browse/THRIFT-4809) - Javascript episodic code generation +- [THRIFT-4844](https://issues.apache.org/jira/browse/THRIFT-4844) - createConnection ignores connect_timeout option + +### perl + +- [THRIFT-4691](https://issues.apache.org/jira/browse/THRIFT-4691) - The perl CPAN module contains no tests + +### PHP + +- [THRIFT-4751](https://issues.apache.org/jira/browse/THRIFT-4751) - Missing imports in TProtocol (phpdoc related only) +- [THRIFT-4794](https://issues.apache.org/jira/browse/THRIFT-4794) - Finish adding json protocol to the php cross test +- [THRIFT-4807](https://issues.apache.org/jira/browse/THRIFT-4807) - PHP extension segfaults if reference is used in input +- [THRIFT-4845](https://issues.apache.org/jira/browse/THRIFT-4845) - PHP's TCurlClient ignores timeout values smaller that 1 second + +### python + +- [THRIFT-1549](https://issues.apache.org/jira/browse/THRIFT-1549) - Python TSSLSocket: Shutdown cleanly +- [THRIFT-4733](https://issues.apache.org/jira/browse/THRIFT-4733) - Address already in use with python unit test +- [THRIFT-4767](https://issues.apache.org/jira/browse/THRIFT-4767) - support tcp keepalive in python +- [THRIFT-4778](https://issues.apache.org/jira/browse/THRIFT-4778) - Python protocol factories do not derive from TProtocolFactory +- [THRIFT-4779](https://issues.apache.org/jira/browse/THRIFT-4779) - Python, Java TMultiplexedProcessor do not raise TProtocolException +- [THRIFT-4780](https://issues.apache.org/jira/browse/THRIFT-4780) - TMultiplexedProcessor is not fully tested or implemented in Python +- [THRIFT-4783](https://issues.apache.org/jira/browse/THRIFT-4783) - Thrift should throw when skipping over unexpected data +- [THRIFT-4798](https://issues.apache.org/jira/browse/THRIFT-4798) - Fix python THttpServer to honor correct oneway reply semantics +- [THRIFT-4892](https://issues.apache.org/jira/browse/THRIFT-4892) - SASL data type exception for PLAIN +- [THRIFT-4920](https://issues.apache.org/jira/browse/THRIFT-4920) - Binary constants emit non-binary Python literals + +### ruby + +- [THRIFT-4721](https://issues.apache.org/jira/browse/THRIFT-4721) - Installing the ruby gem on systems without make fails in the build_ext task. +- [THRIFT-4971](https://issues.apache.org/jira/browse/THRIFT-4971) - Fix lib/rb/spec/union_spec.rb so that CI succeeds + +### rust + +- [THRIFT-4953](https://issues.apache.org/jira/browse/THRIFT-4953) - Unspecified Field Identifier Creates Non Compiling Rust Code +- [THRIFT-4960](https://issues.apache.org/jira/browse/THRIFT-4960) - Bare Trait Warnings + +### Swift + +- [THRIFT-4902](https://issues.apache.org/jira/browse/THRIFT-4902) - Swift compatibility with Swift 4.2, 5.0 and 5.1 + +### Test suite + +- [THRIFT-4301](https://issues.apache.org/jira/browse/THRIFT-4301) - configuring --without-python and --without-py3 still invokes py3 tests in make cross +- [THRIFT-4405](https://issues.apache.org/jira/browse/THRIFT-4405) - Incorrect handling of sequence numbers that wrap to negative +- [THRIFT-4794](https://issues.apache.org/jira/browse/THRIFT-4794) - Finish adding json protocol to the php cross test +- [THRIFT-4969](https://issues.apache.org/jira/browse/THRIFT-4969) - PHP test doesn't check the code generation with php:classmap + +### Tutorial + +- [THRIFT-4426](https://issues.apache.org/jira/browse/THRIFT-4426) - repository should not include symbolic links +- [THRIFT-4965](https://issues.apache.org/jira/browse/THRIFT-4965) - Perl tutorial server doesn't work due to the lack of use statement +- [THRIFT-4967](https://issues.apache.org/jira/browse/THRIFT-4967) - Node.js tutorial server fails if the zip function invoked +- [THRIFT-4968](https://issues.apache.org/jira/browse/THRIFT-4968) - Makefile.am in the Ruby tutorial refers to Python directory +- [THRIFT-4970](https://issues.apache.org/jira/browse/THRIFT-4970) - PHP tutorial doesn't work with Thrift v0.12.0+ + +### Typescript + +- [THRIFT-4675](https://issues.apache.org/jira/browse/THRIFT-4675) - JS code generators not handling int64 type properly for constants and for TypeScript type mappings + +## 0.12.0 + +Released 2019-JAN-04 + +### New Languages +- Common LISP (cl) +- Swift +- Typescript (nodets) + +### Deprecated Languages +- C++03/C++98 (move to C++11) +- Cocoa (move to Swift) + +### Breaking Changes (since 0.11.0) +- [THRIFT-4529](https://issues.apache.org/jira/browse/THRIFT-4529) - Rust enum variants are now camel-cased instead of uppercased to conform to Rust naming conventions +- [THRIFT-4448](https://issues.apache.org/jira/browse/THRIFT-4448) - Support for golang 1.6 and earlier has been dropped. +- [THRIFT-4474](https://issues.apache.org/jira/browse/THRIFT-4474) - PHP now uses the PSR-4 loader by default instead of class maps. +- [THRIFT-4532](https://issues.apache.org/jira/browse/THRIFT-4532) - method signatures changed in the compiler's t_oop_generator. +- [THRIFT-4648](https://issues.apache.org/jira/browse/THRIFT-4648) - The C (GLib) compiler's handling of namespaces has been improved. + +### Known Issues (Blocker or Critical) +- [THRIFT-4037](https://issues.apache.org/jira/browse/THRIFT-4037) - build: use a single build system for thrift +- [THRIFT-4119](https://issues.apache.org/jira/browse/THRIFT-4119) - build: bootstrap.sh is missing from source tarball +- [THRIFT-3289](https://issues.apache.org/jira/browse/THRIFT-3289) - csharp: socket exhaustion in csharp implementation +- [THRIFT-3029](https://issues.apache.org/jira/browse/THRIFT-3029) - cocoa: Getters for fields defined with uppercase names do not work +- [THRIFT-3325](https://issues.apache.org/jira/browse/THRIFT-3325) - cocoa: Extended services aren't subclasses in generated Cocoa +- [THRIFT-4116](https://issues.apache.org/jira/browse/THRIFT-4116) - cocoa: Thrift de-capitalizes the name of IsSet property in Cocoa +- [THRIFT-3877](https://issues.apache.org/jira/browse/THRIFT-3877) - cpp: the http implementation is not standard; interop with other languages is spotty at best +- [THRIFT-4180](https://issues.apache.org/jira/browse/THRIFT-4180) - cpp: Impossible to build Thrift C++ library for Android (NDK) +- [THRIFT-4384](https://issues.apache.org/jira/browse/THRIFT-4384) - cpp: Using multiple async services simultaneously is not thread-safe +- [THRIFT-3108](https://issues.apache.org/jira/browse/THRIFT-3108) - haskell: Defaulted struct parameters on a service generates invalid Haskell +- [THRIFT-3990](https://issues.apache.org/jira/browse/THRIFT-3990) - nodejs: Exception swallowed by deserialization function +- [THRIFT-4214](https://issues.apache.org/jira/browse/THRIFT-4214) - nodejs: map key treated as hex value in JavaScript +- [THRIFT-4602](https://issues.apache.org/jira/browse/THRIFT-4602) - nodejs: ERROR in ./node_modules/thrift/lib/nodejs/lib/thrift/connection.js Module not found: Error: Can't resolve 'child_process' +- [THRIFT-4639](https://issues.apache.org/jira/browse/THRIFT-4639) - nodejs: Sequence numbering for multiplexed protocol broken +- [THRIFT-1310](https://issues.apache.org/jira/browse/THRIFT-1310) - php: sequence and reconnection management issues +- [THRIFT-1538](https://issues.apache.org/jira/browse/THRIFT-1538) - php: Error during deserialization int64 on 32-bit architecture +- [THRIFT-1580](https://issues.apache.org/jira/browse/THRIFT-1580) - php: thrift type i64 java to php serialize/deserealize not working +- [THRIFT-1950](https://issues.apache.org/jira/browse/THRIFT-1950) - php: PHP gets stuck in infinite loop +- [THRIFT-2954](https://issues.apache.org/jira/browse/THRIFT-2954) - python: sending int or float in a double field breaks the connection +- [THRIFT-4080](https://issues.apache.org/jira/browse/THRIFT-4080) - python: unix sockets can get stuck forever +- [THRIFT-4281](https://issues.apache.org/jira/browse/THRIFT-4281) - python: generated code is out of order and causes load issues +- [THRIFT-4677](https://issues.apache.org/jira/browse/THRIFT-4677) - py3: UnicodeDecideError in Python3 + +### Build Process +- [THRIFT-4067](https://issues.apache.org/jira/browse/THRIFT-4067) - Windows thrift compiler distributed on the apache web site has runtime dependencies +- [THRIFT-4308](https://issues.apache.org/jira/browse/THRIFT-4308) - D language docker images need demios for libevent and openssl fixed to re-enable make cross on dlang +- [THRIFT-4579](https://issues.apache.org/jira/browse/THRIFT-4579) - Use Ubuntu Bionic (18.04 LTS) for CI builds instead of Artful (17.10) +- [THRIFT-4508](https://issues.apache.org/jira/browse/THRIFT-4508) - Define CI operating system coverage rules for the project and (hopefully) simplify CI a little more +- [THRIFT-4397](https://issues.apache.org/jira/browse/THRIFT-4397) - ubuntu install instructions broken on 16.04 +- [THRIFT-4545](https://issues.apache.org/jira/browse/THRIFT-4545) - Appveyor builds are failing due to a haskell / cabal update in chocolatey +- [THRIFT-4452](https://issues.apache.org/jira/browse/THRIFT-4452) - optimize Dockerfile (only onetime apt-get update) +- [THRIFT-4440](https://issues.apache.org/jira/browse/THRIFT-4440) - rm `build/docker/ubuntu-trusty/Dockerfile.orig` +- [THRIFT-4352](https://issues.apache.org/jira/browse/THRIFT-4352) - Ubuntu Artful doesn't appear to be compatible with Thrift and Haxe 3.4.2 +- [THRIFT-4666](https://issues.apache.org/jira/browse/THRIFT-4666) - DLang Client Pool Test fails sporadically +- [THRIFT-4676](https://issues.apache.org/jira/browse/THRIFT-4676) - CL tutorial build fails sporadically +- [THRIFT-4456](https://issues.apache.org/jira/browse/THRIFT-4456) - Make haxelib download quiet so it doesn't blow up the build log +- [THRIFT-4605](https://issues.apache.org/jira/browse/THRIFT-4605) - bootstrap.sh fails if automake=1.16.1 + +### c_glib +- [THRIFT-4648](https://issues.apache.org/jira/browse/THRIFT-4648) - The C (GLib) compiler's handling of namespaces has been improved. +- [THRIFT-4622](https://issues.apache.org/jira/browse/THRIFT-4622) - glibC compilation issue +- [THRIFT-4671](https://issues.apache.org/jira/browse/THRIFT-4671) - c glib is unable to handle client close unexpectedly + +### cl (new language support in 0.12.0) +- [THRIFT-82](https://issues.apache.org/jira/browse/THRIFT-82) - Common Lisp support + +### csharp +- [THRIFT-4558](https://issues.apache.org/jira/browse/THRIFT-4558) - reserved Csharp keywords are not escaped in some cases +- [THRIFT-4637](https://issues.apache.org/jira/browse/THRIFT-4637) - C# async mode generates incorrect code with inherited services +- [THRIFT-4672](https://issues.apache.org/jira/browse/THRIFT-4672) - IAsyncResult style methods not being supported by certain transports leads to issues in mixed ISync/IAsync use cases +- [THRIFT-4539](https://issues.apache.org/jira/browse/THRIFT-4539) - Allow TBufferedTransport to be used as base class +- [THRIFT-4535](https://issues.apache.org/jira/browse/THRIFT-4535) - XML docs; code cleanup (tabs->spaces; String->string) +- [THRIFT-4492](https://issues.apache.org/jira/browse/THRIFT-4492) - protected ExceptionType type member of TApplicationException cannot be accessed +- [THRIFT-4446](https://issues.apache.org/jira/browse/THRIFT-4446) - JSONProtocol Base64 Encoding Trims Padding +- [THRIFT-4455](https://issues.apache.org/jira/browse/THRIFT-4455) - Missing dispose calls in ThreadedServer & ThreadpoolServer +- [THRIFT-4609](https://issues.apache.org/jira/browse/THRIFT-4609) - keep InnerException wherever appropriate +- [THRIFT-4673](https://issues.apache.org/jira/browse/THRIFT-4673) - IAsyncResult not supported by layered transports (buffered/framed) + +### cpp +- [THRIFT-4476](https://issues.apache.org/jira/browse/THRIFT-4476) - Typecasting problem on list items +- [THRIFT-4465](https://issues.apache.org/jira/browse/THRIFT-4465) - TNonblockingServer throwing THRIFT LOGGER: TConnection::workSocket(): THRIFT_EAGAIN (unavailable resources) +- [THRIFT-4680](https://issues.apache.org/jira/browse/THRIFT-4680) - TBufferTransports.h does not compile under Visual Studio 2017 +- [THRIFT-4618](https://issues.apache.org/jira/browse/THRIFT-4618) - TNonblockingServer crash because of limitation of select() +- [THRIFT-4620](https://issues.apache.org/jira/browse/THRIFT-4620) - TZlibTransport.cpp doesn't ensure that there is enough space for the zlib flush marker in the buffer. +- [THRIFT-4571](https://issues.apache.org/jira/browse/THRIFT-4571) - ZeroMQ contrib library needs a refresh +- [THRIFT-4559](https://issues.apache.org/jira/browse/THRIFT-4559) - TSSLServerSocket incorrectly prints errors +- [THRIFT-4578](https://issues.apache.org/jira/browse/THRIFT-4578) - Move `TAsyncProtocolProcessor` into main thrift library +- [THRIFT-4418](https://issues.apache.org/jira/browse/THRIFT-4418) - evhttp_connection_new is deprecated; use evhttp_connection_base_new + +### compiler +- [THRIFT-4644](https://issues.apache.org/jira/browse/THRIFT-4644) - Compiler cannot be compiled on macOS(maybe also on other platforms with clang) +- [THRIFT-4531](https://issues.apache.org/jira/browse/THRIFT-4531) - Thrift generates wrong Python code for immutable structures with optional members +- [THRIFT-4513](https://issues.apache.org/jira/browse/THRIFT-4513) - thrift generated code is not stable for constants +- [THRIFT-4532](https://issues.apache.org/jira/browse/THRIFT-4532) - Avoid updating Thrift compiler generated code if the output has not changed +- [THRIFT-4400](https://issues.apache.org/jira/browse/THRIFT-4400) - Visual Studio Compiler project should link runtime statically in release builds +- [THRIFT-4399](https://issues.apache.org/jira/browse/THRIFT-4399) - plugin.thrift t_const_value is not used as a union in C++ code -- fix this +- [THRIFT-4496](https://issues.apache.org/jira/browse/THRIFT-4496) - Dealing with language keywords in Thrift (e.g. service method names) +- [THRIFT-4393](https://issues.apache.org/jira/browse/THRIFT-4393) - repeated runs of compiler produce different binary output at plugin interface + +### dlang +- [THRIFT-4478](https://issues.apache.org/jira/browse/THRIFT-4478) - Thrift will not build with dlang 2.078 or later +- [THRIFT-4503](https://issues.apache.org/jira/browse/THRIFT-4503) - dlang servers logError on normal client disconnection +- [THRIFT-4308](https://issues.apache.org/jira/browse/THRIFT-4308) - D language docker images need demios for libevent and openssl fixed to re-enable make cross on dlang + +### dart +- [THRIFT-4646](https://issues.apache.org/jira/browse/THRIFT-4646) - Effective Dart and Exceptions +- [THRIFT-4439](https://issues.apache.org/jira/browse/THRIFT-4439) - Shouldn't download dart.deb directly. + +### delphi +- [THRIFT-4562](https://issues.apache.org/jira/browse/THRIFT-4562) - Calling wrong exception CTOR leads to "call failed: unknown result" instead of the real exception being thrown +- [THRIFT-4554](https://issues.apache.org/jira/browse/THRIFT-4554) - uncompileable code with member names that are also types under specific conditions +- [THRIFT-4422](https://issues.apache.org/jira/browse/THRIFT-4422) - Add Async implementation via IFuture +- [THRIFT-4485](https://issues.apache.org/jira/browse/THRIFT-4485) - Possible invalid ptr AV with overlapped read/write on pipes +- [THRIFT-4549](https://issues.apache.org/jira/browse/THRIFT-4549) - Thrift exceptions should derive from TException +- [THRIFT-4540](https://issues.apache.org/jira/browse/THRIFT-4540) - buffered transport broken when trying to re-open a formerly closed transport +- [THRIFT-4473](https://issues.apache.org/jira/browse/THRIFT-4473) - Move Thrift.Console.pas out of the Library +- [THRIFT-4490](https://issues.apache.org/jira/browse/THRIFT-4490) - Allow a default service as fallback for multiplex processors connected by old clients +- [THRIFT-4454](https://issues.apache.org/jira/browse/THRIFT-4454) - Large writes/reads may cause range check errors in debug mode +- [THRIFT-4461](https://issues.apache.org/jira/browse/THRIFT-4461) - Compiler directive should match Delphi XE4 +- [THRIFT-4462](https://issues.apache.org/jira/browse/THRIFT-4462) - First line in Console duplicated +- [THRIFT-4642](https://issues.apache.org/jira/browse/THRIFT-4642) - FPU ctrl word settings may cause an unexpected "denormalized" error +- [THRIFT-4589](https://issues.apache.org/jira/browse/THRIFT-4589) - HTTP client timeouts are a) incomplete and b) not used at all +- [THRIFT-4590](https://issues.apache.org/jira/browse/THRIFT-4590) - running the test client using HTTP transport leads to "CoInitialize not called" + +### erlang +- [THRIFT-4497](https://issues.apache.org/jira/browse/THRIFT-4497) - Erlang records should use map() for map type +- [THRIFT-4495](https://issues.apache.org/jira/browse/THRIFT-4495) - Erlang records should allow 'undefined' for non-required fields +- [THRIFT-4580](https://issues.apache.org/jira/browse/THRIFT-4580) - Fix erlang tutorial unpack on Windows +- [THRIFT-4582](https://issues.apache.org/jira/browse/THRIFT-4582) - Ubuntu Xenial erlang 18.3 "make check" fails + +### golang +- [THRIFT-4448](https://issues.apache.org/jira/browse/THRIFT-4448) - Support for golang 1.6 and earlier has been dropped. +- [THRIFT-4253](https://issues.apache.org/jira/browse/THRIFT-4253) - Go generator assigns strings to field in const instead of pointers. +- [THRIFT-4573](https://issues.apache.org/jira/browse/THRIFT-4573) - Unions Field Count Does Not Consider Binary +- [THRIFT-4447](https://issues.apache.org/jira/browse/THRIFT-4447) - Golang: Panic on p.c.Call when using deprecated initializers +- [THRIFT-4650](https://issues.apache.org/jira/browse/THRIFT-4650) - Required field incorrectly marked as set when fieldType does not match +- [THRIFT-4486](https://issues.apache.org/jira/browse/THRIFT-4486) - Golang: -remote.go client cleanup +- [THRIFT-4537](https://issues.apache.org/jira/browse/THRIFT-4537) - TSimpleServer can exit Accept loop with lock still acquired +- [THRIFT-4516](https://issues.apache.org/jira/browse/THRIFT-4516) - Add support for go 1.10 +- [THRIFT-4421](https://issues.apache.org/jira/browse/THRIFT-4421) - golang tests rely on gomock, which has change behaviour, causing tests to fail +- [THRIFT-4626](https://issues.apache.org/jira/browse/THRIFT-4626) - Communication crash when using binary/compact protocol and zlib transport +- [THRIFT-4659](https://issues.apache.org/jira/browse/THRIFT-4659) - golang race detected when closing listener socket + +### haskell +- [THRIFT-4634](https://issues.apache.org/jira/browse/THRIFT-4634) - Haskell builds with older cabal cannot reconcile complex version requirements + +### java +- [THRIFT-4259](https://issues.apache.org/jira/browse/THRIFT-4259) - Thrift does not compile due to Ant Maven task errors +- [THRIFT-1418](https://issues.apache.org/jira/browse/THRIFT-1418) - Compiling Thrift from source: Class org.apache.tools.ant.taskdefs.ConditionTask doesn't support the nested "typefound" element +- [THRIFT-4530](https://issues.apache.org/jira/browse/THRIFT-4530) - proposal: add nullability annotations to generated Java code +- [THRIFT-4614](https://issues.apache.org/jira/browse/THRIFT-4614) - Generate missing @Nullable annotations for Java iterator getters +- [THRIFT-4555](https://issues.apache.org/jira/browse/THRIFT-4555) - Getter of binary field in Java creates unnecessary copy +- [THRIFT-3983](https://issues.apache.org/jira/browse/THRIFT-3983) - libthrift is deployed on central with pom packaging instead of jar +- [THRIFT-4294](https://issues.apache.org/jira/browse/THRIFT-4294) - Java Configure Fails for Ant >= 1.10 +- [THRIFT-4178](https://issues.apache.org/jira/browse/THRIFT-4178) - Java libraries missing from package when using cmake +- [THRIFT-4120](https://issues.apache.org/jira/browse/THRIFT-4120) - pom files are not generated or provided in the build +- [THRIFT-1507](https://issues.apache.org/jira/browse/THRIFT-1507) - Maven can't download resource from central when behind a proxy and won't use local repository +- [THRIFT-4556](https://issues.apache.org/jira/browse/THRIFT-4556) - Optional rethrow of unhandled exceptions in java processor +- [THRIFT-4337](https://issues.apache.org/jira/browse/THRIFT-4337) - Able to set keyStore and trustStore as InputStream in the TSSLTransportFactory.TSSLTransportParameters +- [THRIFT-4566](https://issues.apache.org/jira/browse/THRIFT-4566) - Pass message of unhandled exception to optional rethrow. +- [THRIFT-4506](https://issues.apache.org/jira/browse/THRIFT-4506) - Remove assertion in Java SASL code that would be ignored in release builds +- [THRIFT-4470](https://issues.apache.org/jira/browse/THRIFT-4470) - Include popular IDE file templates to gitignore +- [THRIFT-4429](https://issues.apache.org/jira/browse/THRIFT-4429) - Make TThreadPoolServer.executorService_ available in inherited classes and refactor methods to be able customization +- [THRIFT-3769](https://issues.apache.org/jira/browse/THRIFT-3769) - Fix logic of THRIFT-2268 +- [THRIFT-4494](https://issues.apache.org/jira/browse/THRIFT-4494) - Increase Java Socket Buffer Size +- [THRIFT-4499](https://issues.apache.org/jira/browse/THRIFT-4499) - Remove Magic Number In TFIleTransport + +### js +- [THRIFT-4406](https://issues.apache.org/jira/browse/THRIFT-4406) - JavaScript: Use modern Promise implementations +- [THRIFT-4625](https://issues.apache.org/jira/browse/THRIFT-4625) - let / const variable decorators for es6 compiler +- [THRIFT-4653](https://issues.apache.org/jira/browse/THRIFT-4653) - ES6 Classes +- [THRIFT-4592](https://issues.apache.org/jira/browse/THRIFT-4592) - JS: readI32 performance on large arrays is very poor in Chrome +- [THRIFT-4509](https://issues.apache.org/jira/browse/THRIFT-4509) - js and nodejs libraries need to be refreshed with current libraries +- [THRIFT-4403](https://issues.apache.org/jira/browse/THRIFT-4403) - thrift.js: Incorrect usage of 'this' in TWebSocketTransport.__onOpen +- [THRIFT-4436](https://issues.apache.org/jira/browse/THRIFT-4436) - Deserialization of nested list discards content +- [THRIFT-4437](https://issues.apache.org/jira/browse/THRIFT-4437) - JS WebSocket client callbacks invoked twice on parallel requests +- [THRIFT-4679](https://issues.apache.org/jira/browse/THRIFT-4679) - Duplicate declaration of InputBufferUnderrunError in lib/nodejs/lib/thrift/json_protocol.js +- [THRIFT-4551](https://issues.apache.org/jira/browse/THRIFT-4551) - Add prettier for consistent JS code formatting + +### lua +- [THRIFT-4591](https://issues.apache.org/jira/browse/THRIFT-4591) - lua client uses two write() calls per framed message send +- [THRIFT-3863](https://issues.apache.org/jira/browse/THRIFT-3863) - Can't "make install" Lua Library + +### netcore +- [THRIFT-4524](https://issues.apache.org/jira/browse/THRIFT-4524) - .NET Core Server doesn't close properly when cancelled +- [THRIFT-4434](https://issues.apache.org/jira/browse/THRIFT-4434) - Update .NET Core components, add tests for .Net Core library and .Net Core compiler, fix bugs and build process +- [THRIFT-4446](https://issues.apache.org/jira/browse/THRIFT-4446) - JSONProtocol Base64 Encoding Trims Padding + +### node.js +- [THRIFT-4225](https://issues.apache.org/jira/browse/THRIFT-4225) - Error handling malformed arguments leaks memory, corrupts transport buffers causing next RPC to fail +- [THRIFT-3950](https://issues.apache.org/jira/browse/THRIFT-3950) - Memory leak while calling oneway method +- [THRIFT-3143](https://issues.apache.org/jira/browse/THRIFT-3143) - add typescript directory support +- [THRIFT-4564](https://issues.apache.org/jira/browse/THRIFT-4564) - TBufferedTransport can leave corrupt data in the buffer +- [THRIFT-4647](https://issues.apache.org/jira/browse/THRIFT-4647) - Node.js Fileserver webroot path +- [THRIFT-4489](https://issues.apache.org/jira/browse/THRIFT-4489) - Unix domain socket support for NodeJS client +- [THRIFT-4443](https://issues.apache.org/jira/browse/THRIFT-4443) - node.js json_protocol throws error in skip function +- [THRIFT-4604](https://issues.apache.org/jira/browse/THRIFT-4604) - NodeJS: Expose Int64 from browser.js for consumption by browser +- [THRIFT-4480](https://issues.apache.org/jira/browse/THRIFT-4480) - NodeJS warning on binary_protocol writeMessageEnd when seqid = 0 + +### perl +- [THRIFT-4382](https://issues.apache.org/jira/browse/THRIFT-4382) - Replace the use of Perl Indirect Object Syntax calls to new() +- [THRIFT-4471](https://issues.apache.org/jira/browse/THRIFT-4471) - Thrift CPAN release is missing Makefile.PL and the clients are unable to build the module +- [THRIFT-4416](https://issues.apache.org/jira/browse/THRIFT-4416) - Perl CPAN Packaging Improvements + +### php +- [THRIFT-4474](https://issues.apache.org/jira/browse/THRIFT-4474) - PHP generator use PSR-4 default +- [THRIFT-4463](https://issues.apache.org/jira/browse/THRIFT-4463) - PHP generated code match PSR-2 +- [THRIFT-4373](https://issues.apache.org/jira/browse/THRIFT-4373) - Extending Thrift class results in "Attempt serialize from non-Thrift object" +- [THRIFT-4354](https://issues.apache.org/jira/browse/THRIFT-4354) - TSocket block on read +- [THRIFT-4423](https://issues.apache.org/jira/browse/THRIFT-4423) - migrate php library to psr-4 +- [THRIFT-4656](https://issues.apache.org/jira/browse/THRIFT-4656) - infinite loop in latest PHP library +- [THRIFT-4477](https://issues.apache.org/jira/browse/THRIFT-4477) - TBufferedTransport must have underlying transport +- [THRIFT-4475](https://issues.apache.org/jira/browse/THRIFT-4475) - lib/php/test should be checked for PSR-2 +- [THRIFT-4498](https://issues.apache.org/jira/browse/THRIFT-4498) - add phpcs back +- [THRIFT-4460](https://issues.apache.org/jira/browse/THRIFT-4460) - php library use PSR-2 +- [THRIFT-4641](https://issues.apache.org/jira/browse/THRIFT-4641) - TCurlClient doesn't check for HTTP status code +- [THRIFT-4645](https://issues.apache.org/jira/browse/THRIFT-4645) - TCurlClient: show actual error message when throwing TTransportException +- [THRIFT-4674](https://issues.apache.org/jira/browse/THRIFT-4674) - Add stream context support into PHP/THttpClient +- [THRIFT-4459](https://issues.apache.org/jira/browse/THRIFT-4459) - reduce php library directory depth + +### python +- [THRIFT-4670](https://issues.apache.org/jira/browse/THRIFT-4670) - Twisted, slots, and void method fails with "object has no attribute 'success'" +- [THRIFT-4464](https://issues.apache.org/jira/browse/THRIFT-4464) - Potentially server-crashing typo in Python TNonblockingServer +- [THRIFT-4548](https://issues.apache.org/jira/browse/THRIFT-4548) - Supporting TBinaryProtocolAccelerated protocol when using TMultiplexedProcessor in Python +- [THRIFT-4577](https://issues.apache.org/jira/browse/THRIFT-4577) - Outdated cipher string in python unit test +- [THRIFT-4505](https://issues.apache.org/jira/browse/THRIFT-4505) - python build on Vagrant Windows boxes fails +- [THRIFT-4621](https://issues.apache.org/jira/browse/THRIFT-4621) - THeader for Python +- [THRIFT-4668](https://issues.apache.org/jira/browse/THRIFT-4668) - make socket backlog configurable for python +- [THRIFT-4561](https://issues.apache.org/jira/browse/THRIFT-4561) - Python: cleanup socket timeout settings + +### ruby +- [THRIFT-4289](https://issues.apache.org/jira/browse/THRIFT-4289) - Thrift RSpec test suite fails with Ruby 2.4.x due to Fixnum deprecation +- [THRIFT-4342](https://issues.apache.org/jira/browse/THRIFT-4342) - Support ruby rspec 3 +- [THRIFT-4525](https://issues.apache.org/jira/browse/THRIFT-4525) - Add ssl socket option to ruby cross tests +- [THRIFT-4450](https://issues.apache.org/jira/browse/THRIFT-4450) - Add seek support to TCompactInputProtocol in Rust +- [THRIFT-4631](https://issues.apache.org/jira/browse/THRIFT-4631) - Codegen Creates Invalid Ruby for Recursive Structs +- [THRIFT-4472](https://issues.apache.org/jira/browse/THRIFT-4472) - Fix the genspec for ruby so it does not complain about an invalid license + +### rust +- [THRIFT-4662](https://issues.apache.org/jira/browse/THRIFT-4662) - Rust const string calls function at compile time +- [THRIFT-4661](https://issues.apache.org/jira/browse/THRIFT-4661) - Rust enum name wrong case in generated structs +- [THRIFT-4617](https://issues.apache.org/jira/browse/THRIFT-4617) - Avoid generating conflicting struct names in Rust code +- [THRIFT-4529](https://issues.apache.org/jira/browse/THRIFT-4529) - Rust generation should include #![allow(non_snake_case)] or force conform to Rust style guidelines +- [THRIFT-4390](https://issues.apache.org/jira/browse/THRIFT-4390) - Rust binary protocol and buffered transport cannot handle writes above 4096 bytes +- [THRIFT-4419](https://issues.apache.org/jira/browse/THRIFT-4419) - Rust framed transport cannot handle writes above 4096 bytes +- [THRIFT-4658](https://issues.apache.org/jira/browse/THRIFT-4658) - Rust's TBinaryInputProtocol fails when strict is false +- [THRIFT-4187](https://issues.apache.org/jira/browse/THRIFT-4187) - Dart -> Rust Framed cross tests fail +- [THRIFT-4664](https://issues.apache.org/jira/browse/THRIFT-4664) - Rust cannot create ReadHalf/WriteHalf to implement custom tranports +- [THRIFT-4665](https://issues.apache.org/jira/browse/THRIFT-4665) - Keep Rust library up-to-date on crates.io + +### swift (new language support in 0.12.0) +- [THRIFT-3773](https://issues.apache.org/jira/browse/THRIFT-3773) - Swift Library + +### test suite +- [THRIFT-4515](https://issues.apache.org/jira/browse/THRIFT-4515) - Gracefully shutdown cross-test servers to fully test teardown +- [THRIFT-4085](https://issues.apache.org/jira/browse/THRIFT-4085) - Add .NET Core to the make cross standard test suite +- [THRIFT-4358](https://issues.apache.org/jira/browse/THRIFT-4358) - Add unix domain sockets in ruby to cross test - code exists + +### typescript (new language support in 0.12.0) +- [THRIFT-3143](https://issues.apache.org/jira/browse/THRIFT-3143) - add typescript directory support + +## 0.11.0 + +Released 2017-DEC-27 + +### Sub-task +- [THRIFT-2733](https://issues.apache.org/jira/browse/THRIFT-2733) - Erlang coding standards +- [THRIFT-2740](https://issues.apache.org/jira/browse/THRIFT-2740) - Perl coding standards +- [THRIFT-3610](https://issues.apache.org/jira/browse/THRIFT-3610) - Streamline exception handling in Python server handler +- [THRIFT-3686](https://issues.apache.org/jira/browse/THRIFT-3686) - Java processor should report internal error on uncaught exception +- [THRIFT-4049](https://issues.apache.org/jira/browse/THRIFT-4049) - Skip() should throw TProtocolException.INVALID_DATA on unknown data types +- [THRIFT-4053](https://issues.apache.org/jira/browse/THRIFT-4053) - Skip() should throw TProtocolException.INVALID_DATA on unknown data types +- [THRIFT-4136](https://issues.apache.org/jira/browse/THRIFT-4136) - Align is_binary() method with is_string() to simplify those checks +- [THRIFT-4137](https://issues.apache.org/jira/browse/THRIFT-4137) - Fix remaining undefined behavior invalid vptr casts in Thrift Compiler +- [THRIFT-4138](https://issues.apache.org/jira/browse/THRIFT-4138) - Fix remaining undefined behavior invalid vptr casts in C++ library +- [THRIFT-4296](https://issues.apache.org/jira/browse/THRIFT-4296) - Fix Ubuntu Xenial build environment for the python language +- [THRIFT-4298](https://issues.apache.org/jira/browse/THRIFT-4298) - Fix Ubuntu Xenial build environment for the go 1.6 language +- [THRIFT-4299](https://issues.apache.org/jira/browse/THRIFT-4299) - Fix Ubuntu Xenial build environment for the D language +- [THRIFT-4300](https://issues.apache.org/jira/browse/THRIFT-4300) - Fix make cross in Ubuntu Xenial docker environment, once all language support issues are fixed +- [THRIFT-4302](https://issues.apache.org/jira/browse/THRIFT-4302) - Fix Ubuntu Xenial make cross testing for lua and php7 +- [THRIFT-4398](https://issues.apache.org/jira/browse/THRIFT-4398) - Update EXTRA_DIST for "make dist" + +### Bug +- [THRIFT-381](https://issues.apache.org/jira/browse/THRIFT-381) - Fail fast if configure detects C++ problems +- [THRIFT-1677](https://issues.apache.org/jira/browse/THRIFT-1677) - MinGW support broken +- [THRIFT-1805](https://issues.apache.org/jira/browse/THRIFT-1805) - Thrift should not swallow ALL exceptions +- [THRIFT-2026](https://issues.apache.org/jira/browse/THRIFT-2026) - Fix TCompactProtocol 64 bit builds +- [THRIFT-2642](https://issues.apache.org/jira/browse/THRIFT-2642) - Recursive structs don't work in python +- [THRIFT-2889](https://issues.apache.org/jira/browse/THRIFT-2889) - stable release 0.9.2, erlang tutorial broken +- [THRIFT-2913](https://issues.apache.org/jira/browse/THRIFT-2913) - Ruby Server Thrift::ThreadPoolServer should serve inside a thread +- [THRIFT-2998](https://issues.apache.org/jira/browse/THRIFT-2998) - Node.js: Missing header from http request +- [THRIFT-3000](https://issues.apache.org/jira/browse/THRIFT-3000) - .NET implementation has trouble with mixed IP modes +- [THRIFT-3281](https://issues.apache.org/jira/browse/THRIFT-3281) - Travis CI build passed but the log says BUILD FAILED +- [THRIFT-3358](https://issues.apache.org/jira/browse/THRIFT-3358) - Makefile:1362: *** missing separator. Stop. +- [THRIFT-3600](https://issues.apache.org/jira/browse/THRIFT-3600) - Make TTwisted server send exception on unexpected handler error +- [THRIFT-3602](https://issues.apache.org/jira/browse/THRIFT-3602) - Make Tornado server send exception on unexpected handler error +- [THRIFT-3657](https://issues.apache.org/jira/browse/THRIFT-3657) - D TFileWriterTransport close should use non-priority send +- [THRIFT-3700](https://issues.apache.org/jira/browse/THRIFT-3700) - Go Map has wrong default value when optional +- [THRIFT-3703](https://issues.apache.org/jira/browse/THRIFT-3703) - Unions Field Count Does Not Consider Map/Set/List Fields +- [THRIFT-3730](https://issues.apache.org/jira/browse/THRIFT-3730) - server log error twice +- [THRIFT-3778](https://issues.apache.org/jira/browse/THRIFT-3778) - go client can not pass method parameter to server of other language if no field_id is given +- [THRIFT-3784](https://issues.apache.org/jira/browse/THRIFT-3784) - thrift-maven-plugin generates invalid include directories for IDL in dependency JARs +- [THRIFT-3801](https://issues.apache.org/jira/browse/THRIFT-3801) - Node Thrift client throws exception with multiplexer and responses that are bigger than a single buffer +- [THRIFT-3821](https://issues.apache.org/jira/browse/THRIFT-3821) - TMemoryBuffer buffer may overflow when resizing +- [THRIFT-3832](https://issues.apache.org/jira/browse/THRIFT-3832) - Thrift version 0.9.3 example on Windows, Visual Studio, linking errors during compiling +- [THRIFT-3847](https://issues.apache.org/jira/browse/THRIFT-3847) - thrift/config.h includes a #define for VERSION which will likely conflict with existing user environment or code +- [THRIFT-3873](https://issues.apache.org/jira/browse/THRIFT-3873) - Fix various build warnings when using Visual Studio +- [THRIFT-3891](https://issues.apache.org/jira/browse/THRIFT-3891) - TNonblockingServer configured with more than one IO threads does not always return from serve() upon stop() +- [THRIFT-3892](https://issues.apache.org/jira/browse/THRIFT-3892) - Thrift uses TLS SNI extension provided by OpenSSL library. Older version of OpenSSL(< 0.9.8f) may create problem because they do not support 'SSL_set_tlsext_host_name()'. +- [THRIFT-3895](https://issues.apache.org/jira/browse/THRIFT-3895) - Build fails using Java 1.8 with Ant < 1.9 +- [THRIFT-3896](https://issues.apache.org/jira/browse/THRIFT-3896) - map data with number string key cannot access that deserialized by php extension +- [THRIFT-3938](https://issues.apache.org/jira/browse/THRIFT-3938) - Python TNonblockingServer does not work with SSL +- [THRIFT-3944](https://issues.apache.org/jira/browse/THRIFT-3944) - TSSLSocket has dead code in checkHandshake +- [THRIFT-3946](https://issues.apache.org/jira/browse/THRIFT-3946) - Java 1.5 compatibility broken for binary fields (java5 option) +- [THRIFT-3960](https://issues.apache.org/jira/browse/THRIFT-3960) - Inherited services in Lua generator are not named correctly +- [THRIFT-3962](https://issues.apache.org/jira/browse/THRIFT-3962) - Ant build.xml broken on Windows for Java library +- [THRIFT-3963](https://issues.apache.org/jira/browse/THRIFT-3963) - Thrift.cabal filename does not match module name +- [THRIFT-3967](https://issues.apache.org/jira/browse/THRIFT-3967) - gobject/gparam.h:166:33: warning: enumerator value for ‘G_PARAM_DEPRECATED’ is not an integer constant expression +- [THRIFT-3968](https://issues.apache.org/jira/browse/THRIFT-3968) - Deserializing empty string/binary fields +- [THRIFT-3974](https://issues.apache.org/jira/browse/THRIFT-3974) - Using clang-3.8 and ThreadSanitizer on the concurrency_test claims bad PThread behavior +- [THRIFT-3984](https://issues.apache.org/jira/browse/THRIFT-3984) - PHP7 extension causes segfault +- [THRIFT-4008](https://issues.apache.org/jira/browse/THRIFT-4008) - broken ci due to upstream dependency versioning break +- [THRIFT-4009](https://issues.apache.org/jira/browse/THRIFT-4009) - Use @implementer instead of implements in TTwisted.py +- [THRIFT-4010](https://issues.apache.org/jira/browse/THRIFT-4010) - Q.fcall messing up with *this* pointer inside called function +- [THRIFT-4011](https://issues.apache.org/jira/browse/THRIFT-4011) - Sets of Thrift structs generate Go code that can't be serialized to JSON +- [THRIFT-4012](https://issues.apache.org/jira/browse/THRIFT-4012) - Python Twisted implementation uses implements, not compatible with Py3 +- [THRIFT-4014](https://issues.apache.org/jira/browse/THRIFT-4014) - align C# meta data in AssemblyInfo.cs +- [THRIFT-4015](https://issues.apache.org/jira/browse/THRIFT-4015) - Fix wrongly spelled "Thirft"s +- [THRIFT-4016](https://issues.apache.org/jira/browse/THRIFT-4016) - testInsanity() impl does not conform to test spec in ThriftTest.thrift +- [THRIFT-4023](https://issues.apache.org/jira/browse/THRIFT-4023) - Skip unexpected field types on read/write +- [THRIFT-4024](https://issues.apache.org/jira/browse/THRIFT-4024) - Skip() should throw on unknown data types +- [THRIFT-4026](https://issues.apache.org/jira/browse/THRIFT-4026) - TSSLSocket doesn't work with Python < 2.7.9 +- [THRIFT-4029](https://issues.apache.org/jira/browse/THRIFT-4029) - Accelerated protocols do not build from thrift-py 0.10.0 on PyPI +- [THRIFT-4031](https://issues.apache.org/jira/browse/THRIFT-4031) - Go plugin generates invalid code for lists of typedef'ed built-in types +- [THRIFT-4033](https://issues.apache.org/jira/browse/THRIFT-4033) - Default build WITH_PLUGIN=ON for all builds results in packaging errors +- [THRIFT-4034](https://issues.apache.org/jira/browse/THRIFT-4034) - CMake doesn't work to build compiler on MacOS +- [THRIFT-4036](https://issues.apache.org/jira/browse/THRIFT-4036) - Add .NET Core environment/build support to the docker image +- [THRIFT-4038](https://issues.apache.org/jira/browse/THRIFT-4038) - socket check: checking an unsigned number against >= 0 never fails +- [THRIFT-4042](https://issues.apache.org/jira/browse/THRIFT-4042) - ExtractionError when using accelerated thrift in a multiprocess test +- [THRIFT-4043](https://issues.apache.org/jira/browse/THRIFT-4043) - thrift perl debian package is placing files in the wrong place +- [THRIFT-4044](https://issues.apache.org/jira/browse/THRIFT-4044) - Build job 17 failing on every pull request; hspec core (haskell) 2.4 issue +- [THRIFT-4046](https://issues.apache.org/jira/browse/THRIFT-4046) - MinGW with gcc 6.2 does not compile on Windows +- [THRIFT-4060](https://issues.apache.org/jira/browse/THRIFT-4060) - Thrift printTo ostream overload mechanism breaks down when types are nested +- [THRIFT-4062](https://issues.apache.org/jira/browse/THRIFT-4062) - Remove debug print from TServiceClient +- [THRIFT-4065](https://issues.apache.org/jira/browse/THRIFT-4065) - Document Perl ForkingServer signal restriction imposed by THRIFT-3848 and remove unnecessary code +- [THRIFT-4068](https://issues.apache.org/jira/browse/THRIFT-4068) - A code comment in Java ServerSocket is wrong around accept() +- [THRIFT-4073](https://issues.apache.org/jira/browse/THRIFT-4073) - enum files are still being generated with unused imports +- [THRIFT-4076](https://issues.apache.org/jira/browse/THRIFT-4076) - Appveyor builds failing because ant 1.9.8 was removed from apache servers +- [THRIFT-4077](https://issues.apache.org/jira/browse/THRIFT-4077) - AI_ADDRCONFIG redefined after recent change to PlatformSocket header +- [THRIFT-4079](https://issues.apache.org/jira/browse/THRIFT-4079) - Generated perl code that returns structures from included thrift files is missing a necessary use clause +- [THRIFT-4087](https://issues.apache.org/jira/browse/THRIFT-4087) - Spurious exception destroying TThreadedServer because of incorrect join() call +- [THRIFT-4102](https://issues.apache.org/jira/browse/THRIFT-4102) - TBufferedTransport performance issue since 0.10.0 +- [THRIFT-4106](https://issues.apache.org/jira/browse/THRIFT-4106) - concurrency_test fails randomly +- [THRIFT-4108](https://issues.apache.org/jira/browse/THRIFT-4108) - c_glib thrift ssl has multiple bugs and deprecated functions +- [THRIFT-4109](https://issues.apache.org/jira/browse/THRIFT-4109) - Configure Script uses string comparison for versions +- [THRIFT-4129](https://issues.apache.org/jira/browse/THRIFT-4129) - C++ TNonblockingServer fd leak when failing to dispatch new connections +- [THRIFT-4131](https://issues.apache.org/jira/browse/THRIFT-4131) - Javascript with WebSocket handles oneway methods wrong +- [THRIFT-4134](https://issues.apache.org/jira/browse/THRIFT-4134) - Fix remaining undefined behavior invalid vptr casts +- [THRIFT-4140](https://issues.apache.org/jira/browse/THRIFT-4140) - Use of non-thread-safe function gmtime() +- [THRIFT-4141](https://issues.apache.org/jira/browse/THRIFT-4141) - Installation of haxe in docker files refers to a redirect link and fails +- [THRIFT-4147](https://issues.apache.org/jira/browse/THRIFT-4147) - Rust: protocol should accept transports with non-static lifetime +- [THRIFT-4148](https://issues.apache.org/jira/browse/THRIFT-4148) - [maven-thrift-plugin] compile error while import a thrift in dependency jar file. +- [THRIFT-4149](https://issues.apache.org/jira/browse/THRIFT-4149) - System.out pollutes log files +- [THRIFT-4154](https://issues.apache.org/jira/browse/THRIFT-4154) - PHP close() of a TSocket needs to close any type of socket +- [THRIFT-4158](https://issues.apache.org/jira/browse/THRIFT-4158) - minor issue in README-MSYS2.md +- [THRIFT-4159](https://issues.apache.org/jira/browse/THRIFT-4159) - Building tests fails on MSYS2 (MinGW64) due to a (small?) linker error +- [THRIFT-4160](https://issues.apache.org/jira/browse/THRIFT-4160) - TNonblocking server fix use of closed/freed connections +- [THRIFT-4161](https://issues.apache.org/jira/browse/THRIFT-4161) - TNonBlocking server using uninitialized event in error paths +- [THRIFT-4162](https://issues.apache.org/jira/browse/THRIFT-4162) - TNonBlocking handling of TSockets in error state is incorrect after fd is closed +- [THRIFT-4164](https://issues.apache.org/jira/browse/THRIFT-4164) - Core in TSSLSocket cleanupOpenSSL when destroying a mutex used by openssl +- [THRIFT-4165](https://issues.apache.org/jira/browse/THRIFT-4165) - C++ build has many warnings under c++03 due to recent changes, cmake needs better platform-independent language level control +- [THRIFT-4166](https://issues.apache.org/jira/browse/THRIFT-4166) - Recent fix to remove boost::lexical_cast usage broke VS2010 +- [THRIFT-4167](https://issues.apache.org/jira/browse/THRIFT-4167) - Missing compile flag +- [THRIFT-4170](https://issues.apache.org/jira/browse/THRIFT-4170) - Support lua 5.1 or earlier properly for object length determination +- [THRIFT-4172](https://issues.apache.org/jira/browse/THRIFT-4172) - node.js tutorial client does not import assert, connection issues are not handled properly +- [THRIFT-4177](https://issues.apache.org/jira/browse/THRIFT-4177) - Java compiler produces deep copy constructor that could make shallow copy instead +- [THRIFT-4184](https://issues.apache.org/jira/browse/THRIFT-4184) - Building on Appveyor: invalid escape sequence \L +- [THRIFT-4185](https://issues.apache.org/jira/browse/THRIFT-4185) - fb303 counter encoding fix +- [THRIFT-4189](https://issues.apache.org/jira/browse/THRIFT-4189) - Framed/buffered transport Dispose() does not dispose the nested transport +- [THRIFT-4193](https://issues.apache.org/jira/browse/THRIFT-4193) - Lower the default maxReadBufferBytes for non-blocking servers +- [THRIFT-4195](https://issues.apache.org/jira/browse/THRIFT-4195) - Compilation to GO produces broken code +- [THRIFT-4196](https://issues.apache.org/jira/browse/THRIFT-4196) - Cannot generate recursive Rust types +- [THRIFT-4204](https://issues.apache.org/jira/browse/THRIFT-4204) - typo in compact spec +- [THRIFT-4206](https://issues.apache.org/jira/browse/THRIFT-4206) - Strings in container fields are not decoded properly with py:dynamic and py:utf8strings +- [THRIFT-4208](https://issues.apache.org/jira/browse/THRIFT-4208) - C# NamedPipesServer not really working in some scenarios +- [THRIFT-4211](https://issues.apache.org/jira/browse/THRIFT-4211) - Fix GError glib management under Thrift +- [THRIFT-4212](https://issues.apache.org/jira/browse/THRIFT-4212) - c_glib flush tries to close SSL even if socket is invalid +- [THRIFT-4213](https://issues.apache.org/jira/browse/THRIFT-4213) - Travis build fails at curl -sSL https://www.npmjs.com/install.sh | sh +- [THRIFT-4215](https://issues.apache.org/jira/browse/THRIFT-4215) - Golang TTransportFactory Pattern Squelches Errors +- [THRIFT-4216](https://issues.apache.org/jira/browse/THRIFT-4216) - Golang Http Clients Do Not Respect User Options +- [THRIFT-4218](https://issues.apache.org/jira/browse/THRIFT-4218) - Set TCP_NODELAY for PHP client socket +- [THRIFT-4219](https://issues.apache.org/jira/browse/THRIFT-4219) - Golang HTTP clients created with Nil buffer +- [THRIFT-4231](https://issues.apache.org/jira/browse/THRIFT-4231) - TJSONProtocol throws unexpected non-Thrift-exception on null strings +- [THRIFT-4232](https://issues.apache.org/jira/browse/THRIFT-4232) - ./configure does bad ant version check +- [THRIFT-4234](https://issues.apache.org/jira/browse/THRIFT-4234) - Travis build fails cross language tests with "Unsupported security protocol type" +- [THRIFT-4237](https://issues.apache.org/jira/browse/THRIFT-4237) - Go TServerSocket Race Conditions +- [THRIFT-4240](https://issues.apache.org/jira/browse/THRIFT-4240) - Go TSimpleServer does not close properly +- [THRIFT-4243](https://issues.apache.org/jira/browse/THRIFT-4243) - Go TSimpleServer race on wait in Stop() method +- [THRIFT-4245](https://issues.apache.org/jira/browse/THRIFT-4245) - Golang TFramedTransport's writeBuffer increases if writes to transport failed +- [THRIFT-4246](https://issues.apache.org/jira/browse/THRIFT-4246) - Sequence number mismatch on multiplexed clients +- [THRIFT-4247](https://issues.apache.org/jira/browse/THRIFT-4247) - Compile fails with openssl 1.1 +- [THRIFT-4248](https://issues.apache.org/jira/browse/THRIFT-4248) - Compile fails - strncpy, memcmp, memset not declared in src/thrift/transport/TSSLSocket.cpp +- [THRIFT-4251](https://issues.apache.org/jira/browse/THRIFT-4251) - Java Epoll Selector Bug +- [THRIFT-4257](https://issues.apache.org/jira/browse/THRIFT-4257) - Typescript async callbacks do not provide the correct types +- [THRIFT-4258](https://issues.apache.org/jira/browse/THRIFT-4258) - Boost/std thread wrapping faultiness +- [THRIFT-4260](https://issues.apache.org/jira/browse/THRIFT-4260) - Go context generation issue. Context is parameter in Interface not in implementation +- [THRIFT-4261](https://issues.apache.org/jira/browse/THRIFT-4261) - Go context generation issue: breaking change in generated code regarding thrift.TProcessorFunction interface +- [THRIFT-4262](https://issues.apache.org/jira/browse/THRIFT-4262) - Invalid binding to InterlockedCompareExchange64() with 64-bit targets +- [THRIFT-4263](https://issues.apache.org/jira/browse/THRIFT-4263) - Fix use after free bug for thrown exceptions +- [THRIFT-4266](https://issues.apache.org/jira/browse/THRIFT-4266) - Erlang library throws during skipping fields of composite type (maps, lists, structs, sets) +- [THRIFT-4268](https://issues.apache.org/jira/browse/THRIFT-4268) - Erlang library emits debugging output in transport layer +- [THRIFT-4273](https://issues.apache.org/jira/browse/THRIFT-4273) - erlang:now/0: Deprecated BIF. +- [THRIFT-4274](https://issues.apache.org/jira/browse/THRIFT-4274) - Python feature tests for SSL/TLS failing +- [THRIFT-4279](https://issues.apache.org/jira/browse/THRIFT-4279) - Wrong path in include directive in generated Thrift sources +- [THRIFT-4283](https://issues.apache.org/jira/browse/THRIFT-4283) - TNamedPipeServer race condition in interrupt +- [THRIFT-4284](https://issues.apache.org/jira/browse/THRIFT-4284) - File contains a NBSP: lib/nodejs/lib/thrift/web_server.js +- [THRIFT-4290](https://issues.apache.org/jira/browse/THRIFT-4290) - C# nullable option generates invalid code for non-required enum field with default value +- [THRIFT-4292](https://issues.apache.org/jira/browse/THRIFT-4292) - TimerManager::remove() is not implemented +- [THRIFT-4307](https://issues.apache.org/jira/browse/THRIFT-4307) - Make ssl-open timeout effective in golang client +- [THRIFT-4312](https://issues.apache.org/jira/browse/THRIFT-4312) - Erlang client cannot connect to Python server: exception error: econnrefused +- [THRIFT-4313](https://issues.apache.org/jira/browse/THRIFT-4313) - Program code of the Erlang tutorial files contain syntax errors +- [THRIFT-4316](https://issues.apache.org/jira/browse/THRIFT-4316) - TByteBuffer.java will read too much data if a previous read returns fewer bytes than requested +- [THRIFT-4319](https://issues.apache.org/jira/browse/THRIFT-4319) - command line switch for "evhttp" incorrectly resolved to anon pipes +- [THRIFT-4323](https://issues.apache.org/jira/browse/THRIFT-4323) - range check errors or NPE in edge cases +- [THRIFT-4324](https://issues.apache.org/jira/browse/THRIFT-4324) - field names can conflict with local vars in generated code +- [THRIFT-4328](https://issues.apache.org/jira/browse/THRIFT-4328) - Travis CI builds are timing out (job 1) and haxe builds are failing since 9/11 +- [THRIFT-4329](https://issues.apache.org/jira/browse/THRIFT-4329) - c_glib Doesn't have a multiplexed processor +- [THRIFT-4331](https://issues.apache.org/jira/browse/THRIFT-4331) - C++: TSSLSockets bug in handling huge messages, bug in handling polling +- [THRIFT-4332](https://issues.apache.org/jira/browse/THRIFT-4332) - Binary protocol has memory leaks +- [THRIFT-4334](https://issues.apache.org/jira/browse/THRIFT-4334) - Perl indentation incorrect when defaulting field attribute to a struct +- [THRIFT-4339](https://issues.apache.org/jira/browse/THRIFT-4339) - Thrift Framed Transport in Erlang crashes server when client disconnects +- [THRIFT-4340](https://issues.apache.org/jira/browse/THRIFT-4340) - Erlang fix a crash on client close +- [THRIFT-4355](https://issues.apache.org/jira/browse/THRIFT-4355) - Javascript indentation incorrect when defaulting field attribute to a struct +- [THRIFT-4356](https://issues.apache.org/jira/browse/THRIFT-4356) - thrift_protocol call Transport cause Segmentation fault +- [THRIFT-4359](https://issues.apache.org/jira/browse/THRIFT-4359) - Haxe compiler looks like it is producing incorrect code for map or set key that is binary type +- [THRIFT-4362](https://issues.apache.org/jira/browse/THRIFT-4362) - Missing size-check can lead to huge memory allocation +- [THRIFT-4364](https://issues.apache.org/jira/browse/THRIFT-4364) - Website contributing guide erroneously recommends submitting patches in JIRA +- [THRIFT-4365](https://issues.apache.org/jira/browse/THRIFT-4365) - Perl generated code uses indirect object syntax, which occasionally causes compilation errors. +- [THRIFT-4367](https://issues.apache.org/jira/browse/THRIFT-4367) - python TProcessor.process is missing "self" +- [THRIFT-4370](https://issues.apache.org/jira/browse/THRIFT-4370) - Ubuntu Artful cppcheck and flake8 are more stringent and causing SCA build job failures +- [THRIFT-4372](https://issues.apache.org/jira/browse/THRIFT-4372) - Pipe write operations across a network are limited to 65,535 bytes per write. +- [THRIFT-4374](https://issues.apache.org/jira/browse/THRIFT-4374) - cannot load thrift_protocol due to undefined symbol: _ZTVN10__cxxabiv120__si_class_type_infoE +- [THRIFT-4375](https://issues.apache.org/jira/browse/THRIFT-4375) - TMemory throw bad_alloc due to counter overflow +- [THRIFT-4376](https://issues.apache.org/jira/browse/THRIFT-4376) - Coverity high impact issue resolution +- [THRIFT-4377](https://issues.apache.org/jira/browse/THRIFT-4377) - haxe. socket handles leak in TSimpleServer +- [THRIFT-4381](https://issues.apache.org/jira/browse/THRIFT-4381) - Wrong isset bitfield value after transmission +- [THRIFT-4385](https://issues.apache.org/jira/browse/THRIFT-4385) - Go remote client -u flag is broken +- [THRIFT-4392](https://issues.apache.org/jira/browse/THRIFT-4392) - compiler/..../plugin.thrift structs mis-ordered blows up ocaml generator +- [THRIFT-4395](https://issues.apache.org/jira/browse/THRIFT-4395) - Unable to build in the ubuntu-xenial docker image: clap 2.28 requires Rust 1.20 +- [THRIFT-4396](https://issues.apache.org/jira/browse/THRIFT-4396) - inconsistent (or plain wrong) version numbers in master/trunk + +### Documentation +- [THRIFT-4157](https://issues.apache.org/jira/browse/THRIFT-4157) - outdated readme about Haxe installation on Linux + +### Improvement +- [THRIFT-105](https://issues.apache.org/jira/browse/THRIFT-105) - make a thrift_spec for a structures with negative tags +- [THRIFT-281](https://issues.apache.org/jira/browse/THRIFT-281) - Cocoa library code needs comments, badly +- [THRIFT-775](https://issues.apache.org/jira/browse/THRIFT-775) - performance improvements for Perl +- [THRIFT-2221](https://issues.apache.org/jira/browse/THRIFT-2221) - Generate c++ code with std::shared_ptr instead of boost::shared_ptr. +- [THRIFT-2364](https://issues.apache.org/jira/browse/THRIFT-2364) - OCaml: Use Oasis exclusively for build process +- [THRIFT-2504](https://issues.apache.org/jira/browse/THRIFT-2504) - TMultiplexedProcessor should allow registering default processor called if no service name is present +- [THRIFT-3207](https://issues.apache.org/jira/browse/THRIFT-3207) - Enable build with OpenSSL 1.1.0 series +- [THRIFT-3272](https://issues.apache.org/jira/browse/THRIFT-3272) - Perl SSL Authentication Support +- [THRIFT-3357](https://issues.apache.org/jira/browse/THRIFT-3357) - Generate EnumSet/EnumMap where elements/keys are enums +- [THRIFT-3369](https://issues.apache.org/jira/browse/THRIFT-3369) - Implement SSL/TLS support on C with c_glib +- [THRIFT-3467](https://issues.apache.org/jira/browse/THRIFT-3467) - Go Maps for Thrift Sets Should Have Values of Type struct{} +- [THRIFT-3580](https://issues.apache.org/jira/browse/THRIFT-3580) - THeader for Haskell +- [THRIFT-3627](https://issues.apache.org/jira/browse/THRIFT-3627) - Missing basic code style consistency of JavaScript. +- [THRIFT-3706](https://issues.apache.org/jira/browse/THRIFT-3706) - There's no support for Multiplexed protocol on c_glib library +- [THRIFT-3766](https://issues.apache.org/jira/browse/THRIFT-3766) - Add getUnderlyingTransport() to TZlibTransport +- [THRIFT-3776](https://issues.apache.org/jira/browse/THRIFT-3776) - Go code from multiple thrift files with the same namespace +- [THRIFT-3823](https://issues.apache.org/jira/browse/THRIFT-3823) - Escape documentation while generating non escaped documetation +- [THRIFT-3854](https://issues.apache.org/jira/browse/THRIFT-3854) - allow users to clear read buffers +- [THRIFT-3859](https://issues.apache.org/jira/browse/THRIFT-3859) - Unix Domain Socket Support in Objective-C +- [THRIFT-3921](https://issues.apache.org/jira/browse/THRIFT-3921) - C++ code should print enums as strings +- [THRIFT-3926](https://issues.apache.org/jira/browse/THRIFT-3926) - There should be an error emitted when http status code is not 200 +- [THRIFT-4007](https://issues.apache.org/jira/browse/THRIFT-4007) - Micro-optimization of TTransport.py +- [THRIFT-4040](https://issues.apache.org/jira/browse/THRIFT-4040) - Add real cause of TNonblockingServerSocket error to exception +- [THRIFT-4064](https://issues.apache.org/jira/browse/THRIFT-4064) - Update node library dependencies +- [THRIFT-4069](https://issues.apache.org/jira/browse/THRIFT-4069) - All perl packages should have proper namespace, version syntax, and use proper thrift exceptions +- [THRIFT-4071](https://issues.apache.org/jira/browse/THRIFT-4071) - Consolidate the Travis CI jobs where possible to put less stress on the Apache Foundation's allocation of CI build slaves +- [THRIFT-4072](https://issues.apache.org/jira/browse/THRIFT-4072) - Add the possibility to send custom headers in TCurlClient +- [THRIFT-4075](https://issues.apache.org/jira/browse/THRIFT-4075) - Better MinGW support for headers-only boost (without thread library) +- [THRIFT-4081](https://issues.apache.org/jira/browse/THRIFT-4081) - Provide a MinGW 64-bit Appveyor CI build for better pull request validation +- [THRIFT-4084](https://issues.apache.org/jira/browse/THRIFT-4084) - Improve SSL security in thrift by adding a make cross client that checks to make sure SSLv3 protocol cannot be negotiated +- [THRIFT-4095](https://issues.apache.org/jira/browse/THRIFT-4095) - Add multiplexed protocol to Travis CI for make cross +- [THRIFT-4099](https://issues.apache.org/jira/browse/THRIFT-4099) - Auto-derive Hash for generated Rust structs +- [THRIFT-4110](https://issues.apache.org/jira/browse/THRIFT-4110) - The debian build files do not produce a "-dbg" package for debug symbols of libthrift0 +- [THRIFT-4114](https://issues.apache.org/jira/browse/THRIFT-4114) - Space after '///' in doc comments +- [THRIFT-4126](https://issues.apache.org/jira/browse/THRIFT-4126) - Validate objects in php extension +- [THRIFT-4130](https://issues.apache.org/jira/browse/THRIFT-4130) - Ensure Apache Http connection is released back to pool after use +- [THRIFT-4151](https://issues.apache.org/jira/browse/THRIFT-4151) - Thrift Mutex Contention Profiling (pthreads) should be disabled by default +- [THRIFT-4176](https://issues.apache.org/jira/browse/THRIFT-4176) - Implement a threaded and threadpool server type for Rust +- [THRIFT-4183](https://issues.apache.org/jira/browse/THRIFT-4183) - Named pipe client blocks forever on Open() when there is no server at the other end +- [THRIFT-4190](https://issues.apache.org/jira/browse/THRIFT-4190) - improve C# TThreadPoolServer defaults +- [THRIFT-4197](https://issues.apache.org/jira/browse/THRIFT-4197) - Implement transparent gzip compression for HTTP transport +- [THRIFT-4198](https://issues.apache.org/jira/browse/THRIFT-4198) - Ruby should log Thrift internal errors to global logger +- [THRIFT-4203](https://issues.apache.org/jira/browse/THRIFT-4203) - thrift server stop gracefully +- [THRIFT-4205](https://issues.apache.org/jira/browse/THRIFT-4205) - c_glib is not linking against glib + gobject +- [THRIFT-4209](https://issues.apache.org/jira/browse/THRIFT-4209) - warning CS0414 in T[TLS]ServerSocket.cs +- [THRIFT-4210](https://issues.apache.org/jira/browse/THRIFT-4210) - include Thrift.45.csproj into CI runs +- [THRIFT-4217](https://issues.apache.org/jira/browse/THRIFT-4217) - HttpClient should support gzip and deflate +- [THRIFT-4222](https://issues.apache.org/jira/browse/THRIFT-4222) - Support Unix Domain Sockets in Golang TServerSocket +- [THRIFT-4233](https://issues.apache.org/jira/browse/THRIFT-4233) - Make THsHaServer.invoker available (get method only) in inherited classes +- [THRIFT-4236](https://issues.apache.org/jira/browse/THRIFT-4236) - Support context in go generated code. +- [THRIFT-4238](https://issues.apache.org/jira/browse/THRIFT-4238) - JSON generator: make annotation-aware +- [THRIFT-4269](https://issues.apache.org/jira/browse/THRIFT-4269) - Don't append '.' to Erlang namespace if it ends in '_'. +- [THRIFT-4270](https://issues.apache.org/jira/browse/THRIFT-4270) - Generate Erlang mapping functions for const maps and lists +- [THRIFT-4275](https://issues.apache.org/jira/browse/THRIFT-4275) - Add support for zope.interface only, apart from twisted support. +- [THRIFT-4285](https://issues.apache.org/jira/browse/THRIFT-4285) - Pull generated send/recv into library to allow behaviour to be customised +- [THRIFT-4287](https://issues.apache.org/jira/browse/THRIFT-4287) - Add c++ compiler "no_skeleton" flag option +- [THRIFT-4288](https://issues.apache.org/jira/browse/THRIFT-4288) - Implement logging levels properly for node.js +- [THRIFT-4295](https://issues.apache.org/jira/browse/THRIFT-4295) - Refresh the Docker image file suite for Ubuntu, Debian, and CentOS +- [THRIFT-4305](https://issues.apache.org/jira/browse/THRIFT-4305) - Emit ddoc for generated items +- [THRIFT-4306](https://issues.apache.org/jira/browse/THRIFT-4306) - Thrift imports not replicated to D service output +- [THRIFT-4315](https://issues.apache.org/jira/browse/THRIFT-4315) - Add default message for TApplicationException +- [THRIFT-4318](https://issues.apache.org/jira/browse/THRIFT-4318) - Delphi performance improvements +- [THRIFT-4325](https://issues.apache.org/jira/browse/THRIFT-4325) - Simplify automake cross compilation by relying on one global THRIFT compiler path +- [THRIFT-4327](https://issues.apache.org/jira/browse/THRIFT-4327) - Improve TimerManager API to allow removing specific task +- [THRIFT-4330](https://issues.apache.org/jira/browse/THRIFT-4330) - Allow unused crates in Rust files +- [THRIFT-4333](https://issues.apache.org/jira/browse/THRIFT-4333) - Erlang tutorial examples are using a different port (9999) +- [THRIFT-4343](https://issues.apache.org/jira/browse/THRIFT-4343) - Change CI builds to use node.js 8.x LTS once available +- [THRIFT-4345](https://issues.apache.org/jira/browse/THRIFT-4345) - Create a docker build environment that uses the minimum supported language levels +- [THRIFT-4346](https://issues.apache.org/jira/browse/THRIFT-4346) - Allow Zlib transport factory to wrap other transports +- [THRIFT-4348](https://issues.apache.org/jira/browse/THRIFT-4348) - Perl HTTP Client custom HTTP headers +- [THRIFT-4350](https://issues.apache.org/jira/browse/THRIFT-4350) - Update netcore build for dotnet 2.0 sdk and make cross validation +- [THRIFT-4351](https://issues.apache.org/jira/browse/THRIFT-4351) - Use Travis CI Build Stages to optimize the CI build +- [THRIFT-4353](https://issues.apache.org/jira/browse/THRIFT-4353) - cannot read via thrift_protocol at server side +- [THRIFT-4378](https://issues.apache.org/jira/browse/THRIFT-4378) - add set stopTimeoutUnit method to TThreadPoolServer + +### New Feature +- [THRIFT-750](https://issues.apache.org/jira/browse/THRIFT-750) - C++ Compiler Virtual Function Option +- [THRIFT-2945](https://issues.apache.org/jira/browse/THRIFT-2945) - Implement support for Rust language +- [THRIFT-3857](https://issues.apache.org/jira/browse/THRIFT-3857) - thrift js:node compiler support an object as parameter not an instance of struct +- [THRIFT-3933](https://issues.apache.org/jira/browse/THRIFT-3933) - Port official C# .NET library for Thrift to C# .NET Core library +- [THRIFT-4039](https://issues.apache.org/jira/browse/THRIFT-4039) - Update of Apache Thrift .Net Core lib +- [THRIFT-4113](https://issues.apache.org/jira/browse/THRIFT-4113) - Provide a buffer transport for reading/writing in memory byte stream + +### Question +- [THRIFT-2956](https://issues.apache.org/jira/browse/THRIFT-2956) - autoconf - possibly undefined macro - AC_PROG_BISON +- [THRIFT-4223](https://issues.apache.org/jira/browse/THRIFT-4223) - Add support to the isServing() method for the C++ library + +### Task +- [THRIFT-3622](https://issues.apache.org/jira/browse/THRIFT-3622) - Fix deprecated uses of std::auto_ptr +- [THRIFT-4028](https://issues.apache.org/jira/browse/THRIFT-4028) - Please remove System.out.format from the source code +- [THRIFT-4186](https://issues.apache.org/jira/browse/THRIFT-4186) - Build and test rust client in Travis + +### Test +- [THRIFT-4264](https://issues.apache.org/jira/browse/THRIFT-4264) - PHP - Support both shared & static linking of sockets library + +### Wish +- [THRIFT-4344](https://issues.apache.org/jira/browse/THRIFT-4344) - Define and maintain the minimum language level for all languages in one place + +## 0.10.0 + +### Bug +- [THRIFT-1840](https://issues.apache.org/jira/browse/THRIFT-1840) - Thrift Generated Code Causes Global Variable Leaks +- [THRIFT-1828](https://issues.apache.org/jira/browse/THRIFT-1828) - moc_TQTcpServer.cpp was removed from source tree but is in thrift-0.9.0.tar.gz +- [THRIFT-1790](https://issues.apache.org/jira/browse/THRIFT-1790) - cocoa: Duplicate interface definition error +- [THRIFT-1776](https://issues.apache.org/jira/browse/THRIFT-1776) - TPipeServer should implement "listen", so that TServerEventHandler preServe will work right +- [THRIFT-1351](https://issues.apache.org/jira/browse/THRIFT-1351) - Compiler does not care about binary strings +- [THRIFT-1229](https://issues.apache.org/jira/browse/THRIFT-1229) - Python fastbinary.c can not handle unicode as generated python code +- [THRIFT-749](https://issues.apache.org/jira/browse/THRIFT-749) - C++ TBufferedTransports do not flush their buffers on delete +- [THRIFT-747](https://issues.apache.org/jira/browse/THRIFT-747) - C++ TSocket->close calls shutdown breaking forked parent process +- [THRIFT-732](https://issues.apache.org/jira/browse/THRIFT-732) - server exits abnormally when client calls send_xxx function without calling recv_xxx function +- [THRIFT-3942](https://issues.apache.org/jira/browse/THRIFT-3942) - TSSLSocket does not honor send and receive timeouts +- [THRIFT-3941](https://issues.apache.org/jira/browse/THRIFT-3941) - WinXP version of thrift_poll() relies on undefined behavior by passing a destructed variable to select() +- [THRIFT-3940](https://issues.apache.org/jira/browse/THRIFT-3940) - Visual Studio project file for compiler is broken +- [THRIFT-3943](https://issues.apache.org/jira/browse/THRIFT-3943) - Coverity Scan identified some high severity defects +- [THRIFT-3929](https://issues.apache.org/jira/browse/THRIFT-3929) - PHP "nsglobal" Option Results in Syntax Error in Generated Code (Trailing Backslash) +- [THRIFT-3936](https://issues.apache.org/jira/browse/THRIFT-3936) - Cannot compile 0.10.0 development tip with VS2013 and earlier (snprintf, uint32_t) +- [THRIFT-3935](https://issues.apache.org/jira/browse/THRIFT-3935) - Incorrect skipping of map and set +- [THRIFT-3920](https://issues.apache.org/jira/browse/THRIFT-3920) - Ruby: Ensuring that HTTP failures will clear the http transport outbuf var +- [THRIFT-3919](https://issues.apache.org/jira/browse/THRIFT-3919) - C# TTLSServerSocket does not use clientTimeout +- [THRIFT-3917](https://issues.apache.org/jira/browse/THRIFT-3917) - Check backports.ssl_match_hostname module version +- [THRIFT-3909](https://issues.apache.org/jira/browse/THRIFT-3909) - Fix c_glib static lib CMake build +- [THRIFT-3904](https://issues.apache.org/jira/browse/THRIFT-3904) - Typo in node tutorial leads to wrong transport being used +- [THRIFT-3848](https://issues.apache.org/jira/browse/THRIFT-3848) - As an implementer of a perl socket server, I do not want to have to remember to ignore SIGCHLD for it to work properly +- [THRIFT-3844](https://issues.apache.org/jira/browse/THRIFT-3844) - thrift_protocol cannot compile in 7.0.7 +- [THRIFT-3843](https://issues.apache.org/jira/browse/THRIFT-3843) - integer issues with Haxe PHP targets cause ZigZag encoding to fail +- [THRIFT-3842](https://issues.apache.org/jira/browse/THRIFT-3842) - Dart generates incorrect code for a const struct +- [THRIFT-3841](https://issues.apache.org/jira/browse/THRIFT-3841) - dart compact protocol incorrectly serializes/deserialized doubles +- [THRIFT-3708](https://issues.apache.org/jira/browse/THRIFT-3708) - NameError: global name 'TProtocol' is not defined +- [THRIFT-3704](https://issues.apache.org/jira/browse/THRIFT-3704) - "TConnectedClient died: Could not refill buffer" message shown when using HTTP Server +- [THRIFT-3678](https://issues.apache.org/jira/browse/THRIFT-3678) - Fix javadoc errors on JDK 8 +- [THRIFT-3014](https://issues.apache.org/jira/browse/THRIFT-3014) - AppVeyor support +- [THRIFT-2994](https://issues.apache.org/jira/browse/THRIFT-2994) - Node.js TJSONProtocol cannot be used for object serialization. +- [THRIFT-2974](https://issues.apache.org/jira/browse/THRIFT-2974) - writeToParcel throws NPE for optional enum fields +- [THRIFT-2948](https://issues.apache.org/jira/browse/THRIFT-2948) - Python TJSONProtocol doesn't handle structs with binary fields containing invalid unicode. +- [THRIFT-2845](https://issues.apache.org/jira/browse/THRIFT-2845) - ChildService.Plo: No such file or directory +- [THRIFT-3276](https://issues.apache.org/jira/browse/THRIFT-3276) - Binary data does not decode correctly using the TJSONProtocol when the base64 encoded data is padded. +- [THRIFT-3253](https://issues.apache.org/jira/browse/THRIFT-3253) - Using latest version of D gives deprecation notices +- [THRIFT-2883](https://issues.apache.org/jira/browse/THRIFT-2883) - TTwisted.py, during ConnectionLost processing: exceptions.RuntimeError: dictionary changed size during iteration +- [THRIFT-2019](https://issues.apache.org/jira/browse/THRIFT-2019) - Writing on a disconnected socket on Mac causes SIG PIPE +- [THRIFT-2020](https://issues.apache.org/jira/browse/THRIFT-2020) - Thrift library has some empty files that haven't really been deleted +- [THRIFT-2049](https://issues.apache.org/jira/browse/THRIFT-2049) - Go compiler doesn't build on native Windows +- [THRIFT-2024](https://issues.apache.org/jira/browse/THRIFT-2024) - TServer.cpp warns on 64-bit platforms about truncating an rlim_t into an int +- [THRIFT-2023](https://issues.apache.org/jira/browse/THRIFT-2023) - gettimeofday implementation on Windows errors when no time zone is passed in. +- [THRIFT-2022](https://issues.apache.org/jira/browse/THRIFT-2022) - CoB and dense code generation still uses TR1 bind, even though that doesn't work with clang +- [THRIFT-2027](https://issues.apache.org/jira/browse/THRIFT-2027) - Minor 64-bit and NOMINMAX issues in C++ library +- [THRIFT-2156](https://issues.apache.org/jira/browse/THRIFT-2156) - TServerSocket::listen() is throwing exceptions with misleading information +- [THRIFT-2154](https://issues.apache.org/jira/browse/THRIFT-2154) - Missing #deepCopy should return T +- [THRIFT-3157](https://issues.apache.org/jira/browse/THRIFT-3157) - TBase signature should be TBase, F extends TFieldIdEnum> +- [THRIFT-3156](https://issues.apache.org/jira/browse/THRIFT-3156) - Node TLS: server executes processing logic two full times +- [THRIFT-3154](https://issues.apache.org/jira/browse/THRIFT-3154) - tutorial/py.tornado throw EOF exception +- [THRIFT-3063](https://issues.apache.org/jira/browse/THRIFT-3063) - C++ build -Wunused-parameter warnings on processor_test, TransportTest +- [THRIFT-3056](https://issues.apache.org/jira/browse/THRIFT-3056) - Add string/collection length limits for Python protocol readers +- [THRIFT-3237](https://issues.apache.org/jira/browse/THRIFT-3237) - Fix TNamedPipeServer::createNamedPipe memory leak +- [THRIFT-3233](https://issues.apache.org/jira/browse/THRIFT-3233) - Fix C++ ThreadManager::Impl::removeWorker worker join +- [THRIFT-3232](https://issues.apache.org/jira/browse/THRIFT-3232) - Cannot deserialize json messages created with fieldNamesAsString +- [THRIFT-3206](https://issues.apache.org/jira/browse/THRIFT-3206) - Fix Visual Studio build failure due 'pthread_self': identifier not found +- [THRIFT-3200](https://issues.apache.org/jira/browse/THRIFT-3200) - JS and nodejs do not encode JSON protocol binary fields as base64 +- [THRIFT-3199](https://issues.apache.org/jira/browse/THRIFT-3199) - Exception field has basic metadata +- [THRIFT-3182](https://issues.apache.org/jira/browse/THRIFT-3182) - TFramedTransport is in an invalid state after frame size exception +- [THRIFT-2536](https://issues.apache.org/jira/browse/THRIFT-2536) - new TSocket, uninitialised value reported by valgrind +- [THRIFT-2527](https://issues.apache.org/jira/browse/THRIFT-2527) - Apache Thrift IDL Compiler code generated for Node.js should be jshint clean +- [THRIFT-2519](https://issues.apache.org/jira/browse/THRIFT-2519) - "processor" class is not being generated +- [THRIFT-2431](https://issues.apache.org/jira/browse/THRIFT-2431) - TFileTransportTest fails with "check delta < XXX failed" +- [THRIFT-2708](https://issues.apache.org/jira/browse/THRIFT-2708) - Erlang library does not support "oneway" message type +- [THRIFT-3377](https://issues.apache.org/jira/browse/THRIFT-3377) - Deep copy is actually shallow when using typedef members +- [THRIFT-3376](https://issues.apache.org/jira/browse/THRIFT-3376) - C# and Python JSON protocol double values lose precision +- [THRIFT-3373](https://issues.apache.org/jira/browse/THRIFT-3373) - Various fixes for cross test servers and clients +- [THRIFT-3370](https://issues.apache.org/jira/browse/THRIFT-3370) - errno extern variable redefined. Not compiling for Android +- [THRIFT-3379](https://issues.apache.org/jira/browse/THRIFT-3379) - Potential out of range panic in Go JSON protocols +- [THRIFT-3371](https://issues.apache.org/jira/browse/THRIFT-3371) - Abstract namespace Unix domain sockets broken in C++ +- [THRIFT-3380](https://issues.apache.org/jira/browse/THRIFT-3380) - nodejs: 0.9.2 -> 0.9.3 upgrade breaks Protocol and Transport requires +- [THRIFT-3367](https://issues.apache.org/jira/browse/THRIFT-3367) - Fix bad links to coding_standards.md #634 +- [THRIFT-3401](https://issues.apache.org/jira/browse/THRIFT-3401) - Nested collections emit Objective-C code that cannot compile +- [THRIFT-3403](https://issues.apache.org/jira/browse/THRIFT-3403) - JSON String reader doesn't recognize UTF-16 surrogate pairs +- [THRIFT-3362](https://issues.apache.org/jira/browse/THRIFT-3362) - make check fails for C++ at the SecurityTest +- [THRIFT-3395](https://issues.apache.org/jira/browse/THRIFT-3395) - Cocoa compiler produces corrupt code when boxing enums inside map. +- [THRIFT-3394](https://issues.apache.org/jira/browse/THRIFT-3394) - compiler generates uncompilable code +- [THRIFT-3388](https://issues.apache.org/jira/browse/THRIFT-3388) - hash doesn't work on set/list +- [THRIFT-3391](https://issues.apache.org/jira/browse/THRIFT-3391) - Wrong bool formatting in test server +- [THRIFT-3390](https://issues.apache.org/jira/browse/THRIFT-3390) - TTornado server doesn't handle closed connections properly +- [THRIFT-3382](https://issues.apache.org/jira/browse/THRIFT-3382) - TBase class for C++ Library +- [THRIFT-3392](https://issues.apache.org/jira/browse/THRIFT-3392) - Java TZlibTransport does not close its wrapper streams upon close() +- [THRIFT-3383](https://issues.apache.org/jira/browse/THRIFT-3383) - i64 related warnings +- [THRIFT-3386](https://issues.apache.org/jira/browse/THRIFT-3386) - misc. warnings with make check +- [THRIFT-3385](https://issues.apache.org/jira/browse/THRIFT-3385) - warning: format ‘%lu’ expects ‘long unsigned int’, but has type ‘std::basic_string::size_type {aka unsigned int} +- [THRIFT-3355](https://issues.apache.org/jira/browse/THRIFT-3355) - npm WARN package.json thrift@1.0.0-dev No license field. +- [THRIFT-3360](https://issues.apache.org/jira/browse/THRIFT-3360) - Improve cross test servers and clients further +- [THRIFT-3359](https://issues.apache.org/jira/browse/THRIFT-3359) - Binary field incompatibilities +- [THRIFT-3354](https://issues.apache.org/jira/browse/THRIFT-3354) - Fix word-extraction substr bug in initialism code +- [THRIFT-3350](https://issues.apache.org/jira/browse/THRIFT-3350) - Python JSON protocol does not encode binary as Base64 +- [THRIFT-3577](https://issues.apache.org/jira/browse/THRIFT-3577) - assertion failed at line 512 of testcontainertest.c +- [THRIFT-3576](https://issues.apache.org/jira/browse/THRIFT-3576) - Boost test --log_format arg does not accept lowercase +- [THRIFT-3575](https://issues.apache.org/jira/browse/THRIFT-3575) - Go compiler tries to use unexported library methods when using read_write_private +- [THRIFT-3574](https://issues.apache.org/jira/browse/THRIFT-3574) - Cocoa generator makes uncompilable imports +- [THRIFT-3570](https://issues.apache.org/jira/browse/THRIFT-3570) - Remove duplicate instances that are added by upstream +- [THRIFT-3571](https://issues.apache.org/jira/browse/THRIFT-3571) - Make feature test result browsable +- [THRIFT-3569](https://issues.apache.org/jira/browse/THRIFT-3569) - c_glib protocols do not check number of bytes read by transport +- [THRIFT-3568](https://issues.apache.org/jira/browse/THRIFT-3568) - THeader server crashes on readSlow +- [THRIFT-3567](https://issues.apache.org/jira/browse/THRIFT-3567) - GLib-GObject-CRITICAL **: g_object_unref: assertion 'G_IS_OBJECT (object)' failed +- [THRIFT-3566](https://issues.apache.org/jira/browse/THRIFT-3566) - C++/Qt: TQTcpServerTest::test_communicate() is never executed +- [THRIFT-3564](https://issues.apache.org/jira/browse/THRIFT-3564) - C++/Qt: potential core dump in TQTcpServer in case an exception occurs in TAsyncProcessor::process() +- [THRIFT-3558](https://issues.apache.org/jira/browse/THRIFT-3558) - typos in c_glib tests +- [THRIFT-3559](https://issues.apache.org/jira/browse/THRIFT-3559) - Fix awkward extra semi-colons with Cocoa container literals +- [THRIFT-3555](https://issues.apache.org/jira/browse/THRIFT-3555) - 'configure' script does not honor --with-openssl= for libcrypto for BN_init +- [THRIFT-3554](https://issues.apache.org/jira/browse/THRIFT-3554) - Constant decls may lead to "Error: internal error: prepare_member_name_mapping() already active for different struct" +- [THRIFT-3552](https://issues.apache.org/jira/browse/THRIFT-3552) - glib_c Memory Leak +- [THRIFT-3551](https://issues.apache.org/jira/browse/THRIFT-3551) - Thrift perl library missing package declaration +- [THRIFT-3549](https://issues.apache.org/jira/browse/THRIFT-3549) - Exceptions are not properly stringified in Perl library +- [THRIFT-3546](https://issues.apache.org/jira/browse/THRIFT-3546) - NodeJS code should not be namespaced (and is currently not strict-mode compliant) +- [THRIFT-3545](https://issues.apache.org/jira/browse/THRIFT-3545) - Container type literals do not compile +- [THRIFT-3538](https://issues.apache.org/jira/browse/THRIFT-3538) - Remove UnboundMethodType in TProtocolDecorator +- [THRIFT-3536](https://issues.apache.org/jira/browse/THRIFT-3536) - Error 'char' does not contain a definition for 'IsLowSurrogate' for WP7 target +- [THRIFT-3534](https://issues.apache.org/jira/browse/THRIFT-3534) - Link error when building with Qt5 +- [THRIFT-3533](https://issues.apache.org/jira/browse/THRIFT-3533) - Can not send nil pointer as service method argument +- [THRIFT-3507](https://issues.apache.org/jira/browse/THRIFT-3507) - THttpClient does not use proxy from http_proxy, https_proxy environment variables +- [THRIFT-3502](https://issues.apache.org/jira/browse/THRIFT-3502) - C++ TServerSocket passes small buffer to getsockname +- [THRIFT-3501](https://issues.apache.org/jira/browse/THRIFT-3501) - Forward slash in comment causes compiler error +- [THRIFT-3498](https://issues.apache.org/jira/browse/THRIFT-3498) - C++ library assumes optional function pthread_attr_setschedpolicy is available +- [THRIFT-3497](https://issues.apache.org/jira/browse/THRIFT-3497) - Build fails with "invalid use of incomplete type" +- [THRIFT-3496](https://issues.apache.org/jira/browse/THRIFT-3496) - C++: Cob style client fails when sending a consecutive request +- [THRIFT-3493](https://issues.apache.org/jira/browse/THRIFT-3493) - libthrift does not compile on windows using visual studio +- [THRIFT-3488](https://issues.apache.org/jira/browse/THRIFT-3488) - warning: unused variable 'program' +- [THRIFT-3489](https://issues.apache.org/jira/browse/THRIFT-3489) - warning: deprecated conversion from string constant to 'char*' [-Wwrite-strings] +- [THRIFT-3487](https://issues.apache.org/jira/browse/THRIFT-3487) - Full support for newer Delphi versions +- [THRIFT-3528](https://issues.apache.org/jira/browse/THRIFT-3528) - Fix warnings in thrift.ll +- [THRIFT-3527](https://issues.apache.org/jira/browse/THRIFT-3527) - -gen py:dynamic,utf8strings ignores utf8strings option +- [THRIFT-3526](https://issues.apache.org/jira/browse/THRIFT-3526) - Code generated by py:utf8strings does not work for Python3 +- [THRIFT-3524](https://issues.apache.org/jira/browse/THRIFT-3524) - dcc32 warning "W1000 Symbol 'IsLowSurrogate' is deprecated: 'Use TCharHelper'" in Thrift.Protocol.JSON.pas +- [THRIFT-3525](https://issues.apache.org/jira/browse/THRIFT-3525) - py:dynamic fails to handle binary list/set/map element +- [THRIFT-3521](https://issues.apache.org/jira/browse/THRIFT-3521) - TSimpleJSONProtocolTest is not deterministic (fails when run on JDK 8) +- [THRIFT-3520](https://issues.apache.org/jira/browse/THRIFT-3520) - Dart TSocket onError stream should be typed as Object +- [THRIFT-3519](https://issues.apache.org/jira/browse/THRIFT-3519) - fastbinary does not work with -gen py:utf8strings +- [THRIFT-3518](https://issues.apache.org/jira/browse/THRIFT-3518) - TConcurrentClientSyncInfo files were missing for Visual Studio +- [THRIFT-3512](https://issues.apache.org/jira/browse/THRIFT-3512) - c_glib: Build fails due to missing features.h +- [THRIFT-3483](https://issues.apache.org/jira/browse/THRIFT-3483) - Incorrect empty binary handling introduced by THRIFT-3359 +- [THRIFT-3479](https://issues.apache.org/jira/browse/THRIFT-3479) - Oneway calls should not return exceptions to clients +- [THRIFT-3478](https://issues.apache.org/jira/browse/THRIFT-3478) - Restore dropped method to THsHaServer.java +- [THRIFT-3477](https://issues.apache.org/jira/browse/THRIFT-3477) - Parser fails on enum item that starts with 'E' letter and continues with number +- [THRIFT-3476](https://issues.apache.org/jira/browse/THRIFT-3476) - Missing include in ./src/thrift/protocol/TJSONProtocol.cpp +- [THRIFT-3474](https://issues.apache.org/jira/browse/THRIFT-3474) - Docker: thrift-compiler +- [THRIFT-3473](https://issues.apache.org/jira/browse/THRIFT-3473) - When "optional' is used with a struct member, C++ server seems to not return it correctly +- [THRIFT-3468](https://issues.apache.org/jira/browse/THRIFT-3468) - Dart TSocketTransport onError handler is too restrictive +- [THRIFT-3451](https://issues.apache.org/jira/browse/THRIFT-3451) - thrift_protocol PHP extension missing config.m4 file +- [THRIFT-3456](https://issues.apache.org/jira/browse/THRIFT-3456) - rounding issue in static assert +- [THRIFT-3455](https://issues.apache.org/jira/browse/THRIFT-3455) - struct write method's return value is incorrect +- [THRIFT-3454](https://issues.apache.org/jira/browse/THRIFT-3454) - Python Tornado tutorial is broken +- [THRIFT-3463](https://issues.apache.org/jira/browse/THRIFT-3463) - Java can't be disabled in CMake build +- [THRIFT-3450](https://issues.apache.org/jira/browse/THRIFT-3450) - NPE when using SSL +- [THRIFT-3449](https://issues.apache.org/jira/browse/THRIFT-3449) - TBaseAsyncProcessor fb.responseReady() never called for oneway functions +- [THRIFT-3471](https://issues.apache.org/jira/browse/THRIFT-3471) - Dart generator does not handle uppercase argument names +- [THRIFT-3470](https://issues.apache.org/jira/browse/THRIFT-3470) - Sporadic timeouts with pipes +- [THRIFT-3465](https://issues.apache.org/jira/browse/THRIFT-3465) - Go Code With Complex Const Initializer Compilation Depends On Struct Order +- [THRIFT-3464](https://issues.apache.org/jira/browse/THRIFT-3464) - Fix several defects in c_glib code generator +- [THRIFT-3462](https://issues.apache.org/jira/browse/THRIFT-3462) - Cocoa generates Incorrect #import header names +- [THRIFT-3453](https://issues.apache.org/jira/browse/THRIFT-3453) - remove rat_exclude +- [THRIFT-3418](https://issues.apache.org/jira/browse/THRIFT-3418) - Use of ciphers in ssl.wrap_socket() breaks python 2.6 compatibility +- [THRIFT-3417](https://issues.apache.org/jira/browse/THRIFT-3417) - "namespace xsd" is not really working +- [THRIFT-3413](https://issues.apache.org/jira/browse/THRIFT-3413) - Thrift code generation bug in Go when extending service +- [THRIFT-3420](https://issues.apache.org/jira/browse/THRIFT-3420) - C++: TSSLSockets are not interruptable +- [THRIFT-3415](https://issues.apache.org/jira/browse/THRIFT-3415) - include unistd.h conditionally +- [THRIFT-3414](https://issues.apache.org/jira/browse/THRIFT-3414) - #include in THeaderTransport.h breaks windows build +- [THRIFT-3411](https://issues.apache.org/jira/browse/THRIFT-3411) - Go generates remotes with wrong package qualifiers when including +- [THRIFT-3430](https://issues.apache.org/jira/browse/THRIFT-3430) - Go THttpClient does not read HTTP response body to completion when closing +- [THRIFT-3423](https://issues.apache.org/jira/browse/THRIFT-3423) - First call to thrift_transport:read_exact fails to dispatch correct function +- [THRIFT-3422](https://issues.apache.org/jira/browse/THRIFT-3422) - Go TServerSocket doesn't close on Interrupt +- [THRIFT-3421](https://issues.apache.org/jira/browse/THRIFT-3421) - rebar as dependency instead of bundling (was: rebar fails if PWD contains Unicode) +- [THRIFT-3428](https://issues.apache.org/jira/browse/THRIFT-3428) - Go test fails when running make check +- [THRIFT-3445](https://issues.apache.org/jira/browse/THRIFT-3445) - Throwable messages are hidden from JVM stack trace output +- [THRIFT-3443](https://issues.apache.org/jira/browse/THRIFT-3443) - Thrift include can generate uncompilable code +- [THRIFT-3444](https://issues.apache.org/jira/browse/THRIFT-3444) - Large 64 bit Integer does not preserve value through Node.js JSONProtocol +- [THRIFT-3436](https://issues.apache.org/jira/browse/THRIFT-3436) - misc. cross test issues with UTF-8 path names +- [THRIFT-3435](https://issues.apache.org/jira/browse/THRIFT-3435) - Put generated Java code for fullcamel tests in a separate package/namespace +- [THRIFT-3433](https://issues.apache.org/jira/browse/THRIFT-3433) - Doubles aren't interpreted correctly +- [THRIFT-3437](https://issues.apache.org/jira/browse/THRIFT-3437) - Mingw-w64 build fail +- [THRIFT-3434](https://issues.apache.org/jira/browse/THRIFT-3434) - Dart generator produces empty name in pubspec.yaml for includes without namespaces +- [THRIFT-3408](https://issues.apache.org/jira/browse/THRIFT-3408) - JSON generator emits incorrect types +- [THRIFT-3406](https://issues.apache.org/jira/browse/THRIFT-3406) - Cocoa client should not schedule streams on main runloop +- [THRIFT-3404](https://issues.apache.org/jira/browse/THRIFT-3404) - JSON String reader doesn't recognize UTF-16 surrogate pair +- [THRIFT-3636](https://issues.apache.org/jira/browse/THRIFT-3636) - Double precision is not fully preserved in C++ TJSONProtocol +- [THRIFT-3632](https://issues.apache.org/jira/browse/THRIFT-3632) - c_glib testserialization fails with glib assertion +- [THRIFT-3619](https://issues.apache.org/jira/browse/THRIFT-3619) - Using Thrift 0.9.3 with googletest on Linux gcc 4.9 / C++11 +- [THRIFT-3617](https://issues.apache.org/jira/browse/THRIFT-3617) - CMake does not build gv/xml generators +- [THRIFT-3615](https://issues.apache.org/jira/browse/THRIFT-3615) - Fix Python SSL client resource leak on connection failure +- [THRIFT-3616](https://issues.apache.org/jira/browse/THRIFT-3616) - lib/py/test/test_sslsocket.py is flaky +- [THRIFT-3643](https://issues.apache.org/jira/browse/THRIFT-3643) - Perl SSL server crushes if a client disconnect without handshake +- [THRIFT-3639](https://issues.apache.org/jira/browse/THRIFT-3639) - C# Thrift library forces TLS 1.0, thwarting TLS 1.2 usage +- [THRIFT-3633](https://issues.apache.org/jira/browse/THRIFT-3633) - Travis "C C++ - GCC" build was using clang +- [THRIFT-3634](https://issues.apache.org/jira/browse/THRIFT-3634) - Fix Python TSocket resource leak on connection failure +- [THRIFT-3630](https://issues.apache.org/jira/browse/THRIFT-3630) - Debian/Ubuntu install docs need an update +- [THRIFT-3629](https://issues.apache.org/jira/browse/THRIFT-3629) - Parser sets exitcode on errors, but generator does not +- [THRIFT-3608](https://issues.apache.org/jira/browse/THRIFT-3608) - lib/cpp/test/SecurityTest is flaky in jenkins Thrift-precommit build. +- [THRIFT-3601](https://issues.apache.org/jira/browse/THRIFT-3601) - Better conformance to PEP8 for generated code +- [THRIFT-3599](https://issues.apache.org/jira/browse/THRIFT-3599) - Validate client IP address against cert's SubjectAltName +- [THRIFT-3598](https://issues.apache.org/jira/browse/THRIFT-3598) - TBufferedTransport doesn't instantiate client connection +- [THRIFT-3597](https://issues.apache.org/jira/browse/THRIFT-3597) - `make check` hangs in go tests +- [THRIFT-3589](https://issues.apache.org/jira/browse/THRIFT-3589) - Dart generator uses wrong name in constructor for uppercase arguments with defaults +- [THRIFT-3588](https://issues.apache.org/jira/browse/THRIFT-3588) - Using TypeScript with --noImplicitAny fails +- [THRIFT-3584](https://issues.apache.org/jira/browse/THRIFT-3584) - boolean false value cannot be transferred +- [THRIFT-3578](https://issues.apache.org/jira/browse/THRIFT-3578) - Make THeaderTransport detect TCompact framed and unframed +- [THRIFT-3323](https://issues.apache.org/jira/browse/THRIFT-3323) - Python library does not handle escaped forward slash ("/") in JSON +- [THRIFT-3322](https://issues.apache.org/jira/browse/THRIFT-3322) - CMake generated "make check" failes on python_test +- [THRIFT-3321](https://issues.apache.org/jira/browse/THRIFT-3321) - Thrift can't be added as a subdirectory of another CMake-based project +- [THRIFT-3314](https://issues.apache.org/jira/browse/THRIFT-3314) - Dots in file names of includes causes dots in javascript variable names +- [THRIFT-3307](https://issues.apache.org/jira/browse/THRIFT-3307) - Segfault in Ruby serializer +- [THRIFT-3309](https://issues.apache.org/jira/browse/THRIFT-3309) - Missing TConstant.php in /lib/php/Makefile.am +- [THRIFT-3810](https://issues.apache.org/jira/browse/THRIFT-3810) - unresolved external symbol public: virtual void __cdecl apache::thrift::server::TServerFramework::serve(void) +- [THRIFT-3736](https://issues.apache.org/jira/browse/THRIFT-3736) - C++ library build fails if OpenSSL does not surrpot SSLv3 +- [THRIFT-3878](https://issues.apache.org/jira/browse/THRIFT-3878) - Compile error in TSSLSocket.cpp with new OpenSSL [CRYPTO_num_locks] +- [THRIFT-3949](https://issues.apache.org/jira/browse/THRIFT-3949) - missing make dist entry for compiler/cpp/test +- [THRIFT-449](https://issues.apache.org/jira/browse/THRIFT-449) - The wire format of the JSON Protocol may not always be valid JSON if it contains non-UTF8 encoded strings +- [THRIFT-162](https://issues.apache.org/jira/browse/THRIFT-162) - Thrift structures are unhashable, preventing them from being used as set elements +- [THRIFT-3961](https://issues.apache.org/jira/browse/THRIFT-3961) - TConnectedClient does not terminate the connection to the client if an exception while processing the received message occures. +- [THRIFT-3881](https://issues.apache.org/jira/browse/THRIFT-3881) - Travis CI builds are failing due to docker failures (three retries, and gives up) +- [THRIFT-3937](https://issues.apache.org/jira/browse/THRIFT-3937) - Cannot compile 0.10.0 development tip with gcc-4.6.x +- [THRIFT-3964](https://issues.apache.org/jira/browse/THRIFT-3964) - Unsupported mechanism type ????? due to dependency on default OS-dependent charset +- [THRIFT-3038](https://issues.apache.org/jira/browse/THRIFT-3038) - Use of volatile in cpp library +- [THRIFT-3301](https://issues.apache.org/jira/browse/THRIFT-3301) - Java generated code uses imports that can lead to class name collisions with IDL defined types +- [THRIFT-3348](https://issues.apache.org/jira/browse/THRIFT-3348) - PHP TCompactProtocol bool&int64 readvalue bug +- [THRIFT-3955](https://issues.apache.org/jira/browse/THRIFT-3955) - TThreadedServer Memory Leak +- [THRIFT-3829](https://issues.apache.org/jira/browse/THRIFT-3829) - Thrift does not install Python Libraries if Twisted is not installed +- [THRIFT-3932](https://issues.apache.org/jira/browse/THRIFT-3932) - C++ ThreadManager has a rare termination race +- [THRIFT-3828](https://issues.apache.org/jira/browse/THRIFT-3828) - cmake fails when Boost_INCLUDE_DIRS (and other variables passed to include_directories()) is empty +- [THRIFT-3958](https://issues.apache.org/jira/browse/THRIFT-3958) - CMake WITH_MT option for windows static runtime linking does not support the cmake build type RelWithDebInfo +- [THRIFT-3957](https://issues.apache.org/jira/browse/THRIFT-3957) - TConnectedClient does not disconnect from clients when their timeout is reached. +- [THRIFT-3953](https://issues.apache.org/jira/browse/THRIFT-3953) - TSSLSocket::close should handle exceptions from waitForEvent because it is called by the destructor. +- [THRIFT-3977](https://issues.apache.org/jira/browse/THRIFT-3977) - PHP extension creates undefined values when deserializing sets +- [THRIFT-3947](https://issues.apache.org/jira/browse/THRIFT-3947) - sockaddr type isn't always large enough for the return of getsockname +- [THRIFT-2755](https://issues.apache.org/jira/browse/THRIFT-2755) - ThreadSanitizer reports data race in ThreadManager::Impl::addWorker +- [THRIFT-3948](https://issues.apache.org/jira/browse/THRIFT-3948) - errno is not the correct method of getting the error in windows +- [THRIFT-4008](https://issues.apache.org/jira/browse/THRIFT-4008) - broken ci due to upstream dependency versioning break +- [THRIFT-3999](https://issues.apache.org/jira/browse/THRIFT-3999) - Fix Debian & Ubuntu package dependencies +- [THRIFT-3886](https://issues.apache.org/jira/browse/THRIFT-3886) - PHP cross test client returns 0 even when failing +- [THRIFT-3997](https://issues.apache.org/jira/browse/THRIFT-3997) - building thrift libs does not support new openssl + +### Documentation +- [THRIFT-3867](https://issues.apache.org/jira/browse/THRIFT-3867) - Specify BinaryProtocol and CompactProtocol + +### Epic +- [THRIFT-3049](https://issues.apache.org/jira/browse/THRIFT-3049) - As an iOS developer, I want a generator and library that produces Swift code +- [THRIFT-2336](https://issues.apache.org/jira/browse/THRIFT-2336) - UTF-8 sent by PHP as JSON is not understood by TJsonProtocol + +### Improvement +- [THRIFT-1867](https://issues.apache.org/jira/browse/THRIFT-1867) - Python client/server should support client-side certificates. +- [THRIFT-1313](https://issues.apache.org/jira/browse/THRIFT-1313) - c_glib compact support +- [THRIFT-1385](https://issues.apache.org/jira/browse/THRIFT-1385) - make install doesn't install java library in the setted folder +- [THRIFT-1437](https://issues.apache.org/jira/browse/THRIFT-1437) - Update RPM spec +- [THRIFT-847](https://issues.apache.org/jira/browse/THRIFT-847) - Test Framework harmonization across all languages +- [THRIFT-819](https://issues.apache.org/jira/browse/THRIFT-819) - add Enumeration for protocol, transport and server types +- [THRIFT-3927](https://issues.apache.org/jira/browse/THRIFT-3927) - Emit an error instead of throw an error in the async callback +- [THRIFT-3931](https://issues.apache.org/jira/browse/THRIFT-3931) - TSimpleServer: If process request encounter UNKNOWN_METHOD, don't close transport. +- [THRIFT-3934](https://issues.apache.org/jira/browse/THRIFT-3934) - Automatically resolve OpenSSL binary version on Windows CI +- [THRIFT-3918](https://issues.apache.org/jira/browse/THRIFT-3918) - Run subset of make cross +- [THRIFT-3908](https://issues.apache.org/jira/browse/THRIFT-3908) - Remove redundant dependencies from Dockerfile +- [THRIFT-3907](https://issues.apache.org/jira/browse/THRIFT-3907) - Skip Docker image build on CI when unchanged +- [THRIFT-3868](https://issues.apache.org/jira/browse/THRIFT-3868) - Java struct equals should do identity check before field comparison +- [THRIFT-3849](https://issues.apache.org/jira/browse/THRIFT-3849) - Port Go serializer and deserializer to dart +- [THRIFT-2989](https://issues.apache.org/jira/browse/THRIFT-2989) - Complete CMake build for Apache Thrift +- [THRIFT-2980](https://issues.apache.org/jira/browse/THRIFT-2980) - ThriftMemoryBuffer doesn't have a constructor option to take an existing buffer +- [THRIFT-2856](https://issues.apache.org/jira/browse/THRIFT-2856) - refactor erlang basic transports and unify interfaces +- [THRIFT-2877](https://issues.apache.org/jira/browse/THRIFT-2877) - Optimize generated hashCode +- [THRIFT-2869](https://issues.apache.org/jira/browse/THRIFT-2869) - JSON: run schema validation from tests +- [THRIFT-3112](https://issues.apache.org/jira/browse/THRIFT-3112) - [Java] AsyncMethodCallback should be typed in generated AsyncIface +- [THRIFT-3263](https://issues.apache.org/jira/browse/THRIFT-3263) - PHP jsonSerialize() should cast scalar types +- [THRIFT-2905](https://issues.apache.org/jira/browse/THRIFT-2905) - Cocoa compiler should have option to produce "modern" Objective-C +- [THRIFT-2821](https://issues.apache.org/jira/browse/THRIFT-2821) - Enable the use of custom HTTP-Header in the Transport +- [THRIFT-2093](https://issues.apache.org/jira/browse/THRIFT-2093) - added the ability to set compression level in C++ zlib transport +- [THRIFT-2089](https://issues.apache.org/jira/browse/THRIFT-2089) - Compiler ignores duplicate typenames +- [THRIFT-2056](https://issues.apache.org/jira/browse/THRIFT-2056) - Moved all #include config.h statements to #include +- [THRIFT-2031](https://issues.apache.org/jira/browse/THRIFT-2031) - Make SO_KEEPALIVE configurable for C++ lib +- [THRIFT-2021](https://issues.apache.org/jira/browse/THRIFT-2021) - Improve large binary protocol string performance +- [THRIFT-2028](https://issues.apache.org/jira/browse/THRIFT-2028) - Cleanup threading headers / libraries +- [THRIFT-2014](https://issues.apache.org/jira/browse/THRIFT-2014) - Change C++ lib includes to use style throughout +- [THRIFT-2312](https://issues.apache.org/jira/browse/THRIFT-2312) - travis.yml: build everything +- [THRIFT-1915](https://issues.apache.org/jira/browse/THRIFT-1915) - Multiplexing Services +- [THRIFT-1736](https://issues.apache.org/jira/browse/THRIFT-1736) - Visual Studio top level project files within msvc +- [THRIFT-1735](https://issues.apache.org/jira/browse/THRIFT-1735) - integrate tutorial into regular build +- [THRIFT-1533](https://issues.apache.org/jira/browse/THRIFT-1533) - Make TTransport should be Closeable +- [THRIFT-35](https://issues.apache.org/jira/browse/THRIFT-35) - Move language tests into their appropriate library directory +- [THRIFT-1079](https://issues.apache.org/jira/browse/THRIFT-1079) - Support i64 in AS3 +- [THRIFT-1108](https://issues.apache.org/jira/browse/THRIFT-1108) - SSL support for the Ruby library +- [THRIFT-3856](https://issues.apache.org/jira/browse/THRIFT-3856) - update debian package deependencies +- [THRIFT-3833](https://issues.apache.org/jira/browse/THRIFT-3833) - haxe http server implementation (by embeding into php web server) +- [THRIFT-3839](https://issues.apache.org/jira/browse/THRIFT-3839) - Performance issue with big message deserialization using php extension +- [THRIFT-3820](https://issues.apache.org/jira/browse/THRIFT-3820) - Erlang: Detect OTP >= 18 to use new time correction +- [THRIFT-3816](https://issues.apache.org/jira/browse/THRIFT-3816) - Reduce docker build duration on Travis-CI +- [THRIFT-3815](https://issues.apache.org/jira/browse/THRIFT-3815) - Put appveyor dependency versions to one place +- [THRIFT-3788](https://issues.apache.org/jira/browse/THRIFT-3788) - Compatibility improvements and Win64 support +- [THRIFT-3792](https://issues.apache.org/jira/browse/THRIFT-3792) - Timeouts for anonymous pipes should be configurable +- [THRIFT-3794](https://issues.apache.org/jira/browse/THRIFT-3794) - Split Delphi application, protocol and transport exception subtypes into separate exceptions +- [THRIFT-3774](https://issues.apache.org/jira/browse/THRIFT-3774) - The generated code should have exception_names meta info +- [THRIFT-3762](https://issues.apache.org/jira/browse/THRIFT-3762) - Fix build warnings for deprecated Thrift "byte" fields +- [THRIFT-3756](https://issues.apache.org/jira/browse/THRIFT-3756) - Improve requiredness documentation +- [THRIFT-3761](https://issues.apache.org/jira/browse/THRIFT-3761) - Add debian package for Python3 +- [THRIFT-3742](https://issues.apache.org/jira/browse/THRIFT-3742) - haxe php cli support +- [THRIFT-3733](https://issues.apache.org/jira/browse/THRIFT-3733) - Socket timeout improvements +- [THRIFT-3728](https://issues.apache.org/jira/browse/THRIFT-3728) - http transport for thrift-lua +- [THRIFT-3905](https://issues.apache.org/jira/browse/THRIFT-3905) - Dart compiler does not initialize bool, int, and double properties +- [THRIFT-3911](https://issues.apache.org/jira/browse/THRIFT-3911) - Loosen Ruby dev dependency version requirements +- [THRIFT-3906](https://issues.apache.org/jira/browse/THRIFT-3906) - Run C# tests with make check +- [THRIFT-3900](https://issues.apache.org/jira/browse/THRIFT-3900) - Add Python SSL flags +- [THRIFT-3897](https://issues.apache.org/jira/browse/THRIFT-3897) - Provide meaningful exception type based on WebExceptionStatus in case of timeout +- [THRIFT-3808](https://issues.apache.org/jira/browse/THRIFT-3808) - Missing `DOUBLE` in thrift type enumeration +- [THRIFT-3803](https://issues.apache.org/jira/browse/THRIFT-3803) - Remove "file" attribute from XML generator +- [THRIFT-3660](https://issues.apache.org/jira/browse/THRIFT-3660) - Add V4 mapped address to test client cert's altname +- [THRIFT-3661](https://issues.apache.org/jira/browse/THRIFT-3661) - Use https to download meck in erlang test build +- [THRIFT-3659](https://issues.apache.org/jira/browse/THRIFT-3659) - Check configure result of CMake on CI +- [THRIFT-3667](https://issues.apache.org/jira/browse/THRIFT-3667) - Add TLS SNI support to clients +- [THRIFT-3651](https://issues.apache.org/jira/browse/THRIFT-3651) - Make backports.match_hostname and ipaddress optional +- [THRIFT-3666](https://issues.apache.org/jira/browse/THRIFT-3666) - Build D tutorial as part of Autotools build +- [THRIFT-3665](https://issues.apache.org/jira/browse/THRIFT-3665) - Add D libevent and OpenSSL to docker images +- [THRIFT-3664](https://issues.apache.org/jira/browse/THRIFT-3664) - Remove md5.c +- [THRIFT-3662](https://issues.apache.org/jira/browse/THRIFT-3662) - Add Haskell to debian docker image +- [THRIFT-3711](https://issues.apache.org/jira/browse/THRIFT-3711) - Add D to cross language test +- [THRIFT-3691](https://issues.apache.org/jira/browse/THRIFT-3691) - Run flake8 Python style check on Travis-CI +- [THRIFT-3692](https://issues.apache.org/jira/browse/THRIFT-3692) - (Re)enable Appveyor C++ and Python build +- [THRIFT-3677](https://issues.apache.org/jira/browse/THRIFT-3677) - Improve CMake Java build +- [THRIFT-3679](https://issues.apache.org/jira/browse/THRIFT-3679) - Add stdout log to testBinary in Java test server +- [THRIFT-3718](https://issues.apache.org/jira/browse/THRIFT-3718) - Reduce size of docker image for build environment +- [THRIFT-3698](https://issues.apache.org/jira/browse/THRIFT-3698) - [Travis-CI] Introduce retry to apt commands +- [THRIFT-3127](https://issues.apache.org/jira/browse/THRIFT-3127) - switch -recurse to --recurse and reserve -r +- [THRIFT-3087](https://issues.apache.org/jira/browse/THRIFT-3087) - Pass on errors like "connection closed" +- [THRIFT-3240](https://issues.apache.org/jira/browse/THRIFT-3240) - Thrift Python client should support subjectAltName and wildcard certs in TSSLSocket +- [THRIFT-3213](https://issues.apache.org/jira/browse/THRIFT-3213) - make cross should indicate when it skips a known failing test +- [THRIFT-3208](https://issues.apache.org/jira/browse/THRIFT-3208) - Fix Visual Studio solution build failure due to missing source +- [THRIFT-3186](https://issues.apache.org/jira/browse/THRIFT-3186) - Add TServerHTTP to Go library +- [THRIFT-2342](https://issues.apache.org/jira/browse/THRIFT-2342) - Add __FILE__ and __LINE__ to Thrift C++ excpetions +- [THRIFT-3372](https://issues.apache.org/jira/browse/THRIFT-3372) - Add dart generator to Visual Studio project +- [THRIFT-3366](https://issues.apache.org/jira/browse/THRIFT-3366) - ThriftTest to implement standard return values +- [THRIFT-3402](https://issues.apache.org/jira/browse/THRIFT-3402) - Provide a perl Unix Socket implementation +- [THRIFT-3361](https://issues.apache.org/jira/browse/THRIFT-3361) - Improve C# library +- [THRIFT-3393](https://issues.apache.org/jira/browse/THRIFT-3393) - Introduce i8 to provide consistent set of Thrift IDL integer types +- [THRIFT-3339](https://issues.apache.org/jira/browse/THRIFT-3339) - Support for database/sql +- [THRIFT-3565](https://issues.apache.org/jira/browse/THRIFT-3565) - C++: T[Async]Processor::getEventHandler() should be declared as const member functions +- [THRIFT-3563](https://issues.apache.org/jira/browse/THRIFT-3563) - C++/Qt: removed usage of macro QT_PREPEND_NAMESPACE as it isn't consequently used for all references to Qt types. +- [THRIFT-3562](https://issues.apache.org/jira/browse/THRIFT-3562) - Removed unused TAsyncProcessor::getAsyncServer() +- [THRIFT-3561](https://issues.apache.org/jira/browse/THRIFT-3561) - C++/Qt: make use of Q_DISABLE_COPY() to get rid of copy ctor and assignment operator +- [THRIFT-3556](https://issues.apache.org/jira/browse/THRIFT-3556) - c_glib file descriptor transport +- [THRIFT-3544](https://issues.apache.org/jira/browse/THRIFT-3544) - Make cross test fail when server process died unexpectedly +- [THRIFT-3540](https://issues.apache.org/jira/browse/THRIFT-3540) - Make python tutorial more in line with PEP8 +- [THRIFT-3535](https://issues.apache.org/jira/browse/THRIFT-3535) - Dart generator argument to produce a file structure usable in parent library +- [THRIFT-3505](https://issues.apache.org/jira/browse/THRIFT-3505) - Enhance Python TSSLSocket +- [THRIFT-3506](https://issues.apache.org/jira/browse/THRIFT-3506) - Eliminate old style classes from library code +- [THRIFT-3503](https://issues.apache.org/jira/browse/THRIFT-3503) - Enable py:utf8string by default +- [THRIFT-3499](https://issues.apache.org/jira/browse/THRIFT-3499) - Add package_prefix to python generator +- [THRIFT-3495](https://issues.apache.org/jira/browse/THRIFT-3495) - Minor enhancements and fixes for cross test +- [THRIFT-3486](https://issues.apache.org/jira/browse/THRIFT-3486) - Java generated `getFieldValue` is incompatible with `setFieldValue` for binary values. +- [THRIFT-3484](https://issues.apache.org/jira/browse/THRIFT-3484) - Consolidate temporary buffers in Java's TCompactProtocol +- [THRIFT-3516](https://issues.apache.org/jira/browse/THRIFT-3516) - Add feature test for THeader TBinaryProtocol interop +- [THRIFT-3515](https://issues.apache.org/jira/browse/THRIFT-3515) - Python 2.6 compatibility and test on CI +- [THRIFT-3514](https://issues.apache.org/jira/browse/THRIFT-3514) - PHP 7 compatible version of binary protocol +- [THRIFT-3469](https://issues.apache.org/jira/browse/THRIFT-3469) - Docker: Debian support +- [THRIFT-3416](https://issues.apache.org/jira/browse/THRIFT-3416) - Retire old "xxx_namespace" declarations from the IDL +- [THRIFT-3426](https://issues.apache.org/jira/browse/THRIFT-3426) - Align autogen comment in XSD +- [THRIFT-3424](https://issues.apache.org/jira/browse/THRIFT-3424) - Add CMake android build option +- [THRIFT-3439](https://issues.apache.org/jira/browse/THRIFT-3439) - Run make cross using Python3 when available +- [THRIFT-3440](https://issues.apache.org/jira/browse/THRIFT-3440) - Python make check takes too much time +- [THRIFT-3441](https://issues.apache.org/jira/browse/THRIFT-3441) - Stabilize Travis-CI builds +- [THRIFT-3431](https://issues.apache.org/jira/browse/THRIFT-3431) - Avoid "schemes" HashMap lookups during struct reads/writes +- [THRIFT-3432](https://issues.apache.org/jira/browse/THRIFT-3432) - Add a TByteBuffer transport to the Java library +- [THRIFT-3438](https://issues.apache.org/jira/browse/THRIFT-3438) - Enable py:new_style by default +- [THRIFT-3405](https://issues.apache.org/jira/browse/THRIFT-3405) - Go THttpClient misuses http.Client objects +- [THRIFT-3614](https://issues.apache.org/jira/browse/THRIFT-3614) - Improve logging of test_sslsocket.py +- [THRIFT-3647](https://issues.apache.org/jira/browse/THRIFT-3647) - Fix php extension build warnings +- [THRIFT-3642](https://issues.apache.org/jira/browse/THRIFT-3642) - Speed up cross test runner +- [THRIFT-3637](https://issues.apache.org/jira/browse/THRIFT-3637) - Implement compact protocol for dart +- [THRIFT-3613](https://issues.apache.org/jira/browse/THRIFT-3613) - Port Python C extension to Python 3 +- [THRIFT-3612](https://issues.apache.org/jira/browse/THRIFT-3612) - Add Python C extension for compact protocol +- [THRIFT-3611](https://issues.apache.org/jira/browse/THRIFT-3611) - Add --regex filter to cross test runner +- [THRIFT-3631](https://issues.apache.org/jira/browse/THRIFT-3631) - JSON protocol implementation for Lua +- [THRIFT-3609](https://issues.apache.org/jira/browse/THRIFT-3609) - Remove or replace TestPortFixture.h +- [THRIFT-3605](https://issues.apache.org/jira/browse/THRIFT-3605) - Have the compiler complain about invalid arguments and options +- [THRIFT-3596](https://issues.apache.org/jira/browse/THRIFT-3596) - Better conformance to PEP8 +- [THRIFT-3585](https://issues.apache.org/jira/browse/THRIFT-3585) - Compact protocol implementation for Lua +- [THRIFT-3582](https://issues.apache.org/jira/browse/THRIFT-3582) - Erlang libraries should have service metadata +- [THRIFT-3579](https://issues.apache.org/jira/browse/THRIFT-3579) - Introduce retry to make cross +- [THRIFT-3306](https://issues.apache.org/jira/browse/THRIFT-3306) - Java: TBinaryProtocol: Use 1 temp buffer instead of allocating 8 +- [THRIFT-3910](https://issues.apache.org/jira/browse/THRIFT-3910) - Do not invoke pip as part of build process +- [THRIFT-1857](https://issues.apache.org/jira/browse/THRIFT-1857) - Python 3.X Support +- [THRIFT-1944](https://issues.apache.org/jira/browse/THRIFT-1944) - Binding to zero port +- [THRIFT-3954](https://issues.apache.org/jira/browse/THRIFT-3954) - Enable the usage of structs called "Object" in Java +- [THRIFT-3981](https://issues.apache.org/jira/browse/THRIFT-3981) - Enable analyzer strong mode in Dart library +- [THRIFT-3998](https://issues.apache.org/jira/browse/THRIFT-3998) - Document ability to add custom tags to thrift structs +- [THRIFT-4006](https://issues.apache.org/jira/browse/THRIFT-4006) - Add a removeEventListener method on TSocket + +### New Feature +- [THRIFT-640](https://issues.apache.org/jira/browse/THRIFT-640) - Support deprecation +- [THRIFT-948](https://issues.apache.org/jira/browse/THRIFT-948) - SSL socket support for PHP +- [THRIFT-764](https://issues.apache.org/jira/browse/THRIFT-764) - add Support for Vala language +- [THRIFT-3046](https://issues.apache.org/jira/browse/THRIFT-3046) - Allow PSR4 class loading for generated classes (PHP) +- [THRIFT-2113](https://issues.apache.org/jira/browse/THRIFT-2113) - Erlang SSL Socket Support +- [THRIFT-1482](https://issues.apache.org/jira/browse/THRIFT-1482) - Unix domain socket support under PHP +- [THRIFT-519](https://issues.apache.org/jira/browse/THRIFT-519) - Support collections of types without having to explicitly define it +- [THRIFT-468](https://issues.apache.org/jira/browse/THRIFT-468) - Rack Middleware Application for Rails +- [THRIFT-1708](https://issues.apache.org/jira/browse/THRIFT-1708) - Add event handlers for processor events +- [THRIFT-3834](https://issues.apache.org/jira/browse/THRIFT-3834) - Erlang namespacing and exception metadata +- [THRIFT-2510](https://issues.apache.org/jira/browse/THRIFT-2510) - Implement TNonblockingServer's ability to listen on unix domain sockets +- [THRIFT-3397](https://issues.apache.org/jira/browse/THRIFT-3397) - Implement TProcessorFactory in C# to enable per-client processors +- [THRIFT-3523](https://issues.apache.org/jira/browse/THRIFT-3523) - XML Generator +- [THRIFT-3510](https://issues.apache.org/jira/browse/THRIFT-3510) - Add HttpTaskAsyncHandler implementation +- [THRIFT-3318](https://issues.apache.org/jira/browse/THRIFT-3318) - PHP: SimpleJSONProtocol Implementation +- [THRIFT-3299](https://issues.apache.org/jira/browse/THRIFT-3299) - Dart language bindings in Thrift +- [THRIFT-2835](https://issues.apache.org/jira/browse/THRIFT-2835) - Add possibility to distribute generators separately from thrift core, and load them dynamically +- [THRIFT-184](https://issues.apache.org/jira/browse/THRIFT-184) - Add OSGi Manifest headers to the libthrift java library to be able to use Thrift in the OSGi runtime +- [THRIFT-141](https://issues.apache.org/jira/browse/THRIFT-141) - If a required field is not present on serialization, throw an exception +- [THRIFT-1891](https://issues.apache.org/jira/browse/THRIFT-1891) - Add Windows ALPC transport which is right counterpart of Unix domain sockets + +### Question +- [THRIFT-1808](https://issues.apache.org/jira/browse/THRIFT-1808) - The Thrift struct should be considered self-contained? +- [THRIFT-2895](https://issues.apache.org/jira/browse/THRIFT-2895) - Tutorial cpp +- [THRIFT-3860](https://issues.apache.org/jira/browse/THRIFT-3860) - Elephant-bird application Test fails for Thrift +- [THRIFT-3811](https://issues.apache.org/jira/browse/THRIFT-3811) - HTTPS Support for C++ applications +- [THRIFT-3509](https://issues.apache.org/jira/browse/THRIFT-3509) - "make check" error + +### Story +- [THRIFT-3452](https://issues.apache.org/jira/browse/THRIFT-3452) - .travis.yml: Migrating from legacy to container-based infrastructure + +### Sub-task +- [THRIFT-1811](https://issues.apache.org/jira/browse/THRIFT-1811) - ruby tutorial as part of the regular build +- [THRIFT-2779](https://issues.apache.org/jira/browse/THRIFT-2779) - PHP TJSONProtocol encode unicode into UCS-4LE which can't be parsed by other language bindings +- [THRIFT-2110](https://issues.apache.org/jira/browse/THRIFT-2110) - Erlang: Support for Multiplexing Services on any Transport, Protocol and Server +- [THRIFT-3852](https://issues.apache.org/jira/browse/THRIFT-3852) - A Travis-CI job fails with "write error" +- [THRIFT-3740](https://issues.apache.org/jira/browse/THRIFT-3740) - Fix haxelib.json classpath +- [THRIFT-3653](https://issues.apache.org/jira/browse/THRIFT-3653) - incorrect union serialization +- [THRIFT-3652](https://issues.apache.org/jira/browse/THRIFT-3652) - incorrect serialization of optionals +- [THRIFT-3655](https://issues.apache.org/jira/browse/THRIFT-3655) - incorrect union serialization +- [THRIFT-3654](https://issues.apache.org/jira/browse/THRIFT-3654) - incorrect serialization of optionals +- [THRIFT-3656](https://issues.apache.org/jira/browse/THRIFT-3656) - incorrect serialization of optionals +- [THRIFT-3699](https://issues.apache.org/jira/browse/THRIFT-3699) - Fix integer limit symbol includes in Python C extension +- [THRIFT-3693](https://issues.apache.org/jira/browse/THRIFT-3693) - Fix include issue in C++ TSSLSocketInterruptTest on Windows +- [THRIFT-3694](https://issues.apache.org/jira/browse/THRIFT-3694) - [Windows] Disable tests of a few servers that are not supported +- [THRIFT-3696](https://issues.apache.org/jira/browse/THRIFT-3696) - Install pip to CentOS Docker images to fix Python builds +- [THRIFT-3638](https://issues.apache.org/jira/browse/THRIFT-3638) - Fix haxelib.json +- [THRIFT-3251](https://issues.apache.org/jira/browse/THRIFT-3251) - Add http transport for server to Go lib +- [THRIFT-2424](https://issues.apache.org/jira/browse/THRIFT-2424) - Recursive Types +- [THRIFT-2423](https://issues.apache.org/jira/browse/THRIFT-2423) - THeader +- [THRIFT-2413](https://issues.apache.org/jira/browse/THRIFT-2413) - Python: UTF-8 sent by PHP as JSON is not understood by TJsonProtocol +- [THRIFT-2409](https://issues.apache.org/jira/browse/THRIFT-2409) - Java: UTF-8 sent by PHP as JSON is not understood by TJsonProtocol +- [THRIFT-2412](https://issues.apache.org/jira/browse/THRIFT-2412) - D: UTF-8 sent by PHP as JSON is not understood by TJsonProtocol +- [THRIFT-2411](https://issues.apache.org/jira/browse/THRIFT-2411) - C++: UTF-8 sent by PHP as JSON is not understood by TJsonProtocol +- [THRIFT-2410](https://issues.apache.org/jira/browse/THRIFT-2410) - JavaMe: UTF-8 sent by PHP as JSON is not understood by TJsonProtocol +- [THRIFT-2668](https://issues.apache.org/jira/browse/THRIFT-2668) - TestSuite: detailed result on passed tests by feature +- [THRIFT-2659](https://issues.apache.org/jira/browse/THRIFT-2659) - python Test Server fails when throwing TException +- [THRIFT-3398](https://issues.apache.org/jira/browse/THRIFT-3398) - Add CMake build for Haskell library and tests +- [THRIFT-3396](https://issues.apache.org/jira/browse/THRIFT-3396) - DART: UTF-8 sent by PHP as JSON is not understood by TJsonProtocol +- [THRIFT-3364](https://issues.apache.org/jira/browse/THRIFT-3364) - Fix ruby binary field encoding in TJSONProtocol +- [THRIFT-3381](https://issues.apache.org/jira/browse/THRIFT-3381) - Fix for misc. codegen issues with THRIFT-2905 +- [THRIFT-3573](https://issues.apache.org/jira/browse/THRIFT-3573) - No rule to make target `../../../test/c_glib/src/.deps/testthrifttest-thrift_test_handler.Po'. +- [THRIFT-3572](https://issues.apache.org/jira/browse/THRIFT-3572) - "Unable to determine the behavior of a signed right shift" +- [THRIFT-3542](https://issues.apache.org/jira/browse/THRIFT-3542) - Add length limit support to Java test server +- [THRIFT-3537](https://issues.apache.org/jira/browse/THRIFT-3537) - Remove the (now obsolete) csharp:asyncctp flag +- [THRIFT-3532](https://issues.apache.org/jira/browse/THRIFT-3532) - Add configurable string and container read size limit to Python protocols +- [THRIFT-3531](https://issues.apache.org/jira/browse/THRIFT-3531) - Create cross lang feature test for string and container read length limit +- [THRIFT-3482](https://issues.apache.org/jira/browse/THRIFT-3482) - Haskell JSON protocol does not encode binary field as Base64 +- [THRIFT-3425](https://issues.apache.org/jira/browse/THRIFT-3425) - Minor fixes + simplification for CentOS Dockerfile +- [THRIFT-3442](https://issues.apache.org/jira/browse/THRIFT-3442) - Run CMake tests on Appveyor +- [THRIFT-3409](https://issues.apache.org/jira/browse/THRIFT-3409) - NodeJS binary field issues +- [THRIFT-3621](https://issues.apache.org/jira/browse/THRIFT-3621) - Fix lib/cpp/test/SecurityTest.cpp to use ephemeral ports +- [THRIFT-3628](https://issues.apache.org/jira/browse/THRIFT-3628) - Fix lib/cpp/test/TServerIntegrationTest.cpp to use ephemeral ports +- [THRIFT-3625](https://issues.apache.org/jira/browse/THRIFT-3625) - Kill unused #include "TestPortFixture.h" in lib/cpp/test/TServerTransportTest.cpp. +- [THRIFT-3646](https://issues.apache.org/jira/browse/THRIFT-3646) - Fix Python extension build warnings +- [THRIFT-3626](https://issues.apache.org/jira/browse/THRIFT-3626) - Fix lib/cpp/test/TSocketInterruptTest.cpp to use ephemeral ports. +- [THRIFT-3624](https://issues.apache.org/jira/browse/THRIFT-3624) - Fix lib/cpp/test/TServerSocketTest.cpp to use ephemeral ports +- [THRIFT-3623](https://issues.apache.org/jira/browse/THRIFT-3623) - Fix Fix cpp/lib/test/TSSLSocketInterruptTest.cpp to use ephemeral ports +- [THRIFT-3592](https://issues.apache.org/jira/browse/THRIFT-3592) - Add basic test client +- [THRIFT-3980](https://issues.apache.org/jira/browse/THRIFT-3980) - add TExtendedBinaryProtocol.java + +### Task +- [THRIFT-1801](https://issues.apache.org/jira/browse/THRIFT-1801) - Sync up TApplicationException codes across languages and thrift implementations +- [THRIFT-1259](https://issues.apache.org/jira/browse/THRIFT-1259) - Automate versioning + +### Test +- [THRIFT-3400](https://issues.apache.org/jira/browse/THRIFT-3400) - Add Erlang to cross test +- [THRIFT-3504](https://issues.apache.org/jira/browse/THRIFT-3504) - Fix FastbinaryTest.py + +### Wish +- [THRIFT-3923](https://issues.apache.org/jira/browse/THRIFT-3923) - Maybe remove Aereo from the "Powered by" list +- [THRIFT-2149](https://issues.apache.org/jira/browse/THRIFT-2149) - Add an option to disable the generation of default operators + +## 0.9.3.1 + +Released March 13, 2019 to backport a CVE fix to the popular 0.9.3 release. + +### Bug +- [THRIFT-4506](https://issues.apache.org/jira/browse/THRIFT-4506) - CVE-2018-1320 for Java SASL backported from 0.12.0 + +## 0.9.3 + +### Bug +- [THRIFT-2441](https://issues.apache.org/jira/browse/THRIFT-2441) - Cannot shutdown TThreadedServer when clients are still connected +- [THRIFT-2465](https://issues.apache.org/jira/browse/THRIFT-2465) - TBinaryProtocolT breaks if copied/moved +- [THRIFT-2474](https://issues.apache.org/jira/browse/THRIFT-2474) - thrift.h causes a compile failure +- [THRIFT-2540](https://issues.apache.org/jira/browse/THRIFT-2540) - Running configure from outside the source directory fails +- [THRIFT-2598](https://issues.apache.org/jira/browse/THRIFT-2598) - Add check for minimum Go version to configure.ac +- [THRIFT-2647](https://issues.apache.org/jira/browse/THRIFT-2647) - compiler-hs: don't decapitalize field names, do decapitalize argument bindings +- [THRIFT-2773](https://issues.apache.org/jira/browse/THRIFT-2773) - Generated Java code for 'oneway' methods is incorrect. +- [THRIFT-2789](https://issues.apache.org/jira/browse/THRIFT-2789) - TNonblockingServer leaks socket FD's under load +- [THRIFT-2682](https://issues.apache.org/jira/browse/THRIFT-2682) - TThreadedServer leaks per-thread memory +- [THRIFT-2674](https://issues.apache.org/jira/browse/THRIFT-2674) - JavaScript: declare Accept: and Content-Type: in request +- [THRIFT-3078](https://issues.apache.org/jira/browse/THRIFT-3078) - TNonblockingServerSocket's logger is not named after TNonblockingServerSocket +- [THRIFT-3077](https://issues.apache.org/jira/browse/THRIFT-3077) - C++ TFileTransport ignores return code from ftruncate +- [THRIFT-3067](https://issues.apache.org/jira/browse/THRIFT-3067) - C++ cppcheck performance related warnings +- [THRIFT-3066](https://issues.apache.org/jira/browse/THRIFT-3066) - C++ TDenseProtocol assert modifies instead of checks +- [THRIFT-3071](https://issues.apache.org/jira/browse/THRIFT-3071) - bootstrap.sh on Ubuntu 12.04 (Precise) automake error +- [THRIFT-3069](https://issues.apache.org/jira/browse/THRIFT-3069) - C++ TServerSocket leaks socket on fcntl get or set flags error +- [THRIFT-3079](https://issues.apache.org/jira/browse/THRIFT-3079) - TNonblockingServerSocket's logger is not named after TNonblockingServerSocket +- [THRIFT-3080](https://issues.apache.org/jira/browse/THRIFT-3080) - C++ TNonblockingServer connection leak while accept huge number connections. +- [THRIFT-3086](https://issues.apache.org/jira/browse/THRIFT-3086) - C++ Valgrind Error Cleanup +- [THRIFT-3085](https://issues.apache.org/jira/browse/THRIFT-3085) - thrift_reconnecting_client never try to reconnect +- [THRIFT-3123](https://issues.apache.org/jira/browse/THRIFT-3123) - Missing include in compiler/cpp/src/main.h breaks build in some environments +- [THRIFT-3125](https://issues.apache.org/jira/browse/THRIFT-3125) - Fix the list of exported headers in automake input +- [THRIFT-3126](https://issues.apache.org/jira/browse/THRIFT-3126) - PHP JSON serializer converts empty or int-indexed maps to lists +- [THRIFT-3132](https://issues.apache.org/jira/browse/THRIFT-3132) - Properly format date in Java @Generated annotations +- [THRIFT-3137](https://issues.apache.org/jira/browse/THRIFT-3137) - Travis build hangs after failure +- [THRIFT-3138](https://issues.apache.org/jira/browse/THRIFT-3138) - "make check" parallel execution is underministic +- [THRIFT-3139](https://issues.apache.org/jira/browse/THRIFT-3139) - JS library test is flaky +- [THRIFT-3140](https://issues.apache.org/jira/browse/THRIFT-3140) - ConcurrentModificationException is thrown by JavaScript test server +- [THRIFT-3124](https://issues.apache.org/jira/browse/THRIFT-3124) - Some signed/unsigned warnings while building compiler +- [THRIFT-3128](https://issues.apache.org/jira/browse/THRIFT-3128) - Go generated code produces name collisions between services +- [THRIFT-3146](https://issues.apache.org/jira/browse/THRIFT-3146) - Graphviz generates function name collisions between services +- [THRIFT-3147](https://issues.apache.org/jira/browse/THRIFT-3147) - Segfault while receiving data +- [THRIFT-3148](https://issues.apache.org/jira/browse/THRIFT-3148) - Markdown links to coding_standards are dead +- [THRIFT-3090](https://issues.apache.org/jira/browse/THRIFT-3090) - cmake build is broken on MacOSX +- [THRIFT-3097](https://issues.apache.org/jira/browse/THRIFT-3097) - cmake targets unconditionally depend on optional libraries +- [THRIFT-3094](https://issues.apache.org/jira/browse/THRIFT-3094) - master as of 2015-APR-13 fails -DBOOST_THREADS cmake build +- [THRIFT-3099](https://issues.apache.org/jira/browse/THRIFT-3099) - cmake build is broken on FreeBSD +- [THRIFT-3089](https://issues.apache.org/jira/browse/THRIFT-3089) - Assigning default ENUM values results in non-compilable java code if java namespace is not defined +- [THRIFT-3093](https://issues.apache.org/jira/browse/THRIFT-3093) - mingw compile fixes for c++ library 0.9.2 +- [THRIFT-3098](https://issues.apache.org/jira/browse/THRIFT-3098) - Thrift does not pretty print binary typedefs the way it does binary fields +- [THRIFT-3091](https://issues.apache.org/jira/browse/THRIFT-3091) - c_glib service method should return result from handler method +- [THRIFT-3088](https://issues.apache.org/jira/browse/THRIFT-3088) - TThreadPoolServer with Sasl auth may leak CLOSE_WAIT socket +- [THRIFT-3109](https://issues.apache.org/jira/browse/THRIFT-3109) - Cross test log file cannot be browsed when served in HTTP server +- [THRIFT-3113](https://issues.apache.org/jira/browse/THRIFT-3113) - m4 C++11 macro issue +- [THRIFT-3105](https://issues.apache.org/jira/browse/THRIFT-3105) - C++ libthriftnb library on Windows build failure +- [THRIFT-3115](https://issues.apache.org/jira/browse/THRIFT-3115) - Uncompileable code due to name collision with predefined used types +- [THRIFT-3117](https://issues.apache.org/jira/browse/THRIFT-3117) - Java TSSLTransportFactory can't load certificates within JAR archive +- [THRIFT-3102](https://issues.apache.org/jira/browse/THRIFT-3102) - could not make check for Go Library +- [THRIFT-3120](https://issues.apache.org/jira/browse/THRIFT-3120) - Minor spelling errors and an outdated URL +- [THRIFT-3121](https://issues.apache.org/jira/browse/THRIFT-3121) - Librt does not exist on OS X +- [THRIFT-3152](https://issues.apache.org/jira/browse/THRIFT-3152) - Compiler error on Mac OSX (missing #include ) +- [THRIFT-3162](https://issues.apache.org/jira/browse/THRIFT-3162) - make fails for dmd 2.067 +- [THRIFT-3164](https://issues.apache.org/jira/browse/THRIFT-3164) - Thrift C++ library SSL socket by default allows for unsecure SSLv3 negotiation +- [THRIFT-3168](https://issues.apache.org/jira/browse/THRIFT-3168) - Fix Maven POM +- [THRIFT-3170](https://issues.apache.org/jira/browse/THRIFT-3170) - Initialism code in the Go compiler causes chaos +- [THRIFT-3169](https://issues.apache.org/jira/browse/THRIFT-3169) - Do not export thrift.TestStruct and thrift.TestEnum in thrift Go library +- [THRIFT-3191](https://issues.apache.org/jira/browse/THRIFT-3191) - Perl compiler does not add support for unexpected exception handling +- [THRIFT-3178](https://issues.apache.org/jira/browse/THRIFT-3178) - glib C does not compile +- [THRIFT-3189](https://issues.apache.org/jira/browse/THRIFT-3189) - Perl ServerSocket should allow a specific interface to be listened to +- [THRIFT-3252](https://issues.apache.org/jira/browse/THRIFT-3252) - Missing TConcurrentClientSyncInfo.h in cpp Makefile, so doesn't install +- [THRIFT-3255](https://issues.apache.org/jira/browse/THRIFT-3255) - Thrift generator doesn't exclude 'package' keyword for thrift property names breaking java builds +- [THRIFT-3260](https://issues.apache.org/jira/browse/THRIFT-3260) - multiple warnings in c_glib tutorial +- [THRIFT-3256](https://issues.apache.org/jira/browse/THRIFT-3256) - Some D test timings are too aggressive for slow machines +- [THRIFT-3257](https://issues.apache.org/jira/browse/THRIFT-3257) - warning: extra tokens at end of #endif directive +- [THRIFT-3184](https://issues.apache.org/jira/browse/THRIFT-3184) - Thrift Go leaves file descriptors open +- [THRIFT-3203](https://issues.apache.org/jira/browse/THRIFT-3203) - DOAP - please fix "Ocaml" => "OCaml" +- [THRIFT-3210](https://issues.apache.org/jira/browse/THRIFT-3210) - (uncompileable) code generated for server events while are events not enabled +- [THRIFT-3215](https://issues.apache.org/jira/browse/THRIFT-3215) - TJSONProtocol '(c++) uses "throw new" to throw exceptions instead of "throw" +- [THRIFT-3202](https://issues.apache.org/jira/browse/THRIFT-3202) - Allow HSHAServer to configure min and max worker threads separately. +- [THRIFT-3205](https://issues.apache.org/jira/browse/THRIFT-3205) - TCompactProtocol return a wrong error when the io.EOF happens +- [THRIFT-3209](https://issues.apache.org/jira/browse/THRIFT-3209) - LGPL mentioned in license file +- [THRIFT-3197](https://issues.apache.org/jira/browse/THRIFT-3197) - keepAliveTime is hard coded as 60 sec in TThreadPoolServer +- [THRIFT-3196](https://issues.apache.org/jira/browse/THRIFT-3196) - Misspelling in lua TBinaryProtocol (stirctWrite => strictWrite) +- [THRIFT-3198](https://issues.apache.org/jira/browse/THRIFT-3198) - Allow construction of TTransportFactory with a specified maxLength +- [THRIFT-3192](https://issues.apache.org/jira/browse/THRIFT-3192) - Go import paths changed in 1.4, and expired June 1 +- [THRIFT-3271](https://issues.apache.org/jira/browse/THRIFT-3271) - Could not find or load main class configtest_ax_javac_and_java on some non-english systems +- [THRIFT-3273](https://issues.apache.org/jira/browse/THRIFT-3273) - c_glib: Generated code tries to convert between function and void pointers +- [THRIFT-3264](https://issues.apache.org/jira/browse/THRIFT-3264) - Fix Erlang 16 namespaced types +- [THRIFT-3270](https://issues.apache.org/jira/browse/THRIFT-3270) - reusing TNonblockingServer::TConnection cause dirty TSocket +- [THRIFT-3267](https://issues.apache.org/jira/browse/THRIFT-3267) - c_glib: "Critical" failure during unit tests +- [THRIFT-3277](https://issues.apache.org/jira/browse/THRIFT-3277) - THttpClient leaks connections if it's used for multiple requests +- [THRIFT-3278](https://issues.apache.org/jira/browse/THRIFT-3278) - NodeJS: Fix exception stack traces and names +- [THRIFT-3279](https://issues.apache.org/jira/browse/THRIFT-3279) - Fix a bug in retry_max_delay (NodeJS) +- [THRIFT-3280](https://issues.apache.org/jira/browse/THRIFT-3280) - Initialize retry variables on construction +- [THRIFT-3283](https://issues.apache.org/jira/browse/THRIFT-3283) - c_glib: Tutorial server always exits with warning +- [THRIFT-3284](https://issues.apache.org/jira/browse/THRIFT-3284) - c_glib: Empty service produces unused-variable warning +- [THRIFT-1925](https://issues.apache.org/jira/browse/THRIFT-1925) - c_glib generated code does not compile +- [THRIFT-1849](https://issues.apache.org/jira/browse/THRIFT-1849) - after transport->open() opens isOpen returns true and next open() goes thru when it shall not +- [THRIFT-1866](https://issues.apache.org/jira/browse/THRIFT-1866) - java compiler generates non-compiling code with const's defined in a thrift when name includes non-identifier chars +- [THRIFT-1938](https://issues.apache.org/jira/browse/THRIFT-1938) - FunctionRunner.h -- uses wrong path for Thread.h when installed +- [THRIFT-1844](https://issues.apache.org/jira/browse/THRIFT-1844) - Password string not cleared +- [THRIFT-2004](https://issues.apache.org/jira/browse/THRIFT-2004) - Thrift::Union violates :== method contract and crashes +- [THRIFT-2073](https://issues.apache.org/jira/browse/THRIFT-2073) - Thrift C++ THttpClient error: cannot refill buffer +- [THRIFT-2127](https://issues.apache.org/jira/browse/THRIFT-2127) - Autoconf scripting does not properly account for cross-compile +- [THRIFT-2180](https://issues.apache.org/jira/browse/THRIFT-2180) - Integer types issues in Cocoa lib on ARM64 +- [THRIFT-2189](https://issues.apache.org/jira/browse/THRIFT-2189) - Go needs "isset" to fully support "union" type (and optionals) +- [THRIFT-2192](https://issues.apache.org/jira/browse/THRIFT-2192) - autotools on Redhat based systems +- [THRIFT-2546](https://issues.apache.org/jira/browse/THRIFT-2546) - cross language tests fails at 'TestMultiException' when using nodejs server +- [THRIFT-2547](https://issues.apache.org/jira/browse/THRIFT-2547) - nodejs servers and clients fails to connect with cpp using compact protocol +- [THRIFT-2548](https://issues.apache.org/jira/browse/THRIFT-2548) - Nodejs servers and clients does not work properly with -ssl +- [THRIFT-1471](https://issues.apache.org/jira/browse/THRIFT-1471) - toString() does not print ByteBuffer values when nested in a List +- [THRIFT-1201](https://issues.apache.org/jira/browse/THRIFT-1201) - getaddrinfo resource leak +- [THRIFT-615](https://issues.apache.org/jira/browse/THRIFT-615) - TThreadPoolServer doesn't call task_done after pulling tasks from it's clients queue +- [THRIFT-162](https://issues.apache.org/jira/browse/THRIFT-162) - Thrift structures are unhashable, preventing them from being used as set elements +- [THRIFT-810](https://issues.apache.org/jira/browse/THRIFT-810) - Crashed client on TSocket::close under loads +- [THRIFT-557](https://issues.apache.org/jira/browse/THRIFT-557) - charset problem with file Autogenerated by Thrift +- [THRIFT-233](https://issues.apache.org/jira/browse/THRIFT-233) - IDL doesn't support negative hex literals +- [THRIFT-1649](https://issues.apache.org/jira/browse/THRIFT-1649) - contrib/zeromq does not build in 0.8.0 +- [THRIFT-1642](https://issues.apache.org/jira/browse/THRIFT-1642) - Miscalculation lead to throw unexpected "TTransportException::TIMED_OUT"(or called "EAGAIN (timed out)") exception +- [THRIFT-1587](https://issues.apache.org/jira/browse/THRIFT-1587) - TSocket::setRecvTimeout error +- [THRIFT-1248](https://issues.apache.org/jira/browse/THRIFT-1248) - pointer subtraction in TMemoryBuffer relies on undefined behavior +- [THRIFT-1774](https://issues.apache.org/jira/browse/THRIFT-1774) - Sasl Transport client would hang when trying to connect non-sasl transport server +- [THRIFT-1754](https://issues.apache.org/jira/browse/THRIFT-1754) - RangeError in buffer handling +- [THRIFT-1618](https://issues.apache.org/jira/browse/THRIFT-1618) - static structMap in FieldMetaData is not thread safe and can lead to deadlocks +- [THRIFT-2335](https://issues.apache.org/jira/browse/THRIFT-2335) - thrift incompatibility with py:tornado as server, java as client +- [THRIFT-2803](https://issues.apache.org/jira/browse/THRIFT-2803) - TCP_DEFER_ACCEPT not supported with domain sockets +- [THRIFT-2799](https://issues.apache.org/jira/browse/THRIFT-2799) - Build Problem(s): ld: library not found for -l:libboost_unit_test_framework.a +- [THRIFT-2801](https://issues.apache.org/jira/browse/THRIFT-2801) - C++ test suite compilation warnings +- [THRIFT-2802](https://issues.apache.org/jira/browse/THRIFT-2802) - C++ tutorial compilation warnings +- [THRIFT-2795](https://issues.apache.org/jira/browse/THRIFT-2795) - thrift_binary_protocol.c: 'dereferencing type-punned pointer will break strict-aliasing rules' +- [THRIFT-2817](https://issues.apache.org/jira/browse/THRIFT-2817) - TSimpleJSONProtocol reads beyond end of message +- [THRIFT-2826](https://issues.apache.org/jira/browse/THRIFT-2826) - html:standalone sometimes ignored +- [THRIFT-2829](https://issues.apache.org/jira/browse/THRIFT-2829) - Support haxelib installation via github +- [THRIFT-2828](https://issues.apache.org/jira/browse/THRIFT-2828) - slightly wrong help screen indent +- [THRIFT-2831](https://issues.apache.org/jira/browse/THRIFT-2831) - Removes dead code in web_server.js introduced in THRIFT-2819 +- [THRIFT-2823](https://issues.apache.org/jira/browse/THRIFT-2823) - All JS-tests are failing when run with grunt test +- [THRIFT-2827](https://issues.apache.org/jira/browse/THRIFT-2827) - Thrift 0.9.2 fails to compile on Yosemite due to tr1/functional include in ProcessorTest.cpp +- [THRIFT-2843](https://issues.apache.org/jira/browse/THRIFT-2843) - Automake configure.ac has possible typo related to Java +- [THRIFT-2813](https://issues.apache.org/jira/browse/THRIFT-2813) - multiple haxe library fixes/improvements +- [THRIFT-2825](https://issues.apache.org/jira/browse/THRIFT-2825) - Supplying unicode to python Thrift client can cause next request arguments to get overwritten +- [THRIFT-2840](https://issues.apache.org/jira/browse/THRIFT-2840) - Cabal file points to LICENSE file outside the path of the Haskell project. +- [THRIFT-2818](https://issues.apache.org/jira/browse/THRIFT-2818) - Trailing commas in array +- [THRIFT-2830](https://issues.apache.org/jira/browse/THRIFT-2830) - Clean up ant warnings in tutorial dir +- [THRIFT-2842](https://issues.apache.org/jira/browse/THRIFT-2842) - Erlang thrift client has infinite timeout +- [THRIFT-2810](https://issues.apache.org/jira/browse/THRIFT-2810) - Do not leave the underlying ServerSocket open if construction of TServerSocket fails +- [THRIFT-2812](https://issues.apache.org/jira/browse/THRIFT-2812) - Go server adding redundant buffering layer +- [THRIFT-2839](https://issues.apache.org/jira/browse/THRIFT-2839) - TFramedTransport read bug +- [THRIFT-2844](https://issues.apache.org/jira/browse/THRIFT-2844) - Nodejs support broken when running under Browserify +- [THRIFT-2814](https://issues.apache.org/jira/browse/THRIFT-2814) - args/result classes not found when no namespace is set +- [THRIFT-2847](https://issues.apache.org/jira/browse/THRIFT-2847) - function IfValue() is a duplicate of System.StrUtils.IfThen +- [THRIFT-2848](https://issues.apache.org/jira/browse/THRIFT-2848) - certain Delphi tests do not build if TypeRegistry is used +- [THRIFT-2854](https://issues.apache.org/jira/browse/THRIFT-2854) - Go Struct writer and reader looses important error information +- [THRIFT-2858](https://issues.apache.org/jira/browse/THRIFT-2858) - Enable header field case insensitive match in THttpServer +- [THRIFT-2857](https://issues.apache.org/jira/browse/THRIFT-2857) - C# generator creates uncompilable code for struct constants +- [THRIFT-2860](https://issues.apache.org/jira/browse/THRIFT-2860) - Delphi server closes connection on unexpected exceptions +- [THRIFT-2868](https://issues.apache.org/jira/browse/THRIFT-2868) - Enhance error handling in the Go client +- [THRIFT-2879](https://issues.apache.org/jira/browse/THRIFT-2879) - TMemoryBuffer: using lua string in wrong way +- [THRIFT-2851](https://issues.apache.org/jira/browse/THRIFT-2851) - Remove strange public Peek() from Go transports +- [THRIFT-2852](https://issues.apache.org/jira/browse/THRIFT-2852) - Better Open/IsOpen/Close behavior for StreamTransport. +- [THRIFT-2871](https://issues.apache.org/jira/browse/THRIFT-2871) - Missing semicolon in thrift.js +- [THRIFT-2872](https://issues.apache.org/jira/browse/THRIFT-2872) - ThreadManager deadlock for task expiration +- [THRIFT-2881](https://issues.apache.org/jira/browse/THRIFT-2881) - Handle errors from Accept() correctly +- [THRIFT-2849](https://issues.apache.org/jira/browse/THRIFT-2849) - Spell errors reported by codespell tool +- [THRIFT-2870](https://issues.apache.org/jira/browse/THRIFT-2870) - C++ TJSONProtocol using locale dependent formatting +- [THRIFT-2882](https://issues.apache.org/jira/browse/THRIFT-2882) - Lua Generator: using string.len function to get struct(map,list,set) size +- [THRIFT-2864](https://issues.apache.org/jira/browse/THRIFT-2864) - JSON generator missing from Visual Studio build project +- [THRIFT-2878](https://issues.apache.org/jira/browse/THRIFT-2878) - Go validation support of required fields +- [THRIFT-2873](https://issues.apache.org/jira/browse/THRIFT-2873) - TPipe and TPipeServer don't compile on Windows with UNICODE enabled +- [THRIFT-2888](https://issues.apache.org/jira/browse/THRIFT-2888) - import of is missing in JSON generator +- [THRIFT-2900](https://issues.apache.org/jira/browse/THRIFT-2900) - Python THttpClient does not reset socket timeout on exception +- [THRIFT-2907](https://issues.apache.org/jira/browse/THRIFT-2907) - 'ntohll' macro redefined +- [THRIFT-2884](https://issues.apache.org/jira/browse/THRIFT-2884) - Map does not serialize correctly for JSON protocol in Go library +- [THRIFT-2887](https://issues.apache.org/jira/browse/THRIFT-2887) - --with-openssl configure flag is ignored +- [THRIFT-2894](https://issues.apache.org/jira/browse/THRIFT-2894) - PHP json serializer skips maps with int/bool keys +- [THRIFT-2904](https://issues.apache.org/jira/browse/THRIFT-2904) - json_protocol_test.go fails +- [THRIFT-2906](https://issues.apache.org/jira/browse/THRIFT-2906) - library not found for -l:libboost_unit_test_framework.a +- [THRIFT-2890](https://issues.apache.org/jira/browse/THRIFT-2890) - binary data may lose bytes with JSON transport under specific circumstances +- [THRIFT-2891](https://issues.apache.org/jira/browse/THRIFT-2891) - binary data may cause a failure with JSON transport under specific circumstances +- [THRIFT-2901](https://issues.apache.org/jira/browse/THRIFT-2901) - Fix for generated TypeScript functions + indentation of JavaScript maps +- [THRIFT-2916](https://issues.apache.org/jira/browse/THRIFT-2916) - make check fails for D language +- [THRIFT-2918](https://issues.apache.org/jira/browse/THRIFT-2918) - Race condition in Python TProcessPoolServer test +- [THRIFT-2920](https://issues.apache.org/jira/browse/THRIFT-2920) - Erlang Thrift test uses wrong IDL file +- [THRIFT-2922](https://issues.apache.org/jira/browse/THRIFT-2922) - $TRIAL is used with Python tests but not tested accordingly +- [THRIFT-2912](https://issues.apache.org/jira/browse/THRIFT-2912) - Autotool build for C++ Qt library is invalid +- [THRIFT-2914](https://issues.apache.org/jira/browse/THRIFT-2914) - explicit dependency to Lua5.2 fails on some systems +- [THRIFT-2910](https://issues.apache.org/jira/browse/THRIFT-2910) - libevent is not really optional +- [THRIFT-2911](https://issues.apache.org/jira/browse/THRIFT-2911) - fix c++ version zeromq transport, the old version cannot work +- [THRIFT-2915](https://issues.apache.org/jira/browse/THRIFT-2915) - Lua generator missing from Visual Studio build project +- [THRIFT-2917](https://issues.apache.org/jira/browse/THRIFT-2917) - "make clean" breaks test/c_glib +- [THRIFT-2919](https://issues.apache.org/jira/browse/THRIFT-2919) - Haxe test server timeout too large +- [THRIFT-2923](https://issues.apache.org/jira/browse/THRIFT-2923) - JavaScript client assumes a message being written +- [THRIFT-2924](https://issues.apache.org/jira/browse/THRIFT-2924) - TNonblockingServer crashes when user-provided event_base is used +- [THRIFT-2925](https://issues.apache.org/jira/browse/THRIFT-2925) - CMake build does not work with OpenSSL nor anything installed in non-system location +- [THRIFT-2931](https://issues.apache.org/jira/browse/THRIFT-2931) - Access to undeclared static property: Thrift\Protocol\TProtocol::$TBINARYPROTOCOLACCELERATED +- [THRIFT-2893](https://issues.apache.org/jira/browse/THRIFT-2893) - CMake build fails with boost thread or std thread +- [THRIFT-2902](https://issues.apache.org/jira/browse/THRIFT-2902) - Generated c_glib code does not compile with clang +- [THRIFT-2903](https://issues.apache.org/jira/browse/THRIFT-2903) - Qt4 library built with CMake does not work +- [THRIFT-2942](https://issues.apache.org/jira/browse/THRIFT-2942) - CSharp generate invalid code for property named read or write +- [THRIFT-2932](https://issues.apache.org/jira/browse/THRIFT-2932) - Node.js Thrift connection libraries throw Exceptions into event emitter +- [THRIFT-2933](https://issues.apache.org/jira/browse/THRIFT-2933) - v0.9.2: doubles encoded in node with compact protocol cannot be decoded by python +- [THRIFT-2934](https://issues.apache.org/jira/browse/THRIFT-2934) - createServer signature mismatch +- [THRIFT-2981](https://issues.apache.org/jira/browse/THRIFT-2981) - IDL with no namespace produces unparsable PHP +- [THRIFT-2999](https://issues.apache.org/jira/browse/THRIFT-2999) - Addition of .gitattributes text auto in THRIFT-2724 causes modified files on checkout +- [THRIFT-2949](https://issues.apache.org/jira/browse/THRIFT-2949) - typo in compiler/cpp/README.md +- [THRIFT-2957](https://issues.apache.org/jira/browse/THRIFT-2957) - warning: source file %s is in a subdirectory, but option 'subdir-objects' is disabled +- [THRIFT-2953](https://issues.apache.org/jira/browse/THRIFT-2953) - TNamedPipeServerTransport is not Stop()able +- [THRIFT-2962](https://issues.apache.org/jira/browse/THRIFT-2962) - Docker Thrift env for development and testing +- [THRIFT-2971](https://issues.apache.org/jira/browse/THRIFT-2971) - C++ test and tutorial parallel build is unstable +- [THRIFT-2972](https://issues.apache.org/jira/browse/THRIFT-2972) - Missing backslash in lib/cpp/test/Makefile.am +- [THRIFT-2951](https://issues.apache.org/jira/browse/THRIFT-2951) - Fix Erlang name conflict test +- [THRIFT-2955](https://issues.apache.org/jira/browse/THRIFT-2955) - Using list of typedefs does not compile on Go +- [THRIFT-2960](https://issues.apache.org/jira/browse/THRIFT-2960) - namespace regression for Ruby +- [THRIFT-2959](https://issues.apache.org/jira/browse/THRIFT-2959) - nodejs: fix binary unit tests +- [THRIFT-2966](https://issues.apache.org/jira/browse/THRIFT-2966) - nodejs: Fix bad references to TProtocolException and TProtocolExceptionType +- [THRIFT-2970](https://issues.apache.org/jira/browse/THRIFT-2970) - grunt-jsdoc fails due to dependency issues +- [THRIFT-3001](https://issues.apache.org/jira/browse/THRIFT-3001) - C# Equals fails for binary fields (byte[]) +- [THRIFT-3003](https://issues.apache.org/jira/browse/THRIFT-3003) - Missing LICENSE file prevents package from being installed +- [THRIFT-3008](https://issues.apache.org/jira/browse/THRIFT-3008) - Node.js server does not fully support exception +- [THRIFT-3007](https://issues.apache.org/jira/browse/THRIFT-3007) - Travis build is broken because of directory conflict +- [THRIFT-3009](https://issues.apache.org/jira/browse/THRIFT-3009) - TSSLSocket does not use the correct hostname (breaks certificate checks) +- [THRIFT-3011](https://issues.apache.org/jira/browse/THRIFT-3011) - C# test server testException() not implemented according to specs +- [THRIFT-3012](https://issues.apache.org/jira/browse/THRIFT-3012) - Timing problems in NamedPipe implementation due to unnecessary open/close +- [THRIFT-3019](https://issues.apache.org/jira/browse/THRIFT-3019) - Golang generator missing docstring for structs +- [THRIFT-3021](https://issues.apache.org/jira/browse/THRIFT-3021) - Service remote tool does not import stub package with package prefix +- [THRIFT-3026](https://issues.apache.org/jira/browse/THRIFT-3026) - TMultiplexedProcessor does not have a constructor +- [THRIFT-3028](https://issues.apache.org/jira/browse/THRIFT-3028) - Regression caused by THRIFT-2180 +- [THRIFT-3017](https://issues.apache.org/jira/browse/THRIFT-3017) - order of map key/value types incorrect for one CTOR +- [THRIFT-3020](https://issues.apache.org/jira/browse/THRIFT-3020) - Cannot compile thrift as C++03 +- [THRIFT-3024](https://issues.apache.org/jira/browse/THRIFT-3024) - User-Agent "BattleNet" used in some Thrift library files +- [THRIFT-3047](https://issues.apache.org/jira/browse/THRIFT-3047) - Uneven calls to indent_up and indent_down in Cocoa generator +- [THRIFT-3048](https://issues.apache.org/jira/browse/THRIFT-3048) - NodeJS decoding of I64 is inconsistent across protocols +- [THRIFT-3043](https://issues.apache.org/jira/browse/THRIFT-3043) - go compiler generator uses non C++98 code +- [THRIFT-3044](https://issues.apache.org/jira/browse/THRIFT-3044) - Docker README.md paths to Dockerfiles are incorrect +- [THRIFT-3040](https://issues.apache.org/jira/browse/THRIFT-3040) - bower.json wrong "main" path +- [THRIFT-3051](https://issues.apache.org/jira/browse/THRIFT-3051) - Go Thrift generator creates bad go code +- [THRIFT-3057](https://issues.apache.org/jira/browse/THRIFT-3057) - Java compiler build is broken +- [THRIFT-3061](https://issues.apache.org/jira/browse/THRIFT-3061) - C++ TSSLSocket shutdown delay/vulnerability +- [THRIFT-3062](https://issues.apache.org/jira/browse/THRIFT-3062) - C++ TServerSocket invalid port number (over 999999) causes stack corruption +- [THRIFT-3065](https://issues.apache.org/jira/browse/THRIFT-3065) - Update libthrift dependencies (slf4j, httpcore, httpclient) +- [THRIFT-3244](https://issues.apache.org/jira/browse/THRIFT-3244) - TypeScript: fix namespace of included types +- [THRIFT-3246](https://issues.apache.org/jira/browse/THRIFT-3246) - Reduce the number of trivial warnings in Windows C++ CMake builds +- [THRIFT-3224](https://issues.apache.org/jira/browse/THRIFT-3224) - Fix TNamedPipeServer unpredictable behavior on accept +- [THRIFT-3230](https://issues.apache.org/jira/browse/THRIFT-3230) - Python compiler generates wrong code if there is function throwing a typedef of exception with another namespace +- [THRIFT-3236](https://issues.apache.org/jira/browse/THRIFT-3236) - MaxSkipDepth never checked +- [THRIFT-3239](https://issues.apache.org/jira/browse/THRIFT-3239) - Limit recursion depth +- [THRIFT-3241](https://issues.apache.org/jira/browse/THRIFT-3241) - fatal error: runtime: cannot map pages in arena address space +- [THRIFT-3242](https://issues.apache.org/jira/browse/THRIFT-3242) - OSGi Import-Package directive is missing the Apache HTTP packages +- [THRIFT-3234](https://issues.apache.org/jira/browse/THRIFT-3234) - Limit recursion depth +- [THRIFT-3222](https://issues.apache.org/jira/browse/THRIFT-3222) - TypeScript: Generated Enums are quoted +- [THRIFT-3229](https://issues.apache.org/jira/browse/THRIFT-3229) - unexpected Timeout exception when desired bytes are only partially available +- [THRIFT-3231](https://issues.apache.org/jira/browse/THRIFT-3231) - CPP: Limit recursion depth to 64 +- [THRIFT-3235](https://issues.apache.org/jira/browse/THRIFT-3235) - Limit recursion depth +- [THRIFT-3175](https://issues.apache.org/jira/browse/THRIFT-3175) - fastbinary.c python deserialize can cause huge allocations from garbage +- [THRIFT-3176](https://issues.apache.org/jira/browse/THRIFT-3176) - Union incorrectly implements == +- [THRIFT-3177](https://issues.apache.org/jira/browse/THRIFT-3177) - Fails to run rake test +- [THRIFT-3180](https://issues.apache.org/jira/browse/THRIFT-3180) - lua plugin: framed transport do not work +- [THRIFT-3179](https://issues.apache.org/jira/browse/THRIFT-3179) - lua plugin cant connect to remote server because function l_socket_create_and_connect always bind socket to localhost +- [THRIFT-3248](https://issues.apache.org/jira/browse/THRIFT-3248) - TypeScript: additional comma in method signature without parameters +- [THRIFT-3302](https://issues.apache.org/jira/browse/THRIFT-3302) - Go JSON protocol should encode Thrift byte type as signed integer string +- [THRIFT-3297](https://issues.apache.org/jira/browse/THRIFT-3297) - c_glib: an abstract base class is not generated +- [THRIFT-3294](https://issues.apache.org/jira/browse/THRIFT-3294) - TZlibTransport for Java does not write data correctly +- [THRIFT-3296](https://issues.apache.org/jira/browse/THRIFT-3296) - Go cross test does not conform to spec +- [THRIFT-3295](https://issues.apache.org/jira/browse/THRIFT-3295) - C# library does not build on Mono 4.0.2.5 or later +- [THRIFT-3293](https://issues.apache.org/jira/browse/THRIFT-3293) - JavaScript: null values turn into empty structs in constructor +- [THRIFT-3310](https://issues.apache.org/jira/browse/THRIFT-3310) - lib/erl/README.md has incorrect formatting +- [THRIFT-3319](https://issues.apache.org/jira/browse/THRIFT-3319) - CSharp tutorial will not build using the *.sln +- [THRIFT-3335](https://issues.apache.org/jira/browse/THRIFT-3335) - Ruby server does not handle processor exception +- [THRIFT-3338](https://issues.apache.org/jira/browse/THRIFT-3338) - Stray underscore in generated go when service name starts with "New" +- [THRIFT-3324](https://issues.apache.org/jira/browse/THRIFT-3324) - Update Go Docs for pulling all packages +- [THRIFT-3345](https://issues.apache.org/jira/browse/THRIFT-3345) - Clients blocked indefinitely when a java.lang.Error is thrown +- [THRIFT-3332](https://issues.apache.org/jira/browse/THRIFT-3332) - make dist fails on clean build +- [THRIFT-3326](https://issues.apache.org/jira/browse/THRIFT-3326) - Tests do not compile under *BSD +- [THRIFT-3334](https://issues.apache.org/jira/browse/THRIFT-3334) - Markdown notation of protocol spec is malformed +- [THRIFT-3331](https://issues.apache.org/jira/browse/THRIFT-3331) - warning: ‘etype’ may be used uninitialized in this function +- [THRIFT-3349](https://issues.apache.org/jira/browse/THRIFT-3349) - Python server does not handle processor exception +- [THRIFT-3343](https://issues.apache.org/jira/browse/THRIFT-3343) - Fix haskell README +- [THRIFT-3340](https://issues.apache.org/jira/browse/THRIFT-3340) - Python: enable json tests again +- [THRIFT-3311](https://issues.apache.org/jira/browse/THRIFT-3311) - Top level README.md has incorrect formmating +- [THRIFT-2936](https://issues.apache.org/jira/browse/THRIFT-2936) - Minor memory leak in SSL +- [THRIFT-3290](https://issues.apache.org/jira/browse/THRIFT-3290) - Using from in variable names causes the generated Python code to have errors +- [THRIFT-3225](https://issues.apache.org/jira/browse/THRIFT-3225) - Fix TPipeServer unpredictable behavior on interrupt() +- [THRIFT-3354](https://issues.apache.org/jira/browse/THRIFT-3354) - Fix word-extraction substr bug in initialism code +- [THRIFT-2006](https://issues.apache.org/jira/browse/THRIFT-2006) - TBinaryProtocol message header call name length is not validated and can be used to core the server +- [THRIFT-3329](https://issues.apache.org/jira/browse/THRIFT-3329) - C++ library unit tests don't compile against the new boost-1.59 unit test framework +- [THRIFT-2630](https://issues.apache.org/jira/browse/THRIFT-2630) - windows7 64bit pc. ipv4 and ipv6 pc.can't use +- [THRIFT-3336](https://issues.apache.org/jira/browse/THRIFT-3336) - Thrift generated streaming operators added in 0.9.2 cannot be overridden +- [THRIFT-2681](https://issues.apache.org/jira/browse/THRIFT-2681) - Core of unwind_cleanup +- [THRIFT-3317](https://issues.apache.org/jira/browse/THRIFT-3317) - cpp namespace org.apache issue appears in 0.9 + +### Documentation +- [THRIFT-3286](https://issues.apache.org/jira/browse/THRIFT-3286) - Apache Ant is a necessary dependency + +### Improvement +- [THRIFT-227](https://issues.apache.org/jira/browse/THRIFT-227) - Byte[] in collections aren't pretty printed like regular binary fields +- [THRIFT-2744](https://issues.apache.org/jira/browse/THRIFT-2744) - Vagrantfile for Centos 6.5 +- [THRIFT-2644](https://issues.apache.org/jira/browse/THRIFT-2644) - Haxe support +- [THRIFT-2756](https://issues.apache.org/jira/browse/THRIFT-2756) - register Media Type @ IANA +- [THRIFT-3076](https://issues.apache.org/jira/browse/THRIFT-3076) - Compatibility with Haxe 3.2.0 +- [THRIFT-3081](https://issues.apache.org/jira/browse/THRIFT-3081) - C++ Consolidate client processing loops in TServers +- [THRIFT-3083](https://issues.apache.org/jira/browse/THRIFT-3083) - C++ Consolidate server processing loops in TSimpleServer, TThreadedServer, TThreadPoolServer +- [THRIFT-3084](https://issues.apache.org/jira/browse/THRIFT-3084) - C++ add concurrent client limit to threaded servers +- [THRIFT-3074](https://issues.apache.org/jira/browse/THRIFT-3074) - Add compiler/cpp/lex.yythriftl.cc to gitignore. +- [THRIFT-3134](https://issues.apache.org/jira/browse/THRIFT-3134) - Remove use of deprecated "phantom.args" +- [THRIFT-3133](https://issues.apache.org/jira/browse/THRIFT-3133) - Allow "make cross" and "make precross" to run without building all languages +- [THRIFT-3142](https://issues.apache.org/jira/browse/THRIFT-3142) - Make JavaScript use downloaded libraries +- [THRIFT-3141](https://issues.apache.org/jira/browse/THRIFT-3141) - Improve logging of JavaScript test +- [THRIFT-3144](https://issues.apache.org/jira/browse/THRIFT-3144) - Proposal: make String representation of enums in generated go code less verbose +- [THRIFT-3130](https://issues.apache.org/jira/browse/THRIFT-3130) - Remove the last vestiges of THRIFT_OVERLOAD_IF from THRIFT-1316 +- [THRIFT-3131](https://issues.apache.org/jira/browse/THRIFT-3131) - Consolidate suggested import path for go thrift library to git.apache.org in docs and code +- [THRIFT-3092](https://issues.apache.org/jira/browse/THRIFT-3092) - Generated Haskell types should derive Generic +- [THRIFT-3110](https://issues.apache.org/jira/browse/THRIFT-3110) - Print error log after cross test failures on Travis +- [THRIFT-3114](https://issues.apache.org/jira/browse/THRIFT-3114) - Using local temp variables to not pollute the global table +- [THRIFT-3106](https://issues.apache.org/jira/browse/THRIFT-3106) - CMake summary should give more information why a library is set to off +- [THRIFT-3119](https://issues.apache.org/jira/browse/THRIFT-3119) - Java's TThreadedSelectorServer has indistinguishable log messages in run() +- [THRIFT-3122](https://issues.apache.org/jira/browse/THRIFT-3122) - Javascript struct constructor should properly initialize struct and container members from plain js arguments +- [THRIFT-3151](https://issues.apache.org/jira/browse/THRIFT-3151) - Fix links to git-wip* - should be git.apache.org +- [THRIFT-3167](https://issues.apache.org/jira/browse/THRIFT-3167) - Windows build from source instructions need to be revised +- [THRIFT-3155](https://issues.apache.org/jira/browse/THRIFT-3155) - move contrib/mingw32-toolchain.cmake to build/cmake/ +- [THRIFT-3160](https://issues.apache.org/jira/browse/THRIFT-3160) - Make generated go enums implement TextMarshaller and TextUnmarshaller interfaces +- [THRIFT-3150](https://issues.apache.org/jira/browse/THRIFT-3150) - Add an option to thrift go generator to make Read and Write methods private +- [THRIFT-3149](https://issues.apache.org/jira/browse/THRIFT-3149) - Make ReadFieldN methods in generated Go code private +- [THRIFT-3172](https://issues.apache.org/jira/browse/THRIFT-3172) - Add tutorial to Thrift web site +- [THRIFT-3214](https://issues.apache.org/jira/browse/THRIFT-3214) - Add Erlang option for using maps instead of dicts +- [THRIFT-3201](https://issues.apache.org/jira/browse/THRIFT-3201) - Capture github test artifacts for failed builds +- [THRIFT-3266](https://issues.apache.org/jira/browse/THRIFT-3266) - c_glib: Multiple compiler warnings building unit tests +- [THRIFT-3285](https://issues.apache.org/jira/browse/THRIFT-3285) - c_glib: Build library with all warnings enabled, no warnings generated +- [THRIFT-1954](https://issues.apache.org/jira/browse/THRIFT-1954) - Allow for a separate connection timeout value +- [THRIFT-2098](https://issues.apache.org/jira/browse/THRIFT-2098) - Add support for Qt5+ +- [THRIFT-2199](https://issues.apache.org/jira/browse/THRIFT-2199) - Remove Dense protocol (was: move to Contrib) +- [THRIFT-406](https://issues.apache.org/jira/browse/THRIFT-406) - C++ Test suite cleanup +- [THRIFT-902](https://issues.apache.org/jira/browse/THRIFT-902) - socket and connect timeout in TSocket should be distinguished +- [THRIFT-388](https://issues.apache.org/jira/browse/THRIFT-388) - Use a separate wire format for async calls +- [THRIFT-727](https://issues.apache.org/jira/browse/THRIFT-727) - support native C++ language specific exception message +- [THRIFT-1784](https://issues.apache.org/jira/browse/THRIFT-1784) - pep-3110 compliance for exception handling +- [THRIFT-1025](https://issues.apache.org/jira/browse/THRIFT-1025) - C++ ServerSocket should inherit from Socket with the necessary Ctor to listen on connections from a specific host +- [THRIFT-2269](https://issues.apache.org/jira/browse/THRIFT-2269) - Can deploy libthrift-source.jar to maven center repository +- [THRIFT-2804](https://issues.apache.org/jira/browse/THRIFT-2804) - Pull an interface out of TBaseAsyncProcessor +- [THRIFT-2806](https://issues.apache.org/jira/browse/THRIFT-2806) - more whitespace fixups +- [THRIFT-2811](https://issues.apache.org/jira/browse/THRIFT-2811) - Make remote socket address accessible +- [THRIFT-2809](https://issues.apache.org/jira/browse/THRIFT-2809) - .gitignore update for compiler's visual project +- [THRIFT-2846](https://issues.apache.org/jira/browse/THRIFT-2846) - Expose ciphers parameter from ssl.wrap_socket() +- [THRIFT-2859](https://issues.apache.org/jira/browse/THRIFT-2859) - JSON generator: output complete descriptors +- [THRIFT-2861](https://issues.apache.org/jira/browse/THRIFT-2861) - add buffered transport +- [THRIFT-2865](https://issues.apache.org/jira/browse/THRIFT-2865) - Test case for Go: SeqId out of sequence +- [THRIFT-2866](https://issues.apache.org/jira/browse/THRIFT-2866) - Go generator source code is hard to read and maintain +- [THRIFT-2880](https://issues.apache.org/jira/browse/THRIFT-2880) - Read the network address from the listener if available. +- [THRIFT-2875](https://issues.apache.org/jira/browse/THRIFT-2875) - Typo in TDenseProtocol.h comment +- [THRIFT-2874](https://issues.apache.org/jira/browse/THRIFT-2874) - TBinaryProtocol member variable "string_buf_" is never used. +- [THRIFT-2855](https://issues.apache.org/jira/browse/THRIFT-2855) - Move contributing.md to the root of the repository +- [THRIFT-2862](https://issues.apache.org/jira/browse/THRIFT-2862) - Enable RTTI and/or build macros for generated code +- [THRIFT-2876](https://issues.apache.org/jira/browse/THRIFT-2876) - Add test for THRIFT-2526 Assignment operators and copy constructors in c++ don't copy the __isset struct +- [THRIFT-2897](https://issues.apache.org/jira/browse/THRIFT-2897) - Generate -isEqual: and -hash methods +- [THRIFT-2909](https://issues.apache.org/jira/browse/THRIFT-2909) - Improve travis build +- [THRIFT-2921](https://issues.apache.org/jira/browse/THRIFT-2921) - Make Erlang impl ready for OTP 18 release (dict/0 and set/0 are deprecated) +- [THRIFT-2928](https://issues.apache.org/jira/browse/THRIFT-2928) - Rename the erlang test_server module +- [THRIFT-2940](https://issues.apache.org/jira/browse/THRIFT-2940) - Allow installing Thrift from git as NPM module by providing package.json in top level directory +- [THRIFT-2937](https://issues.apache.org/jira/browse/THRIFT-2937) - Allow setting a maximum frame size in TFramedTransport +- [THRIFT-2976](https://issues.apache.org/jira/browse/THRIFT-2976) - nodejs: xhr and websocket support for browserify +- [THRIFT-2996](https://issues.apache.org/jira/browse/THRIFT-2996) - Test for Haxe 3.1.3 or better +- [THRIFT-2969](https://issues.apache.org/jira/browse/THRIFT-2969) - nodejs: DRY up library tests +- [THRIFT-2973](https://issues.apache.org/jira/browse/THRIFT-2973) - Update Haxe lib readme regarding Haxe 3.1.3 +- [THRIFT-2952](https://issues.apache.org/jira/browse/THRIFT-2952) - Improve handling of Server.Stop() +- [THRIFT-2964](https://issues.apache.org/jira/browse/THRIFT-2964) - nodejs: move protocols and transports into separate files +- [THRIFT-2963](https://issues.apache.org/jira/browse/THRIFT-2963) - nodejs - add test coverage +- [THRIFT-3006](https://issues.apache.org/jira/browse/THRIFT-3006) - Attach 'omitempty' json tag for optional fields in Go +- [THRIFT-3027](https://issues.apache.org/jira/browse/THRIFT-3027) - Go compiler does not ensure common initialisms have consistent case +- [THRIFT-3030](https://issues.apache.org/jira/browse/THRIFT-3030) - TThreadedServer: Property for number of clientThreads +- [THRIFT-3023](https://issues.apache.org/jira/browse/THRIFT-3023) - Go compiler is a little overly conservative with names of attributes +- [THRIFT-3018](https://issues.apache.org/jira/browse/THRIFT-3018) - Compact protocol for Delphi +- [THRIFT-3025](https://issues.apache.org/jira/browse/THRIFT-3025) - Change pure Int constants into @enums (where possible) +- [THRIFT-3031](https://issues.apache.org/jira/browse/THRIFT-3031) - migrate "shouldStop" flag to TServer +- [THRIFT-3022](https://issues.apache.org/jira/browse/THRIFT-3022) - Compact protocol for Haxe +- [THRIFT-3041](https://issues.apache.org/jira/browse/THRIFT-3041) - Generate asynchronous clients for Cocoa +- [THRIFT-3053](https://issues.apache.org/jira/browse/THRIFT-3053) - Perl SSL Socket Support (Encryption) +- [THRIFT-3247](https://issues.apache.org/jira/browse/THRIFT-3247) - Generate a C++ thread-safe client +- [THRIFT-3217](https://issues.apache.org/jira/browse/THRIFT-3217) - Provide a little endian variant of the binary protocol in C++ +- [THRIFT-3223](https://issues.apache.org/jira/browse/THRIFT-3223) - TypeScript: Add initial support for Enum Maps +- [THRIFT-3220](https://issues.apache.org/jira/browse/THRIFT-3220) - Option to suppress @Generated Annotation entirely +- [THRIFT-3300](https://issues.apache.org/jira/browse/THRIFT-3300) - Reimplement TZlibTransport in Java using streams +- [THRIFT-3288](https://issues.apache.org/jira/browse/THRIFT-3288) - c_glib: Build unit tests with all warnings enabled, no warnings generated +- [THRIFT-3347](https://issues.apache.org/jira/browse/THRIFT-3347) - Improve cross test servers and clients +- [THRIFT-3342](https://issues.apache.org/jira/browse/THRIFT-3342) - Improve ruby cross test client and server compatibility +- [THRIFT-2296](https://issues.apache.org/jira/browse/THRIFT-2296) - Add C++ Base class for service +- [THRIFT-3337](https://issues.apache.org/jira/browse/THRIFT-3337) - Add testBool method to cross tests +- [THRIFT-3303](https://issues.apache.org/jira/browse/THRIFT-3303) - Disable concurrent cabal jobs on Travis to avoid GHC crash +- [THRIFT-2623](https://issues.apache.org/jira/browse/THRIFT-2623) - Docker container for Thrift +- [THRIFT-3298](https://issues.apache.org/jira/browse/THRIFT-3298) - thrift endian converters may conflict with other libraries +- [THRIFT-1559](https://issues.apache.org/jira/browse/THRIFT-1559) - Provide memory pool for TBinaryProtocol to eliminate memory fragmentation +- [THRIFT-424](https://issues.apache.org/jira/browse/THRIFT-424) - Steal ProtocolBuffers' VarInt implementation for C++ + +### New Feature +- [THRIFT-3070](https://issues.apache.org/jira/browse/THRIFT-3070) - Add ability to set the LocalCertificateSelectionCallback +- [THRIFT-1909](https://issues.apache.org/jira/browse/THRIFT-1909) - Java: Add compiler flag to use the "option pattern" for optional fields +- [THRIFT-2099](https://issues.apache.org/jira/browse/THRIFT-2099) - Stop TThreadPoolServer with alive connections. +- [THRIFT-123](https://issues.apache.org/jira/browse/THRIFT-123) - implement TZlibTransport in Java +- [THRIFT-2368](https://issues.apache.org/jira/browse/THRIFT-2368) - New option: reuse-objects for Java generator +- [THRIFT-2836](https://issues.apache.org/jira/browse/THRIFT-2836) - Optionally generate C++11 MoveConstructible types +- [THRIFT-2824](https://issues.apache.org/jira/browse/THRIFT-2824) - Flag to disable html escaping doctext +- [THRIFT-2819](https://issues.apache.org/jira/browse/THRIFT-2819) - Add WebsSocket client to node.js +- [THRIFT-3050](https://issues.apache.org/jira/browse/THRIFT-3050) - Client certificate authentication for non-http TLS in C# +- [THRIFT-3292](https://issues.apache.org/jira/browse/THRIFT-3292) - Implement TZlibTransport in Go + +### Question +- [THRIFT-2583](https://issues.apache.org/jira/browse/THRIFT-2583) - Thrift on xPC target (SpeedGoat) +- [THRIFT-2592](https://issues.apache.org/jira/browse/THRIFT-2592) - thrift server using c_glib +- [THRIFT-2832](https://issues.apache.org/jira/browse/THRIFT-2832) - c_glib: Handle string lists correctly +- [THRIFT-3136](https://issues.apache.org/jira/browse/THRIFT-3136) - thrift installation problem on mac +- [THRIFT-3346](https://issues.apache.org/jira/browse/THRIFT-3346) - c_glib: Tutorials example crashes saying Calculator.ping implementation returned FALSE but did not set an error + +### Sub-task +- [THRIFT-2578](https://issues.apache.org/jira/browse/THRIFT-2578) - Moving 'make cross' from test.sh to test.py +- [THRIFT-2734](https://issues.apache.org/jira/browse/THRIFT-2734) - Go coding standards +- [THRIFT-2748](https://issues.apache.org/jira/browse/THRIFT-2748) - Add Vagrantfile for Centos 6.5 +- [THRIFT-2753](https://issues.apache.org/jira/browse/THRIFT-2753) - Misc. Haxe improvements +- [THRIFT-2640](https://issues.apache.org/jira/browse/THRIFT-2640) - Compact Protocol in Cocoa +- [THRIFT-3262](https://issues.apache.org/jira/browse/THRIFT-3262) - warning: overflow in implicit constant conversion in DenseProtoTest.cpp +- [THRIFT-3194](https://issues.apache.org/jira/browse/THRIFT-3194) - Can't build with go enabled. gomock SCC path incorrect. +- [THRIFT-3275](https://issues.apache.org/jira/browse/THRIFT-3275) - c_glib tutorial warnings in generated code +- [THRIFT-1125](https://issues.apache.org/jira/browse/THRIFT-1125) - Multiplexing support for the Ruby Library +- [THRIFT-2807](https://issues.apache.org/jira/browse/THRIFT-2807) - PHP Code Style +- [THRIFT-2841](https://issues.apache.org/jira/browse/THRIFT-2841) - Add comprehensive integration tests for the whole Go stack +- [THRIFT-2815](https://issues.apache.org/jira/browse/THRIFT-2815) - Haxe: Support for Multiplexing Services on any Transport, Protocol and Server +- [THRIFT-2886](https://issues.apache.org/jira/browse/THRIFT-2886) - Integrate binary type in standard Thrift cross test +- [THRIFT-2946](https://issues.apache.org/jira/browse/THRIFT-2946) - Enhance usability of cross test framework +- [THRIFT-2967](https://issues.apache.org/jira/browse/THRIFT-2967) - Add .editorconfig to root +- [THRIFT-3033](https://issues.apache.org/jira/browse/THRIFT-3033) - Perl: Support for Multiplexing Services on any Transport, Protocol and Server +- [THRIFT-3174](https://issues.apache.org/jira/browse/THRIFT-3174) - Initialism code in the Go compiler doesn't check first word +- [THRIFT-3193](https://issues.apache.org/jira/browse/THRIFT-3193) - Option to suppress date value in @Generated annotation +- [THRIFT-3305](https://issues.apache.org/jira/browse/THRIFT-3305) - Missing dist files for 0.9.3 release candidate +- [THRIFT-3341](https://issues.apache.org/jira/browse/THRIFT-3341) - Add testBool methods +- [THRIFT-3308](https://issues.apache.org/jira/browse/THRIFT-3308) - Fix broken test cases for 0.9.3 release candidate + +### Task +- [THRIFT-2834](https://issues.apache.org/jira/browse/THRIFT-2834) - Remove semi-colons from python code generator +- [THRIFT-2853](https://issues.apache.org/jira/browse/THRIFT-2853) - Adjust comments not applying anymore after THRIFT-2852 + +### Test +- [THRIFT-3211](https://issues.apache.org/jira/browse/THRIFT-3211) - Add make cross support for php TCompactProtocol + +### Wish +- [THRIFT-2838](https://issues.apache.org/jira/browse/THRIFT-2838) - TNonblockingServer can bind to port 0 (i.e., get an OS-assigned port) but there is no way to get the port number + +## 0.9.2 + +### Bug +- [THRIFT-2793](https://issues.apache.org/jira/browse/THRIFT-2793) - Go compiler produces uncompilable code +- [THRIFT-1481](https://issues.apache.org/jira/browse/THRIFT-1481) - Unix domain sockets in C++ do not support the abstract namespace +- [THRIFT-1455](https://issues.apache.org/jira/browse/THRIFT-1455) - TBinaryProtocolT::writeString casts from size_t to uint32_t, which is not safe on 64-bit platforms +- [THRIFT-1579](https://issues.apache.org/jira/browse/THRIFT-1579) - PHP Extension - function thrift_protocol_read_binary not working from TBinarySerializer::deserialize +- [THRIFT-1584](https://issues.apache.org/jira/browse/THRIFT-1584) - Error: could not SetMinThreads in ThreadPool on single-core machines +- [THRIFT-1614](https://issues.apache.org/jira/browse/THRIFT-1614) - Thrift build from svn repo sources fails with automake-1.12 +- [THRIFT-1047](https://issues.apache.org/jira/browse/THRIFT-1047) - rb_thrift_memory_buffer_write treats arg as string without check, segfaults if you pass non-string +- [THRIFT-1639](https://issues.apache.org/jira/browse/THRIFT-1639) - Java/Python: Serialization/Deserialization of double type using CompactProtocol +- [THRIFT-1647](https://issues.apache.org/jira/browse/THRIFT-1647) - NodeJS BufferedTransport does not work beyond the hello-world example +- [THRIFT-2130](https://issues.apache.org/jira/browse/THRIFT-2130) - Thrift's D library/test: parts of "make check" code do not compile with recent dmd-2.062 through dmd-2.064alpha +- [THRIFT-2140](https://issues.apache.org/jira/browse/THRIFT-2140) - Error compiling cpp tutorials +- [THRIFT-2139](https://issues.apache.org/jira/browse/THRIFT-2139) - MSVC 2012 Error - Cannot compile due to BoostThreadFactory +- [THRIFT-2138](https://issues.apache.org/jira/browse/THRIFT-2138) - pkgconfig file created with wrong include path +- [THRIFT-2160](https://issues.apache.org/jira/browse/THRIFT-2160) - Warning in thrift.h when compiling with -Wunused and NDEBUG +- [THRIFT-2158](https://issues.apache.org/jira/browse/THRIFT-2158) - Compact, JSON, and SimpleJSON protocols are not working correctly +- [THRIFT-2167](https://issues.apache.org/jira/browse/THRIFT-2167) - nodejs lib throws error if options argument isn't passed +- [THRIFT-2288](https://issues.apache.org/jira/browse/THRIFT-2288) - Go impl of Thrift JSON protocol wrongly writes/expects true/false for bools +- [THRIFT-2147](https://issues.apache.org/jira/browse/THRIFT-2147) - Thrift IDL grammar allows for dotted identifier names +- [THRIFT-2145](https://issues.apache.org/jira/browse/THRIFT-2145) - Rack and Thin are not just development dependencies +- [THRIFT-2267](https://issues.apache.org/jira/browse/THRIFT-2267) - Should be able to choose socket family in Python TSocket +- [THRIFT-2276](https://issues.apache.org/jira/browse/THRIFT-2276) - java path in spec file needs updating +- [THRIFT-2281](https://issues.apache.org/jira/browse/THRIFT-2281) - Generated send/recv code ignores errors returned by the underlying protocol +- [THRIFT-2280](https://issues.apache.org/jira/browse/THRIFT-2280) - TJSONProtocol.Flush() does not really flush the transport +- [THRIFT-2274](https://issues.apache.org/jira/browse/THRIFT-2274) - TNonblockingServer and TThreadedSelectorServer do not close their channel selectors on exit and leak file descriptors +- [THRIFT-2265](https://issues.apache.org/jira/browse/THRIFT-2265) - php library doesn't build +- [THRIFT-2232](https://issues.apache.org/jira/browse/THRIFT-2232) - IsSet* broken in Go +- [THRIFT-2246](https://issues.apache.org/jira/browse/THRIFT-2246) - Unset enum value is printed by ToString() +- [THRIFT-2240](https://issues.apache.org/jira/browse/THRIFT-2240) - thrift.vim (contrib) does not correctly handle 'union' +- [THRIFT-2243](https://issues.apache.org/jira/browse/THRIFT-2243) - TNonblockingServer in thrift crashes when TFramedTransport opens +- [THRIFT-2230](https://issues.apache.org/jira/browse/THRIFT-2230) - Cannot Build on RHEL/Centos/Amazon Linux 6.x +- [THRIFT-2247](https://issues.apache.org/jira/browse/THRIFT-2247) - Go generator doesn't deal well with map keys of type binary +- [THRIFT-2253](https://issues.apache.org/jira/browse/THRIFT-2253) - Python Tornado TTornadoServer base class change +- [THRIFT-2261](https://issues.apache.org/jira/browse/THRIFT-2261) - java: error: unmappable character for encoding ASCII +- [THRIFT-2259](https://issues.apache.org/jira/browse/THRIFT-2259) - C#: unexpected null logDelegate() pointer causes AV in TServer.serve() +- [THRIFT-2225](https://issues.apache.org/jira/browse/THRIFT-2225) - SSLContext destroy before cleanupOpenSSL +- [THRIFT-2224](https://issues.apache.org/jira/browse/THRIFT-2224) - TSSLSocket.h and TSSLServerSocket.h should use the platfromsocket too +- [THRIFT-2229](https://issues.apache.org/jira/browse/THRIFT-2229) - thrift failed to build on OSX 10.9 GM +- [THRIFT-2227](https://issues.apache.org/jira/browse/THRIFT-2227) - Thrift compiler generates spurious warnings with Xlint +- [THRIFT-2219](https://issues.apache.org/jira/browse/THRIFT-2219) - Thrift gem fails to build on OS X Mavericks with 1.9.3 rubies +- [THRIFT-2226](https://issues.apache.org/jira/browse/THRIFT-2226) - TServerSocket - keepAlive wrong initialization order +- [THRIFT-2285](https://issues.apache.org/jira/browse/THRIFT-2285) - TJsonProtocol implementation for Java doesn't allow a slash (/) to be escaped (\/) +- [THRIFT-2216](https://issues.apache.org/jira/browse/THRIFT-2216) - Extraneous semicolon in TProtocolUtil.h makes clang mad +- [THRIFT-2215](https://issues.apache.org/jira/browse/THRIFT-2215) - Generated HTML/Graphviz lists referenced enum identifiers as UNKNOWN. +- [THRIFT-2211](https://issues.apache.org/jira/browse/THRIFT-2211) - Exception constructor does not contain namespace prefix. +- [THRIFT-2210](https://issues.apache.org/jira/browse/THRIFT-2210) - lib/java TSimpleJSONProtocol can emit invalid JSON +- [THRIFT-2209](https://issues.apache.org/jira/browse/THRIFT-2209) - Ruby generator -- please namespace classes +- [THRIFT-2202](https://issues.apache.org/jira/browse/THRIFT-2202) - Delphi TServerImpl.DefaultLogDelegate may stop the server with I/O-Error 105 +- [THRIFT-2201](https://issues.apache.org/jira/browse/THRIFT-2201) - Ternary operator returns different types (build error for some compilers) +- [THRIFT-2200](https://issues.apache.org/jira/browse/THRIFT-2200) - nested structs cause generate_fingerprint() to slow down at excessive CPU load +- [THRIFT-2197](https://issues.apache.org/jira/browse/THRIFT-2197) - fix jar output directory in rpm spec file +- [THRIFT-2196](https://issues.apache.org/jira/browse/THRIFT-2196) - Fix invalid dependency in Makefile.am +- [THRIFT-2194](https://issues.apache.org/jira/browse/THRIFT-2194) - Node: Not actually prepending residual data in TFramedTransport.receiver +- [THRIFT-2193](https://issues.apache.org/jira/browse/THRIFT-2193) - Java code generator emits spurious semicolon when deep copying binary data +- [THRIFT-2191](https://issues.apache.org/jira/browse/THRIFT-2191) - Fix charp JSONProtocol.ReadJSONDouble (specify InvariantCulture) +- [THRIFT-2214](https://issues.apache.org/jira/browse/THRIFT-2214) - System header sys/param.h is included inside the Thrift namespace +- [THRIFT-2178](https://issues.apache.org/jira/browse/THRIFT-2178) - Thrift generator returns error exit code on --version +- [THRIFT-2171](https://issues.apache.org/jira/browse/THRIFT-2171) - NodeJS implementation has extremely low test coverage +- [THRIFT-2183](https://issues.apache.org/jira/browse/THRIFT-2183) - gem install fails on zsh +- [THRIFT-2182](https://issues.apache.org/jira/browse/THRIFT-2182) - segfault in regression tests (GC bug in rb_thrift_memory_buffer_write) +- [THRIFT-2181](https://issues.apache.org/jira/browse/THRIFT-2181) - oneway calls don't work in NodeJS +- [THRIFT-2169](https://issues.apache.org/jira/browse/THRIFT-2169) - JavaME Thrift Library causes "java.io.IOException: No Response Entries Available" after using the Thrift client for some time +- [THRIFT-2168](https://issues.apache.org/jira/browse/THRIFT-2168) - Node.js appears broken (at least, examples don't work as intended) +- [THRIFT-2293](https://issues.apache.org/jira/browse/THRIFT-2293) - TSSLTransportFactory.createSSLContext() leaves files open +- [THRIFT-2279](https://issues.apache.org/jira/browse/THRIFT-2279) - TSerializer only returns the first 1024 bytes serialized +- [THRIFT-2278](https://issues.apache.org/jira/browse/THRIFT-2278) - Buffered transport doesn't support writes > buffer size +- [THRIFT-2275](https://issues.apache.org/jira/browse/THRIFT-2275) - Fix memory leak in golang compact_protocol. +- [THRIFT-2282](https://issues.apache.org/jira/browse/THRIFT-2282) - Incorect code generated for some typedefs +- [THRIFT-2009](https://issues.apache.org/jira/browse/THRIFT-2009) - Go redeclaration error +- [THRIFT-1964](https://issues.apache.org/jira/browse/THRIFT-1964) - 'Isset' causes problems with C#/.NET serializers +- [THRIFT-2026](https://issues.apache.org/jira/browse/THRIFT-2026) - Fix TCompactProtocol 64 bit builds +- [THRIFT-2108](https://issues.apache.org/jira/browse/THRIFT-2108) - Fix TAsyncClientManager timeout race +- [THRIFT-2068](https://issues.apache.org/jira/browse/THRIFT-2068) - Multiple calls from same connection are not processed in node +- [THRIFT-1750](https://issues.apache.org/jira/browse/THRIFT-1750) - Make compiler build cleanly under visual studio 10 +- [THRIFT-1755](https://issues.apache.org/jira/browse/THRIFT-1755) - Comment parsing bug +- [THRIFT-1771](https://issues.apache.org/jira/browse/THRIFT-1771) - "make check" fails on x64 for libboost_unit_test_framework.a +- [THRIFT-1841](https://issues.apache.org/jira/browse/THRIFT-1841) - NodeJS Thrift incorrectly parses non-UTF8-string types +- [THRIFT-1908](https://issues.apache.org/jira/browse/THRIFT-1908) - Using php thrift_protocol accelerated transfer causes core dump +- [THRIFT-1892](https://issues.apache.org/jira/browse/THRIFT-1892) - Socket timeouts are declared in milli-seconds, but are actually set in micro-seconds +- [THRIFT-2303](https://issues.apache.org/jira/browse/THRIFT-2303) - TBufferredTransport not properly closing underlying transport +- [THRIFT-2313](https://issues.apache.org/jira/browse/THRIFT-2313) - nodejs server crash after processing the first request when using MultiplexedProcessor/FramedBuffer/BinaryProtocol +- [THRIFT-2311](https://issues.apache.org/jira/browse/THRIFT-2311) - Go: invalid code generated when exception name is a go keyword +- [THRIFT-2308](https://issues.apache.org/jira/browse/THRIFT-2308) - node: TJSONProtocol parse error when reading from buffered message +- [THRIFT-2316](https://issues.apache.org/jira/browse/THRIFT-2316) - ccp: TFileTransportTest +- [THRIFT-2352](https://issues.apache.org/jira/browse/THRIFT-2352) - msvc failed to compile thrift tests +- [THRIFT-2337](https://issues.apache.org/jira/browse/THRIFT-2337) - Golang does not report TIMED_OUT exceptions +- [THRIFT-2340](https://issues.apache.org/jira/browse/THRIFT-2340) - Generated server implementation does not send response type EXCEPTION on the Thrift.TApplicationExceptionType.UNKNOWN_METHOD exception +- [THRIFT-2354](https://issues.apache.org/jira/browse/THRIFT-2354) - Connection errors can lead to case_clause exceptions +- [THRIFT-2339](https://issues.apache.org/jira/browse/THRIFT-2339) - Uncaught exception in thrift c# driver +- [THRIFT-2356](https://issues.apache.org/jira/browse/THRIFT-2356) - c++ thrift client not working with ssl (SSL_connect hangs) +- [THRIFT-2331](https://issues.apache.org/jira/browse/THRIFT-2331) - Missing call to ReadStructBegin() in TApplicationException.Read() +- [THRIFT-2323](https://issues.apache.org/jira/browse/THRIFT-2323) - Uncompileable Delphi code generated for typedef'd structs +- [THRIFT-2322](https://issues.apache.org/jira/browse/THRIFT-2322) - Correctly show the number of times ExecutorService (java) has rejected the client. +- [THRIFT-2389](https://issues.apache.org/jira/browse/THRIFT-2389) - namespaces handled wrongly in acrionscript 3.0 implementation +- [THRIFT-2388](https://issues.apache.org/jira/browse/THRIFT-2388) - GoLang - Fix data races in simple_server and server_socket +- [THRIFT-2386](https://issues.apache.org/jira/browse/THRIFT-2386) - Thrift refuses to link yylex +- [THRIFT-2375](https://issues.apache.org/jira/browse/THRIFT-2375) - Excessive
's in generated HTML +- [THRIFT-2373](https://issues.apache.org/jira/browse/THRIFT-2373) - warning CS0414 in THttpClient.cs: private field 'Thrift.Transport.THttpClient.connection' assigned but never used +- [THRIFT-2372](https://issues.apache.org/jira/browse/THRIFT-2372) - thrift/json_protocol.go:160: function ends without a return statement +- [THRIFT-2371](https://issues.apache.org/jira/browse/THRIFT-2371) - ruby bundler version fails on ~1.3.1, remove and take latest avail +- [THRIFT-2370](https://issues.apache.org/jira/browse/THRIFT-2370) - Compiler SEGFAULTs generating HTML documentation for complex strucre +- [THRIFT-2384](https://issues.apache.org/jira/browse/THRIFT-2384) - Binary map keys produce uncompilable code in go +- [THRIFT-2380](https://issues.apache.org/jira/browse/THRIFT-2380) - unreachable code (CID 1174546, CID 1174679) +- [THRIFT-2378](https://issues.apache.org/jira/browse/THRIFT-2378) - service method arguments of binary type lead to uncompileable Go code +- [THRIFT-2363](https://issues.apache.org/jira/browse/THRIFT-2363) - Issue with character encoding of Success returned from Login using Thrift Proxy and NodeJS +- [THRIFT-2359](https://issues.apache.org/jira/browse/THRIFT-2359) - TBufferedTransport doesn't clear it's buffer on a failed flush call +- [THRIFT-2428](https://issues.apache.org/jira/browse/THRIFT-2428) - Python 3 setup.py support +- [THRIFT-2367](https://issues.apache.org/jira/browse/THRIFT-2367) - Build failure: stdlib and boost both define uint64_t +- [THRIFT-2365](https://issues.apache.org/jira/browse/THRIFT-2365) - C# decodes too many binary bytes from JSON +- [THRIFT-2402](https://issues.apache.org/jira/browse/THRIFT-2402) - byte count of FrameBuffer in AWAITING_CLOSE state is not subtracted from readBufferBytesAllocated +- [THRIFT-2396](https://issues.apache.org/jira/browse/THRIFT-2396) - Build Error on MacOSX +- [THRIFT-2395](https://issues.apache.org/jira/browse/THRIFT-2395) - thrift Ruby gem requires development dependency 'thin' regardless of environment +- [THRIFT-2414](https://issues.apache.org/jira/browse/THRIFT-2414) - c_glib fix several bug. +- [THRIFT-2420](https://issues.apache.org/jira/browse/THRIFT-2420) - Go argument parser for methods without arguments does not skip fields +- [THRIFT-2439](https://issues.apache.org/jira/browse/THRIFT-2439) - Bug in TProtocolDecorator Class causes parsing errors +- [THRIFT-2419](https://issues.apache.org/jira/browse/THRIFT-2419) - golang - Fix fmt.Errorf in generated code +- [THRIFT-2418](https://issues.apache.org/jira/browse/THRIFT-2418) - Go handler function panics on internal error +- [THRIFT-2405](https://issues.apache.org/jira/browse/THRIFT-2405) - Node.js Multiplexer tests fail (silently) +- [THRIFT-2581](https://issues.apache.org/jira/browse/THRIFT-2581) - TFDTransport destructor should not throw +- [THRIFT-2575](https://issues.apache.org/jira/browse/THRIFT-2575) - Thrift includes siginfo_t within apache::thrift::protocol namespace +- [THRIFT-2577](https://issues.apache.org/jira/browse/THRIFT-2577) - TFileTransport missuse of closesocket on windows platform +- [THRIFT-2576](https://issues.apache.org/jira/browse/THRIFT-2576) - Implement Thrift.Protocol.prototype.skip method in JavaScript library +- [THRIFT-2588](https://issues.apache.org/jira/browse/THRIFT-2588) - Thrift compiler is not buildable in Visual Studio 2010 +- [THRIFT-2594](https://issues.apache.org/jira/browse/THRIFT-2594) - JS Compiler: Single quotes are not being escaped in constants. +- [THRIFT-2591](https://issues.apache.org/jira/browse/THRIFT-2591) - TFramedTransport does not handle payloads split across packets correctly +- [THRIFT-2599](https://issues.apache.org/jira/browse/THRIFT-2599) - Uncompileable Delphi code due to naming conflicts with IDL +- [THRIFT-2590](https://issues.apache.org/jira/browse/THRIFT-2590) - C++ Visual Studio solution doesn't include Multiplexing support +- [THRIFT-2595](https://issues.apache.org/jira/browse/THRIFT-2595) - Node.js: Fix global leaks and copy-paste errors +- [THRIFT-2565](https://issues.apache.org/jira/browse/THRIFT-2565) - autoconf fails to find mingw-g++ cross compiler on travis CI +- [THRIFT-2555](https://issues.apache.org/jira/browse/THRIFT-2555) - excessive "unused field" comments +- [THRIFT-2554](https://issues.apache.org/jira/browse/THRIFT-2554) - double initialization in generated Read() method +- [THRIFT-2551](https://issues.apache.org/jira/browse/THRIFT-2551) - OutOfMemoryError "unable to create new native thread" kills serve thread +- [THRIFT-2543](https://issues.apache.org/jira/browse/THRIFT-2543) - Generated enum type in haskell should be qualified +- [THRIFT-2560](https://issues.apache.org/jira/browse/THRIFT-2560) - Thrift compiler generator tries to concat ints with strings using + +- [THRIFT-2559](https://issues.apache.org/jira/browse/THRIFT-2559) - Centos 6.5 unable to "make" with Thrift 0.9.1 +- [THRIFT-2526](https://issues.apache.org/jira/browse/THRIFT-2526) - Assignment operators and copy constructors in c++ don't copy the __isset struct +- [THRIFT-2454](https://issues.apache.org/jira/browse/THRIFT-2454) - c_glib: There is no gethostbyname_r() in some OS. +- [THRIFT-2451](https://issues.apache.org/jira/browse/THRIFT-2451) - Do not use pointers for optional fields with defaults. Do not write such fields if its value set to default. Also, do not use pointers for any optional fields mapped to go map or slice. generate Get accessors +- [THRIFT-2450](https://issues.apache.org/jira/browse/THRIFT-2450) - include HowToContribute in the src repo +- [THRIFT-2448](https://issues.apache.org/jira/browse/THRIFT-2448) - thrift/test/test.sh has incorrect Node.js test path +- [THRIFT-2460](https://issues.apache.org/jira/browse/THRIFT-2460) - unopened socket fd must be less than zero. +- [THRIFT-2459](https://issues.apache.org/jira/browse/THRIFT-2459) - --version should not exit 1 +- [THRIFT-2468](https://issues.apache.org/jira/browse/THRIFT-2468) - Timestamp handling +- [THRIFT-2467](https://issues.apache.org/jira/browse/THRIFT-2467) - Unable to build contrib/fb303 on OSX 10.9.2 +- [THRIFT-2466](https://issues.apache.org/jira/browse/THRIFT-2466) - Improper error handling for SSL/TLS connections that don't complete a handshake +- [THRIFT-2463](https://issues.apache.org/jira/browse/THRIFT-2463) - test/py/RunClientServer.py fails sometimes +- [THRIFT-2458](https://issues.apache.org/jira/browse/THRIFT-2458) - Generated golang server code for "oneway" methods is incorrect +- [THRIFT-2456](https://issues.apache.org/jira/browse/THRIFT-2456) - THttpClient fails when using async support outside Silverlight +- [THRIFT-2524](https://issues.apache.org/jira/browse/THRIFT-2524) - Visual Studio project is missing TThreadedServer files +- [THRIFT-2523](https://issues.apache.org/jira/browse/THRIFT-2523) - Visual Studio project is missing OverlappedSubmissionThread files +- [THRIFT-2520](https://issues.apache.org/jira/browse/THRIFT-2520) - cpp:cob_style generates incorrect .tcc file +- [THRIFT-2508](https://issues.apache.org/jira/browse/THRIFT-2508) - Uncompileable C# code due to language keywords in IDL +- [THRIFT-2506](https://issues.apache.org/jira/browse/THRIFT-2506) - Update TProtocolException error codes to be used consistently throughout the library +- [THRIFT-2505](https://issues.apache.org/jira/browse/THRIFT-2505) - go: struct should always be a pointer to avoid copying of potentially size-unbounded structs +- [THRIFT-2515](https://issues.apache.org/jira/browse/THRIFT-2515) - TLS Method error during make +- [THRIFT-2503](https://issues.apache.org/jira/browse/THRIFT-2503) - C++: Fix name collision when a struct has a member named "val" +- [THRIFT-2477](https://issues.apache.org/jira/browse/THRIFT-2477) - thrift --help text with misplaced comma +- [THRIFT-2492](https://issues.apache.org/jira/browse/THRIFT-2492) - test/cpp does not compile on mac +- [THRIFT-2500](https://issues.apache.org/jira/browse/THRIFT-2500) - sending random data crashes thrift(golang) service +- [THRIFT-2475](https://issues.apache.org/jira/browse/THRIFT-2475) - c_glib: buffered_transport_write function return always TRUE. +- [THRIFT-2495](https://issues.apache.org/jira/browse/THRIFT-2495) - JavaScript/Node string constants lack proper escaping +- [THRIFT-2491](https://issues.apache.org/jira/browse/THRIFT-2491) - unable to import generated ThriftTest service +- [THRIFT-2490](https://issues.apache.org/jira/browse/THRIFT-2490) - c_glib: if fail to read a exception from server, client may be occurred double free +- [THRIFT-2470](https://issues.apache.org/jira/browse/THRIFT-2470) - THttpHandler swallows exceptions from processor +- [THRIFT-2533](https://issues.apache.org/jira/browse/THRIFT-2533) - Boost version in requirements should be updated +- [THRIFT-2532](https://issues.apache.org/jira/browse/THRIFT-2532) - Java version in installation requirements should be updated +- [THRIFT-2529](https://issues.apache.org/jira/browse/THRIFT-2529) - TBufferedTransport split Tcp data bug in nodeJs +- [THRIFT-2537](https://issues.apache.org/jira/browse/THRIFT-2537) - Path for "go get" does not work (pull request 115) +- [THRIFT-2443](https://issues.apache.org/jira/browse/THRIFT-2443) - Node fails cross lang tests +- [THRIFT-2437](https://issues.apache.org/jira/browse/THRIFT-2437) - Author fields in Python setup.py must be strings not lists. +- [THRIFT-2435](https://issues.apache.org/jira/browse/THRIFT-2435) - Java compiler doesn't like struct member names that are identical to an existing enum or struct type +- [THRIFT-2434](https://issues.apache.org/jira/browse/THRIFT-2434) - Missing namespace import for php TMultiplexedProcessor implementation +- [THRIFT-2432](https://issues.apache.org/jira/browse/THRIFT-2432) - Flaky parallel build +- [THRIFT-2430](https://issues.apache.org/jira/browse/THRIFT-2430) - Crash during TThreadPoolServer shutdown +- [THRIFT-667](https://issues.apache.org/jira/browse/THRIFT-667) - Period should not be allowed in identifier names +- [THRIFT-1212](https://issues.apache.org/jira/browse/THRIFT-1212) - Members capital case conflict +- [THRIFT-2584](https://issues.apache.org/jira/browse/THRIFT-2584) - Error handler not listened on javascript client +- [THRIFT-2294](https://issues.apache.org/jira/browse/THRIFT-2294) - Incorrect Makefile generation +- [THRIFT-2601](https://issues.apache.org/jira/browse/THRIFT-2601) - Fix vagrant to work again for builds again +- [THRIFT-2092](https://issues.apache.org/jira/browse/THRIFT-2092) - TNonblocking server should release handler as soon as connection closes +- [THRIFT-2557](https://issues.apache.org/jira/browse/THRIFT-2557) - CS0542 member names cannot be the same as their enclosing type +- [THRIFT-2605](https://issues.apache.org/jira/browse/THRIFT-2605) - TSocket warning on gcc 4.8.3 +- [THRIFT-2607](https://issues.apache.org/jira/browse/THRIFT-2607) - ThreadManager.cpp warning on clang++ 3.4 +- [THRIFT-1998](https://issues.apache.org/jira/browse/THRIFT-1998) - TCompactProtocol.tcc - one more warning on Visual 2010 +- [THRIFT-2610](https://issues.apache.org/jira/browse/THRIFT-2610) - MSVC warning in TSocket.cpp +- [THRIFT-2614](https://issues.apache.org/jira/browse/THRIFT-2614) - TNonblockingServer.cpp warnings on MSVC +- [THRIFT-2608](https://issues.apache.org/jira/browse/THRIFT-2608) - TNonblockingServer.cpp warnings on clang 3.4 +- [THRIFT-2606](https://issues.apache.org/jira/browse/THRIFT-2606) - ThreadManager.h warning in clang++ 3.4 +- [THRIFT-2609](https://issues.apache.org/jira/browse/THRIFT-2609) - TFileTransport.h unused field warning (clang 3.4) +- [THRIFT-2416](https://issues.apache.org/jira/browse/THRIFT-2416) - Cannot use TCompactProtocol with MSVC +- [THRIFT-1803](https://issues.apache.org/jira/browse/THRIFT-1803) - Ruby Thrift 0.9.0 tries to encode UUID to UTF8 and crashes +- [THRIFT-2385](https://issues.apache.org/jira/browse/THRIFT-2385) - Problem with gethostbyname2 during make check +- [THRIFT-2262](https://issues.apache.org/jira/browse/THRIFT-2262) - thrift server 'MutateRow' operation gives no indication of success / failure +- [THRIFT-2048](https://issues.apache.org/jira/browse/THRIFT-2048) - Prefer boolean context to nullptr_t conversion +- [THRIFT-2528](https://issues.apache.org/jira/browse/THRIFT-2528) - Thrift Erlang Library: Multiple thrift applications in one bundle +- [THRIFT-1999](https://issues.apache.org/jira/browse/THRIFT-1999) - warning on gcc 4.7 while compiling BoostMutex.cpp +- [THRIFT-2104](https://issues.apache.org/jira/browse/THRIFT-2104) - Structs lose binary data when transferred from server to client in Java +- [THRIFT-2184](https://issues.apache.org/jira/browse/THRIFT-2184) - undefined method rspec_verify for Thrift::MemoryBufferTransport +- [THRIFT-2351](https://issues.apache.org/jira/browse/THRIFT-2351) - PHP TCompactProtocol has fails to decode messages +- [THRIFT-2016](https://issues.apache.org/jira/browse/THRIFT-2016) - Resource Leak in thrift struct under compiler/cpp/src/parse/t_function.h +- [THRIFT-2273](https://issues.apache.org/jira/browse/THRIFT-2273) - Please delete old releases from mirroring system +- [THRIFT-2270](https://issues.apache.org/jira/browse/THRIFT-2270) - Faulty library version numbering at build or documentation +- [THRIFT-2203](https://issues.apache.org/jira/browse/THRIFT-2203) - Tests keeping failing on Jenkins and Travis CI +- [THRIFT-2399](https://issues.apache.org/jira/browse/THRIFT-2399) - thrift.el: recognize "//"-style comments in emacs thrift-mode +- [THRIFT-2582](https://issues.apache.org/jira/browse/THRIFT-2582) - "FileTransport error" exception is raised when trying to use Java's TFileTransport +- [THRIFT-1682](https://issues.apache.org/jira/browse/THRIFT-1682) - Multiple thread calling a Service function unsafely causes message corruption and terminates with Broken Pipe +- [THRIFT-2357](https://issues.apache.org/jira/browse/THRIFT-2357) - recurse option has no effect when generating php +- [THRIFT-2248](https://issues.apache.org/jira/browse/THRIFT-2248) - Go generator doesn't deal well with map keys of type binary +- [THRIFT-2426](https://issues.apache.org/jira/browse/THRIFT-2426) - clarify IP rights and contributions from fbthrift +- [THRIFT-2041](https://issues.apache.org/jira/browse/THRIFT-2041) - TNonblocking server compilation on windows (ARITHMETIC_RIGHT_SHIFT) +- [THRIFT-2400](https://issues.apache.org/jira/browse/THRIFT-2400) - thrift.el: recognize "//"-style comments in emacs thrift-mode +- [THRIFT-1717](https://issues.apache.org/jira/browse/THRIFT-1717) - Fix deb build in jenkins +- [THRIFT-2266](https://issues.apache.org/jira/browse/THRIFT-2266) - ThreadManager.h:24:10: fatal error: 'tr1/functional' file not found on Mac 10.9 (Mavericks) +- [THRIFT-1300](https://issues.apache.org/jira/browse/THRIFT-1300) - Test failures with parallel builds (make -j) +- [THRIFT-2487](https://issues.apache.org/jira/browse/THRIFT-2487) - Tutorial requires two IDL files but only one is linked from the Thrift web site +- [THRIFT-2329](https://issues.apache.org/jira/browse/THRIFT-2329) - missing release tags within git +- [THRIFT-2306](https://issues.apache.org/jira/browse/THRIFT-2306) - concurent client calls with nodejs +- [THRIFT-2222](https://issues.apache.org/jira/browse/THRIFT-2222) - ruby gem cannot be compiled on OS X mavericks +- [THRIFT-2381](https://issues.apache.org/jira/browse/THRIFT-2381) - code which generated by thrift2/hbase.thrift compile error +- [THRIFT-2390](https://issues.apache.org/jira/browse/THRIFT-2390) - no close event when connection lost +- [THRIFT-2146](https://issues.apache.org/jira/browse/THRIFT-2146) - Unable to pass multiple "--gen" options to the thrift compiler +- [THRIFT-2438](https://issues.apache.org/jira/browse/THRIFT-2438) - Unexpected readFieldEnd call causes JSON Parsing errors +- [THRIFT-2498](https://issues.apache.org/jira/browse/THRIFT-2498) - Error message "Invalid method name" while trying to call HBase Thrift API +- [THRIFT-841](https://issues.apache.org/jira/browse/THRIFT-841) - Build cruft +- [THRIFT-2570](https://issues.apache.org/jira/browse/THRIFT-2570) - Wrong URL given in http://thrift.apache.org/developers +- [THRIFT-2604](https://issues.apache.org/jira/browse/THRIFT-2604) - Fix debian packaging +- [THRIFT-2618](https://issues.apache.org/jira/browse/THRIFT-2618) - Unignore /aclocal files required for build +- [THRIFT-2562](https://issues.apache.org/jira/browse/THRIFT-2562) - ./configure create MakeFile in lib/d with errors +- [THRIFT-2593](https://issues.apache.org/jira/browse/THRIFT-2593) - Unable to build thrift on ubuntu-12.04 (Precise) +- [THRIFT-2461](https://issues.apache.org/jira/browse/THRIFT-2461) - Can't install thrift-0.8.0 on OS X 10.9.2 +- [THRIFT-2602](https://issues.apache.org/jira/browse/THRIFT-2602) - Fix missing dist files +- [THRIFT-2620](https://issues.apache.org/jira/browse/THRIFT-2620) - Fix python packaging +- [THRIFT-2545](https://issues.apache.org/jira/browse/THRIFT-2545) - Test CPP fails to build (possibly typo) + +## Documentation +- [THRIFT-2155](https://issues.apache.org/jira/browse/THRIFT-2155) - Adding one liner guide to rename the version.h.in and rename thrifty.cc.h +- [THRIFT-1991](https://issues.apache.org/jira/browse/THRIFT-1991) - Add exceptions to examples +- [THRIFT-2334](https://issues.apache.org/jira/browse/THRIFT-2334) - add a tutorial for node JS +- [THRIFT-2392](https://issues.apache.org/jira/browse/THRIFT-2392) - Actionscript tutorial +- [THRIFT-2383](https://issues.apache.org/jira/browse/THRIFT-2383) - contrib: sample for connecting Thrift with Rebus +- [THRIFT-2382](https://issues.apache.org/jira/browse/THRIFT-2382) - contrib: sample for connecting Thrift with STOMP + +### Improvement +- [THRIFT-1457](https://issues.apache.org/jira/browse/THRIFT-1457) - Capacity of TframedTransport write buffer is never reset +- [THRIFT-1135](https://issues.apache.org/jira/browse/THRIFT-1135) - Node.js tutorial +- [THRIFT-1371](https://issues.apache.org/jira/browse/THRIFT-1371) - Socket timeouts (SO_RCVTIMEO and SO_SNDTIMEO) not supported on Solaris +- [THRIFT-2142](https://issues.apache.org/jira/browse/THRIFT-2142) - Minor tweaks to thrift.el for better emacs package compatibility +- [THRIFT-2268](https://issues.apache.org/jira/browse/THRIFT-2268) - Modify TSaslTransport to ignore TCP health checks from loadbalancers +- [THRIFT-2264](https://issues.apache.org/jira/browse/THRIFT-2264) - GitHub page incorrectly states that Thrift is still incubating +- [THRIFT-2263](https://issues.apache.org/jira/browse/THRIFT-2263) - Always generate good hashCode for Java +- [THRIFT-2233](https://issues.apache.org/jira/browse/THRIFT-2233) - Java compiler should defensively copy its binary inputs +- [THRIFT-2239](https://issues.apache.org/jira/browse/THRIFT-2239) - Address FindBugs errors +- [THRIFT-2249](https://issues.apache.org/jira/browse/THRIFT-2249) - Add SMP Build option to thrift.spec (and three config defines) +- [THRIFT-2254](https://issues.apache.org/jira/browse/THRIFT-2254) - Exceptions generated by Go compiler should implement error interface +- [THRIFT-2260](https://issues.apache.org/jira/browse/THRIFT-2260) - Thrift imposes unneeded dependency on commons-lang3 +- [THRIFT-2258](https://issues.apache.org/jira/browse/THRIFT-2258) - Add TLS v1.1/1.2 support to TSSLSocket.cpp +- [THRIFT-2205](https://issues.apache.org/jira/browse/THRIFT-2205) - Node.js Test Server to support test.js JavaScript Browser test and sundry fixes +- [THRIFT-2204](https://issues.apache.org/jira/browse/THRIFT-2204) - SSL client for the cocoa client +- [THRIFT-2172](https://issues.apache.org/jira/browse/THRIFT-2172) - Java compiler allocates optionals array for every struct with an optional field +- [THRIFT-2185](https://issues.apache.org/jira/browse/THRIFT-2185) - use cabal instead of runhaskell in haskell library +- [THRIFT-1926](https://issues.apache.org/jira/browse/THRIFT-1926) - PHP Constant Generation Refactoring +- [THRIFT-2029](https://issues.apache.org/jira/browse/THRIFT-2029) - Port C++ tests to Windows +- [THRIFT-2054](https://issues.apache.org/jira/browse/THRIFT-2054) - TSimpleFileTransport - Java Lib has no straight forward TTransport based file transport +- [THRIFT-2040](https://issues.apache.org/jira/browse/THRIFT-2040) - "uninitialized variable" warnings on MSVC/windows +- [THRIFT-2034](https://issues.apache.org/jira/browse/THRIFT-2034) - Give developers' C++ code direct access to socket FDs on server side +- [THRIFT-2095](https://issues.apache.org/jira/browse/THRIFT-2095) - Use print function for Python 3 compatibility +- [THRIFT-1868](https://issues.apache.org/jira/browse/THRIFT-1868) - Make the TPC backlog configurable in the Java servers +- [THRIFT-1813](https://issues.apache.org/jira/browse/THRIFT-1813) - Add @Generated annotation to generated classes +- [THRIFT-1815](https://issues.apache.org/jira/browse/THRIFT-1815) - Code generators line buffer output +- [THRIFT-2305](https://issues.apache.org/jira/browse/THRIFT-2305) - TFramedTransport empty constructor should probably be private +- [THRIFT-2304](https://issues.apache.org/jira/browse/THRIFT-2304) - Move client assignments from construtor in method +- [THRIFT-2309](https://issues.apache.org/jira/browse/THRIFT-2309) - Ruby (gem) & PHP RPM subpackages +- [THRIFT-2318](https://issues.apache.org/jira/browse/THRIFT-2318) - perl: dependency Class::Accessor not checked +- [THRIFT-2317](https://issues.apache.org/jira/browse/THRIFT-2317) - exclude tutorial from build +- [THRIFT-2320](https://issues.apache.org/jira/browse/THRIFT-2320) - Program level doctext does not get attached by parser +- [THRIFT-2349](https://issues.apache.org/jira/browse/THRIFT-2349) - Golang - improve tutorial +- [THRIFT-2348](https://issues.apache.org/jira/browse/THRIFT-2348) - PHP Generator: add array typehint to functions +- [THRIFT-2344](https://issues.apache.org/jira/browse/THRIFT-2344) - configure.ac: compiler-only option +- [THRIFT-2343](https://issues.apache.org/jira/browse/THRIFT-2343) - Golang - Return a single error for all exceptions instead of multiple return values +- [THRIFT-2341](https://issues.apache.org/jira/browse/THRIFT-2341) - Enable generation of Delphi XMLDoc comments (a.k.a. "Help Insight") +- [THRIFT-2355](https://issues.apache.org/jira/browse/THRIFT-2355) - Add SSL and Web Socket Support to Node and JavaScript +- [THRIFT-2350](https://issues.apache.org/jira/browse/THRIFT-2350) - Add async calls to normal JavaScript +- [THRIFT-2330](https://issues.apache.org/jira/browse/THRIFT-2330) - Generate PHPDoc comments +- [THRIFT-2332](https://issues.apache.org/jira/browse/THRIFT-2332) - RPMBUILD: run bootstrap (if needed) +- [THRIFT-2391](https://issues.apache.org/jira/browse/THRIFT-2391) - simple socket transport for actionscript 3.0 +- [THRIFT-2376](https://issues.apache.org/jira/browse/THRIFT-2376) - nodejs: allow Promise style calls for client and server +- [THRIFT-2369](https://issues.apache.org/jira/browse/THRIFT-2369) - Add ssl support for nodejs implementation +- [THRIFT-2401](https://issues.apache.org/jira/browse/THRIFT-2401) - Haskell tutorial compiles +- [THRIFT-2417](https://issues.apache.org/jira/browse/THRIFT-2417) - C# Union classes are not partial +- [THRIFT-2415](https://issues.apache.org/jira/browse/THRIFT-2415) - Named pipes server performance & message mode +- [THRIFT-2404](https://issues.apache.org/jira/browse/THRIFT-2404) - emit warning on (typically inefficient) list +- [THRIFT-2398](https://issues.apache.org/jira/browse/THRIFT-2398) - Improve Node Server Library +- [THRIFT-2397](https://issues.apache.org/jira/browse/THRIFT-2397) - Add CORS and CSP support for JavaScript and Node.js libraries +- [THRIFT-2407](https://issues.apache.org/jira/browse/THRIFT-2407) - use markdown (rename README => README.md) +- [THRIFT-2300](https://issues.apache.org/jira/browse/THRIFT-2300) - D configure info output should follow same format as other languages +- [THRIFT-2579](https://issues.apache.org/jira/browse/THRIFT-2579) - Windows CE support +- [THRIFT-2574](https://issues.apache.org/jira/browse/THRIFT-2574) - Compiler option to generate namespace directories for Ruby +- [THRIFT-2571](https://issues.apache.org/jira/browse/THRIFT-2571) - Simplify cross compilation using CMake +- [THRIFT-2569](https://issues.apache.org/jira/browse/THRIFT-2569) - Introduce file to specify third party library locations on Windows +- [THRIFT-2568](https://issues.apache.org/jira/browse/THRIFT-2568) - Implement own certificate handler +- [THRIFT-2552](https://issues.apache.org/jira/browse/THRIFT-2552) - eliminate warning from configure.ac +- [THRIFT-2549](https://issues.apache.org/jira/browse/THRIFT-2549) - Generate json tag for struct members. use go.tag annotation to override the default generated tag. +- [THRIFT-2544](https://issues.apache.org/jira/browse/THRIFT-2544) - Add support for socket transport for c# library when using Windows Phone projects +- [THRIFT-2453](https://issues.apache.org/jira/browse/THRIFT-2453) - haskell tutorial: fix up division by 0 example +- [THRIFT-2449](https://issues.apache.org/jira/browse/THRIFT-2449) - Enhance typedef structure to distinguish between forwards and real typedefs +- [THRIFT-2446](https://issues.apache.org/jira/browse/THRIFT-2446) - There is no way to handle server stream errors +- [THRIFT-2455](https://issues.apache.org/jira/browse/THRIFT-2455) - Allow client certificates to be used with THttpClient +- [THRIFT-2511](https://issues.apache.org/jira/browse/THRIFT-2511) - Node.js needs the compact protocol +- [THRIFT-2493](https://issues.apache.org/jira/browse/THRIFT-2493) - Node.js lib needs HTTP client +- [THRIFT-2502](https://issues.apache.org/jira/browse/THRIFT-2502) - Optimize go implementations of binary and compact protocols for speed +- [THRIFT-2494](https://issues.apache.org/jira/browse/THRIFT-2494) - Add enum toString helper function in c_glib +- [THRIFT-2471](https://issues.apache.org/jira/browse/THRIFT-2471) - Make cpp.ref annotation language agnostic +- [THRIFT-2497](https://issues.apache.org/jira/browse/THRIFT-2497) - server and client for test/go, also several fixes and improvements +- [THRIFT-2535](https://issues.apache.org/jira/browse/THRIFT-2535) - TJSONProtocol when serialized yields TField ids rather than names +- [THRIFT-2220](https://issues.apache.org/jira/browse/THRIFT-2220) - Add a new struct structv? +- [THRIFT-1352](https://issues.apache.org/jira/browse/THRIFT-1352) - Thrift server +- [THRIFT-989](https://issues.apache.org/jira/browse/THRIFT-989) - Push boost m4 macros upstream +- [THRIFT-1349](https://issues.apache.org/jira/browse/THRIFT-1349) - Remove unnecessary print outs +- [THRIFT-2496](https://issues.apache.org/jira/browse/THRIFT-2496) - server and client for test/go, also several fixes and improvements +- [THRIFT-1114](https://issues.apache.org/jira/browse/THRIFT-1114) - Maven publish shouldn't require passwords hardcoded in settings.xml +- [THRIFT-2043](https://issues.apache.org/jira/browse/THRIFT-2043) - visual 2010 warnings - unreachable code +- [THRIFT-1683](https://issues.apache.org/jira/browse/THRIFT-1683) - Implement alternatives to Javascript Client side Transport protocol, just as NPAPI and WebSocket. +- [THRIFT-1746](https://issues.apache.org/jira/browse/THRIFT-1746) - provide a SPDX file +- [THRIFT-1772](https://issues.apache.org/jira/browse/THRIFT-1772) - Serialization does not check types of embedded structures. +- [THRIFT-2387](https://issues.apache.org/jira/browse/THRIFT-2387) - nodejs: external imports should be centralized in index.js +- [THRIFT-2037](https://issues.apache.org/jira/browse/THRIFT-2037) - More general macro THRIFT_UNUSED_VARIABLE + +### New Feature +- [THRIFT-1012](https://issues.apache.org/jira/browse/THRIFT-1012) - Transport for DataInput DataOutput interface +- [THRIFT-2256](https://issues.apache.org/jira/browse/THRIFT-2256) - Using c++11/c++0x std library replace boost library +- [THRIFT-2250](https://issues.apache.org/jira/browse/THRIFT-2250) - JSON and MemoryBuffer for JavaME +- [THRIFT-2114](https://issues.apache.org/jira/browse/THRIFT-2114) - Python Service Remote SSL Option +- [THRIFT-1719](https://issues.apache.org/jira/browse/THRIFT-1719) - SASL client support for Python +- [THRIFT-1894](https://issues.apache.org/jira/browse/THRIFT-1894) - Thrift multi-threaded async Java Server using Java 7 AsynchronousChannelGroup +- [THRIFT-1893](https://issues.apache.org/jira/browse/THRIFT-1893) - HTTP/JSON server/client for node js +- [THRIFT-2347](https://issues.apache.org/jira/browse/THRIFT-2347) - C# TLS Transport based on THRIFT-181 +- [THRIFT-2377](https://issues.apache.org/jira/browse/THRIFT-2377) - Allow addition of custom HTTP Headers to an HTTP Transport +- [THRIFT-2408](https://issues.apache.org/jira/browse/THRIFT-2408) - Named Pipe Transport Option for C# +- [THRIFT-2572](https://issues.apache.org/jira/browse/THRIFT-2572) - Add string/collection length limit checks (from C++) to java protocol readers +- [THRIFT-2469](https://issues.apache.org/jira/browse/THRIFT-2469) - "java:fullcamel" option to automatically camel-case underscored attribute names +- [THRIFT-795](https://issues.apache.org/jira/browse/THRIFT-795) - Importing service functions (simulation multiple inheritance) +- [THRIFT-2164](https://issues.apache.org/jira/browse/THRIFT-2164) - Add a Get/Post Http Server to Node along with examples +- [THRIFT-2255](https://issues.apache.org/jira/browse/THRIFT-2255) - add Parent Class for generated Struct class + +### Question +- [THRIFT-2539](https://issues.apache.org/jira/browse/THRIFT-2539) - Tsocket.cpp addrinfo ai_flags = AI_ADDRCONFIG +- [THRIFT-2440](https://issues.apache.org/jira/browse/THRIFT-2440) - how to connect as3 to java by thrift , +- [THRIFT-2379](https://issues.apache.org/jira/browse/THRIFT-2379) - Memmory leaking while using multithreading in C++ server. +- [THRIFT-2277](https://issues.apache.org/jira/browse/THRIFT-2277) - Thrift: installing fb303 error +- [THRIFT-2567](https://issues.apache.org/jira/browse/THRIFT-2567) - Csharp slow ? +- [THRIFT-2573](https://issues.apache.org/jira/browse/THRIFT-2573) - thrift 0.9.2 release + +### Sub-task +- [THRIFT-981](https://issues.apache.org/jira/browse/THRIFT-981) - cocoa: add version Info to the library +- [THRIFT-2132](https://issues.apache.org/jira/browse/THRIFT-2132) - Go: Support for Multiplexing Services on any Transport, Protocol and Server +- [THRIFT-2299](https://issues.apache.org/jira/browse/THRIFT-2299) - TJsonProtocol implementation for Ruby does not allow for both possible slash (solidus) encodings +- [THRIFT-2298](https://issues.apache.org/jira/browse/THRIFT-2298) - TJsonProtocol implementation for C# does not allow for both possible slash (solidus) encodings +- [THRIFT-2297](https://issues.apache.org/jira/browse/THRIFT-2297) - TJsonProtocol implementation for Delphi does not allow for both possible slash (solidus) encodings +- [THRIFT-2271](https://issues.apache.org/jira/browse/THRIFT-2271) - JavaScript: Support for Multiplexing Services +- [THRIFT-2251](https://issues.apache.org/jira/browse/THRIFT-2251) - go test for compact protocol is not running +- [THRIFT-2195](https://issues.apache.org/jira/browse/THRIFT-2195) - Delphi: Add event handlers for server and processing events +- [THRIFT-2176](https://issues.apache.org/jira/browse/THRIFT-2176) - TSimpleJSONProtocol.ReadFieldBegin() does not return field type and ID +- [THRIFT-2175](https://issues.apache.org/jira/browse/THRIFT-2175) - Wrong field type set for binary +- [THRIFT-2174](https://issues.apache.org/jira/browse/THRIFT-2174) - Deserializing JSON fails in specific cases +- [THRIFT-2053](https://issues.apache.org/jira/browse/THRIFT-2053) - NodeJS: Support for Multiplexing Services +- [THRIFT-1914](https://issues.apache.org/jira/browse/THRIFT-1914) - Python: Support for Multiplexing Services on any Transport, Protocol and Server +- [THRIFT-1810](https://issues.apache.org/jira/browse/THRIFT-1810) - add ruby to test/test.sh +- [THRIFT-2310](https://issues.apache.org/jira/browse/THRIFT-2310) - PHP: Client-side support for Multiplexing Services +- [THRIFT-2346](https://issues.apache.org/jira/browse/THRIFT-2346) - C#: UTF-8 sent by PHP as JSON is not understood by TJsonProtocol +- [THRIFT-2345](https://issues.apache.org/jira/browse/THRIFT-2345) - Delphi: UTF-8 sent by PHP as JSON is not understood by TJsonProtocol +- [THRIFT-2338](https://issues.apache.org/jira/browse/THRIFT-2338) - First doctext wrongly interpreted as program doctext in some cases +- [THRIFT-2325](https://issues.apache.org/jira/browse/THRIFT-2325) - SSL test certificates +- [THRIFT-2358](https://issues.apache.org/jira/browse/THRIFT-2358) - C++: add compact protocol to cross language test suite +- [THRIFT-2425](https://issues.apache.org/jira/browse/THRIFT-2425) - PHP: Server-side support for Multiplexing Services +- [THRIFT-2421](https://issues.apache.org/jira/browse/THRIFT-2421) - Tree/Recursive struct support in thrift +- [THRIFT-2290](https://issues.apache.org/jira/browse/THRIFT-2290) - Update Go tutorial to align with THRIFT-2232 +- [THRIFT-2558](https://issues.apache.org/jira/browse/THRIFT-2558) - CSharp compiler generator tries to concat ints with strings using + +- [THRIFT-2507](https://issues.apache.org/jira/browse/THRIFT-2507) - Additional LUA TProtocolException error code needed? +- [THRIFT-2499](https://issues.apache.org/jira/browse/THRIFT-2499) - Compiler: allow annotations without "= value" +- [THRIFT-2534](https://issues.apache.org/jira/browse/THRIFT-2534) - Cross language test results should recorded to a status.md or status.html file automatically +- [THRIFT-66](https://issues.apache.org/jira/browse/THRIFT-66) - Java: Allow multiplexing multiple services over a single TCP connection +- [THRIFT-1681](https://issues.apache.org/jira/browse/THRIFT-1681) - Add Lua Support +- [THRIFT-1727](https://issues.apache.org/jira/browse/THRIFT-1727) - Ruby-1.9: data loss: "binary" fields are re-encoded +- [THRIFT-1726](https://issues.apache.org/jira/browse/THRIFT-1726) - Ruby-1.9: "binary" fields are represented by string whose encoding is "UTF-8" +- [THRIFT-988](https://issues.apache.org/jira/browse/THRIFT-988) - perl: add version Info to the library via configure +- [THRIFT-334](https://issues.apache.org/jira/browse/THRIFT-334) - Compact Protocol for PHP +- [THRIFT-2444](https://issues.apache.org/jira/browse/THRIFT-2444) - pull request 88: thrift: clean up enum value assignment + +### Task +- [THRIFT-2223](https://issues.apache.org/jira/browse/THRIFT-2223) - Spam links on wiki +- [THRIFT-2566](https://issues.apache.org/jira/browse/THRIFT-2566) - Please create a DOAP file for your TLP +- [THRIFT-2237](https://issues.apache.org/jira/browse/THRIFT-2237) - Update archive to contain all versions +- [THRIFT-962](https://issues.apache.org/jira/browse/THRIFT-962) - Tutorial page on our website is really unhelpful + +### Test +- [THRIFT-2327](https://issues.apache.org/jira/browse/THRIFT-2327) - nodejs: nodejs test suite should be bundled with the library +- [THRIFT-2445](https://issues.apache.org/jira/browse/THRIFT-2445) - THRIFT-2384 (code generation for go maps with binary keys) should be tested +- [THRIFT-2501](https://issues.apache.org/jira/browse/THRIFT-2501) - C# The test parameters from the TestServer and TestClient are different from the http://thrift.apache.org/test/ + +### Wish +- [THRIFT-2190](https://issues.apache.org/jira/browse/THRIFT-2190) - Add the JavaScript thrift.js lib to the Bower registry +- [THRIFT-2076](https://issues.apache.org/jira/browse/THRIFT-2076) - boost::optional instead of __isset + +## 0.9.1 + +### Bug +- [THRIFT-1440](https://issues.apache.org/jira/browse/THRIFT-1440) - debian packaging: minor-ish policy problems +- [THRIFT-1402](https://issues.apache.org/jira/browse/THRIFT-1402) - Generated Y_types.js does not require() X_types.js when an include in the IDL file was used +- [THRIFT-1551](https://issues.apache.org/jira/browse/THRIFT-1551) - 2 thrift file define only struct (no service), one include another, the gen nodejs file didn't have "requires" at the top +- [THRIFT-1264](https://issues.apache.org/jira/browse/THRIFT-1264) - TSocketClient is queried by run loop after deallocation in Cocoa +- [THRIFT-1600](https://issues.apache.org/jira/browse/THRIFT-1600) - Thrift Go Compiler and Library out of date with Go 1 Release. +- [THRIFT-1603](https://issues.apache.org/jira/browse/THRIFT-1603) - Thrift IDL allows for multiple exceptions, args or struct member names to be the same +- [THRIFT-1062](https://issues.apache.org/jira/browse/THRIFT-1062) - Problems with python tutorials +- [THRIFT-864](https://issues.apache.org/jira/browse/THRIFT-864) - default value fails if identifier is a struct +- [THRIFT-930](https://issues.apache.org/jira/browse/THRIFT-930) - Ruby and Haskell bindings don't properly support DESTDIR (makes packaging painful) +- [THRIFT-820](https://issues.apache.org/jira/browse/THRIFT-820) - The readLength attribute of TBinaryProtocol is used as an instance variable and is decremented on each call of checkReadLength +- [THRIFT-1640](https://issues.apache.org/jira/browse/THRIFT-1640) - None of the tutorials linked on the website contain content +- [THRIFT-1637](https://issues.apache.org/jira/browse/THRIFT-1637) - NPM registry does not include version 0.8 +- [THRIFT-1648](https://issues.apache.org/jira/browse/THRIFT-1648) - NodeJS clients always receive 0 for 'double' values. +- [THRIFT-1660](https://issues.apache.org/jira/browse/THRIFT-1660) - Python Thrift library can be installed with pip but not easy_install +- [THRIFT-1657](https://issues.apache.org/jira/browse/THRIFT-1657) - Chrome browser sending OPTIONS method before POST in xmlHttpRequest +- [THRIFT-2118](https://issues.apache.org/jira/browse/THRIFT-2118) - Certificate error handling still incorrect +- [THRIFT-2137](https://issues.apache.org/jira/browse/THRIFT-2137) - Ruby test lib fails jenkins build #864 +- [THRIFT-2136](https://issues.apache.org/jira/browse/THRIFT-2136) - Vagrant build not compiling java, ruby, php, go libs due to missing dependencies +- [THRIFT-2135](https://issues.apache.org/jira/browse/THRIFT-2135) - GO lib leaves behind test files that are auto generated +- [THRIFT-2134](https://issues.apache.org/jira/browse/THRIFT-2134) - mingw-cross-compile script failing with strip errors +- [THRIFT-2133](https://issues.apache.org/jira/browse/THRIFT-2133) - java TestTBinaryProtocol.java test failing +- [THRIFT-2126](https://issues.apache.org/jira/browse/THRIFT-2126) - lib/cpp/src/thrift/concurrency/STD* files missing from DIST +- [THRIFT-2125](https://issues.apache.org/jira/browse/THRIFT-2125) - debian missing from DIST +- [THRIFT-2124](https://issues.apache.org/jira/browse/THRIFT-2124) - .o, .so, .la, .deps, .libs, gen-* files left tutorials, test and lib/cpp when making DIST +- [THRIFT-2123](https://issues.apache.org/jira/browse/THRIFT-2123) - GO lib missing files in DIST build +- [THRIFT-2121](https://issues.apache.org/jira/browse/THRIFT-2121) - Compilation bug for Node.js +- [THRIFT-2129](https://issues.apache.org/jira/browse/THRIFT-2129) - php ext missing from dist +- [THRIFT-2128](https://issues.apache.org/jira/browse/THRIFT-2128) - lib GO tests fail with funct ends without a return statement +- [THRIFT-2286](https://issues.apache.org/jira/browse/THRIFT-2286) - Failed to compile Thrift0.9.1 with boost1.55 by VS2010 if select Debug-mt&x64 mode. +- [THRIFT-1973](https://issues.apache.org/jira/browse/THRIFT-1973) - TCompactProtocol in C# lib does not serialize and deserialize negative int32 and int64 number correctly +- [THRIFT-1992](https://issues.apache.org/jira/browse/THRIFT-1992) - casts in TCompactProtocol.tcc causing "dereferencing type-punned pointer will break strict-aliasing rules" warnings from gcc +- [THRIFT-1930](https://issues.apache.org/jira/browse/THRIFT-1930) - C# generates unsigned byte for Thrift "byte" type +- [THRIFT-1929](https://issues.apache.org/jira/browse/THRIFT-1929) - Update website to use Mirrors for downloads +- [THRIFT-1928](https://issues.apache.org/jira/browse/THRIFT-1928) - Race may still exist in TFileTransport::flush() +- [THRIFT-1934](https://issues.apache.org/jira/browse/THRIFT-1934) - Tabs in Example section on main page are not working +- [THRIFT-1933](https://issues.apache.org/jira/browse/THRIFT-1933) - Delphi generator crashes when a typedef references another typedef from an included file +- [THRIFT-1942](https://issues.apache.org/jira/browse/THRIFT-1942) - Binary accelerated cpp extension does not use Thrift namespaces for Exceptions +- [THRIFT-1959](https://issues.apache.org/jira/browse/THRIFT-1959) - C#: Add Union TMemoryBuffer support +- [THRIFT-1958](https://issues.apache.org/jira/browse/THRIFT-1958) - C#: Use static Object.Equals instead of .Equals() calls in equals +- [THRIFT-1957](https://issues.apache.org/jira/browse/THRIFT-1957) - NodeJS TFramedTransport and TBufferedTransport read bytes as unsigned +- [THRIFT-1955](https://issues.apache.org/jira/browse/THRIFT-1955) - Union Type writer generated in C# does not WriteStructBegin +- [THRIFT-1952](https://issues.apache.org/jira/browse/THRIFT-1952) - Travis CI +- [THRIFT-1949](https://issues.apache.org/jira/browse/THRIFT-1949) - WP7 build broken +- [THRIFT-1943](https://issues.apache.org/jira/browse/THRIFT-1943) - docstrings for enum values are ignored +- [THRIFT-2070](https://issues.apache.org/jira/browse/THRIFT-2070) - Improper `HexChar' and 'HexVal' implementation in TJSONProtocol.cs +- [THRIFT-2017](https://issues.apache.org/jira/browse/THRIFT-2017) - Resource Leak in thrift struct under compiler/cpp/src/parse/t_program.h +- [THRIFT-2032](https://issues.apache.org/jira/browse/THRIFT-2032) - C# client leaks sockets/handles +- [THRIFT-1996](https://issues.apache.org/jira/browse/THRIFT-1996) - JavaME Constants generation is broken / inconsistent with regular Java generation +- [THRIFT-2002](https://issues.apache.org/jira/browse/THRIFT-2002) - Haskell: Test use Data.Maybe instead of Maybe +- [THRIFT-2051](https://issues.apache.org/jira/browse/THRIFT-2051) - Vagrant fails to build erlang +- [THRIFT-2050](https://issues.apache.org/jira/browse/THRIFT-2050) - Vagrant C# lib compile fails with TException missing +- [THRIFT-1978](https://issues.apache.org/jira/browse/THRIFT-1978) - Ruby: Thrift should allow for the SSL verify mode to be set +- [THRIFT-1984](https://issues.apache.org/jira/browse/THRIFT-1984) - namespace collision in python bindings +- [THRIFT-1988](https://issues.apache.org/jira/browse/THRIFT-1988) - When trying to build a debian package it fails as the file NEWS doesn't exist +- [THRIFT-1975](https://issues.apache.org/jira/browse/THRIFT-1975) - TBinaryProtocol CheckLength can't be used for a client +- [THRIFT-1995](https://issues.apache.org/jira/browse/THRIFT-1995) - '.' allowed at end of identifier generates non-compilable code +- [THRIFT-2112](https://issues.apache.org/jira/browse/THRIFT-2112) - Error in Go generator when using typedefs in map keys +- [THRIFT-2088](https://issues.apache.org/jira/browse/THRIFT-2088) - Typos in Thrift compiler help text +- [THRIFT-2080](https://issues.apache.org/jira/browse/THRIFT-2080) - C# multiplex processor does not catch IOException +- [THRIFT-2082](https://issues.apache.org/jira/browse/THRIFT-2082) - Executing "gmake clean" is broken +- [THRIFT-2102](https://issues.apache.org/jira/browse/THRIFT-2102) - constants are not referencing to correct type when included from another thrift file +- [THRIFT-2100](https://issues.apache.org/jira/browse/THRIFT-2100) - typedefs are not correctly referenced when including from other thrift files +- [THRIFT-2066](https://issues.apache.org/jira/browse/THRIFT-2066) - 'make install' does not install two headers required for C++ bindings +- [THRIFT-2065](https://issues.apache.org/jira/browse/THRIFT-2065) - Not valid constants filename in Java +- [THRIFT-2047](https://issues.apache.org/jira/browse/THRIFT-2047) - Thrift.Protocol.TCompactProtocol, intToZigZag data lost (TCompactProtocol.cs) +- [THRIFT-2036](https://issues.apache.org/jira/browse/THRIFT-2036) - Thrift gem warns about class variable access from top level +- [THRIFT-2057](https://issues.apache.org/jira/browse/THRIFT-2057) - Vagrant fails on php tests +- [THRIFT-2105](https://issues.apache.org/jira/browse/THRIFT-2105) - Generated code for default values of collections ignores t_field::T_REQUIRED +- [THRIFT-2091](https://issues.apache.org/jira/browse/THRIFT-2091) - Unnecessary 'friend' declaration causes warning in TWinsockSingleton +- [THRIFT-2090](https://issues.apache.org/jira/browse/THRIFT-2090) - Go generator, fix including of other thrift files +- [THRIFT-2106](https://issues.apache.org/jira/browse/THRIFT-2106) - Fix support for namespaces in GO generator +- [THRIFT-1783](https://issues.apache.org/jira/browse/THRIFT-1783) - C# doesn't handle required fields correctly +- [THRIFT-1782](https://issues.apache.org/jira/browse/THRIFT-1782) - async only defined in silverlight +- [THRIFT-1779](https://issues.apache.org/jira/browse/THRIFT-1779) - Missing process_XXXX method in generated TProcessor implementation for all 'oneway' service functions +- [THRIFT-1692](https://issues.apache.org/jira/browse/THRIFT-1692) - SO_REUSEADDR allows for socket hijacking on Windows +- [THRIFT-1720](https://issues.apache.org/jira/browse/THRIFT-1720) - JRuby times out on successful connection +- [THRIFT-1713](https://issues.apache.org/jira/browse/THRIFT-1713) - Named and Anonymous Pipe transport (Delphi) +- [THRIFT-1699](https://issues.apache.org/jira/browse/THRIFT-1699) - Native Union#read has extra read_field_end call +- [THRIFT-1749](https://issues.apache.org/jira/browse/THRIFT-1749) - Python TSSLSocket error handling obscures actual error +- [THRIFT-1748](https://issues.apache.org/jira/browse/THRIFT-1748) - Guard and RWGuard macros defined in global namespace +- [THRIFT-1734](https://issues.apache.org/jira/browse/THRIFT-1734) - Front webpage is still advertising v0.8 as current release +- [THRIFT-1729](https://issues.apache.org/jira/browse/THRIFT-1729) - C glib refactor left empty folders in svn +- [THRIFT-1767](https://issues.apache.org/jira/browse/THRIFT-1767) - unions can't have required fields (Delphi) +- [THRIFT-1765](https://issues.apache.org/jira/browse/THRIFT-1765) - Incorrect error message printed for null or negative keys +- [THRIFT-1778](https://issues.apache.org/jira/browse/THRIFT-1778) - Configure requires manual intervention due to tar failure +- [THRIFT-1777](https://issues.apache.org/jira/browse/THRIFT-1777) - TPipeServer is UNSTOPPABLE +- [THRIFT-1753](https://issues.apache.org/jira/browse/THRIFT-1753) - Multiple C++ Windows, OSX, and iOS portability issues +- [THRIFT-1756](https://issues.apache.org/jira/browse/THRIFT-1756) - 'make -j 8' fails with "unterminated #ifdef" error +- [THRIFT-1773](https://issues.apache.org/jira/browse/THRIFT-1773) - Python library should run on python 2.4 +- [THRIFT-1769](https://issues.apache.org/jira/browse/THRIFT-1769) - unions can't have required fields (C++) +- [THRIFT-1768](https://issues.apache.org/jira/browse/THRIFT-1768) - unions can't have required fields (Compiler) +- [THRIFT-1666](https://issues.apache.org/jira/browse/THRIFT-1666) - htonll usage in TBinaryProtocol.tcc generates warning with MSVC2010 +- [THRIFT-1919](https://issues.apache.org/jira/browse/THRIFT-1919) - libthrift depends on httpcore-4.1.3 (directly) and httpcore-4.1.4 (transitively) +- [THRIFT-1864](https://issues.apache.org/jira/browse/THRIFT-1864) - implement event handler for non-blocking server +- [THRIFT-1859](https://issues.apache.org/jira/browse/THRIFT-1859) - Generated error c++ code with -out and include_prefix param +- [THRIFT-1869](https://issues.apache.org/jira/browse/THRIFT-1869) - TThreadPoolServer (java) dies when threadpool is consumed +- [THRIFT-1842](https://issues.apache.org/jira/browse/THRIFT-1842) - Memory leak with Pipes +- [THRIFT-1838](https://issues.apache.org/jira/browse/THRIFT-1838) - Can't build compiler on OS X because of missing thrifty.h +- [THRIFT-1846](https://issues.apache.org/jira/browse/THRIFT-1846) - Restore socket.h header to support builds with Android NDK +- [THRIFT-1850](https://issues.apache.org/jira/browse/THRIFT-1850) - make check hangs on TSocket tests in TransportTest.cpp +- [THRIFT-1873](https://issues.apache.org/jira/browse/THRIFT-1873) - Binary protocol factory ignores struct read/write flags +- [THRIFT-1872](https://issues.apache.org/jira/browse/THRIFT-1872) - issues with TBufferedTransport buffer +- [THRIFT-1904](https://issues.apache.org/jira/browse/THRIFT-1904) - Incorrect code is generated for typedefs which use included types +- [THRIFT-1903](https://issues.apache.org/jira/browse/THRIFT-1903) - PHP namespaces cause binary protocols to not be used +- [THRIFT-1895](https://issues.apache.org/jira/browse/THRIFT-1895) - Delphi: reserved variable name "result" not detected properly +- [THRIFT-1881](https://issues.apache.org/jira/browse/THRIFT-1881) - TNonblockingServer does not release open connections or threads on shutdown +- [THRIFT-1888](https://issues.apache.org/jira/browse/THRIFT-1888) - Java Thrift client can't connect to Python Thrift server on same host +- [THRIFT-1831](https://issues.apache.org/jira/browse/THRIFT-1831) - Bug in list deserializer +- [THRIFT-1824](https://issues.apache.org/jira/browse/THRIFT-1824) - many compile warning, becase Thread.h includes config.h +- [THRIFT-1823](https://issues.apache.org/jira/browse/THRIFT-1823) - Missing parenthesis breaks "IS_..." macro in generated code +- [THRIFT-1806](https://issues.apache.org/jira/browse/THRIFT-1806) - Python generation always truncates __init__.py files +- [THRIFT-1795](https://issues.apache.org/jira/browse/THRIFT-1795) - Race condition in TThreadedServerPool java implementation +- [THRIFT-1794](https://issues.apache.org/jira/browse/THRIFT-1794) - C# asyncctp broken +- [THRIFT-1804](https://issues.apache.org/jira/browse/THRIFT-1804) - Binary+compact protocol single byte error in Ruby library (ARM architecture): caused by different char signedness +- [THRIFT-1800](https://issues.apache.org/jira/browse/THRIFT-1800) - Documentation text not always escaped correctly when rendered to HTML +- [THRIFT-1788](https://issues.apache.org/jira/browse/THRIFT-1788) - C#: Constants static constructor does not compile +- [THRIFT-1816](https://issues.apache.org/jira/browse/THRIFT-1816) - Need "require" included thrift files in "xxx_types.js" +- [THRIFT-1907](https://issues.apache.org/jira/browse/THRIFT-1907) - Compiling namespace and sub-namespace directives for unrecognized generators should only be a warning +- [THRIFT-1913](https://issues.apache.org/jira/browse/THRIFT-1913) - skipping unknown fields in java unions +- [THRIFT-2553](https://issues.apache.org/jira/browse/THRIFT-2553) - C++ linker error - transport/TSocket +- [THRIFT-274](https://issues.apache.org/jira/browse/THRIFT-274) - Towards a working release/versioning process + +### Documentation +- [THRIFT-1971](https://issues.apache.org/jira/browse/THRIFT-1971) - [Graphviz] Adds tutorial/general description documentation +- [THRIFT-2001](https://issues.apache.org/jira/browse/THRIFT-2001) - http://thrift.apache.org/ Example "C++ Server" tab is broken + +### Improvement +- [THRIFT-1574](https://issues.apache.org/jira/browse/THRIFT-1574) - Apache project branding requirements: DOAP file [PATCH] +- [THRIFT-1347](https://issues.apache.org/jira/browse/THRIFT-1347) - Unify the exceptions returned in generated Go code +- [THRIFT-1353](https://issues.apache.org/jira/browse/THRIFT-1353) - Switch to performance branch, get rid of BinaryParser +- [THRIFT-1629](https://issues.apache.org/jira/browse/THRIFT-1629) - Ruby 1.9 Compatibility during Thrift configure, make, install +- [THRIFT-991](https://issues.apache.org/jira/browse/THRIFT-991) - Refactor Haskell code and generator +- [THRIFT-990](https://issues.apache.org/jira/browse/THRIFT-990) - Sanify gettimeofday usage codebase-wide +- [THRIFT-791](https://issues.apache.org/jira/browse/THRIFT-791) - Let C++ TSimpleServer be driven by an external main loop +- [THRIFT-2117](https://issues.apache.org/jira/browse/THRIFT-2117) - Cocoa TBinaryProtocol strictWrite should be set to true by default +- [THRIFT-2014](https://issues.apache.org/jira/browse/THRIFT-2014) - Change C++ lib includes to use style throughout +- [THRIFT-1972](https://issues.apache.org/jira/browse/THRIFT-1972) - Add support for async processors +- [THRIFT-1970](https://issues.apache.org/jira/browse/THRIFT-1970) - [Graphviz] Adds option to render exceptions relationships +- [THRIFT-1966](https://issues.apache.org/jira/browse/THRIFT-1966) - Support different files for SSL certificates and keys +- [THRIFT-1965](https://issues.apache.org/jira/browse/THRIFT-1965) - Adds Graphviz (graph description language) generator +- [THRIFT-1956](https://issues.apache.org/jira/browse/THRIFT-1956) - Switch to Apache Commons Lang 3 +- [THRIFT-1962](https://issues.apache.org/jira/browse/THRIFT-1962) - Multiplex processor should send any TApplicationException back to client +- [THRIFT-1960](https://issues.apache.org/jira/browse/THRIFT-1960) - main() declares 22 unused gen bools +- [THRIFT-1951](https://issues.apache.org/jira/browse/THRIFT-1951) - libthrift.jar has source files in it +- [THRIFT-1997](https://issues.apache.org/jira/browse/THRIFT-1997) - Add accept backlog configuration method to TServerSocket +- [THRIFT-2003](https://issues.apache.org/jira/browse/THRIFT-2003) - Deprecate senum +- [THRIFT-2052](https://issues.apache.org/jira/browse/THRIFT-2052) - Vagrant machine image defaults to only 384MB of RAM +- [THRIFT-1980](https://issues.apache.org/jira/browse/THRIFT-1980) - Modernize Go tooling, fix go client library. +- [THRIFT-1977](https://issues.apache.org/jira/browse/THRIFT-1977) - C# compiler should generate constant files prefixed with thrift file name +- [THRIFT-1985](https://issues.apache.org/jira/browse/THRIFT-1985) - add a Vagrantfile to build and test Apache Thrift fully reproducible +- [THRIFT-1994](https://issues.apache.org/jira/browse/THRIFT-1994) - Deprecate slist +- [THRIFT-1993](https://issues.apache.org/jira/browse/THRIFT-1993) - Factory to create instances from known (generated) interface types with Delphi +- [THRIFT-2081](https://issues.apache.org/jira/browse/THRIFT-2081) - Specified timeout should be used in TSocket.Open() +- [THRIFT-2084](https://issues.apache.org/jira/browse/THRIFT-2084) - Delphi: Ability to create entity Thrift-generated instances based on TypeInfo +- [THRIFT-2083](https://issues.apache.org/jira/browse/THRIFT-2083) - Improve the go lib: buffered Transport, save memory allocation, handle concurrent request +- [THRIFT-2109](https://issues.apache.org/jira/browse/THRIFT-2109) - Secure connections should be supported in Go +- [THRIFT-2107](https://issues.apache.org/jira/browse/THRIFT-2107) - minor Go generator fixes +- [THRIFT-1695](https://issues.apache.org/jira/browse/THRIFT-1695) - allow warning-free compilation in VS 2012 and GNU 4.6 +- [THRIFT-1735](https://issues.apache.org/jira/browse/THRIFT-1735) - integrate tutorial into regular build +- [THRIFT-1716](https://issues.apache.org/jira/browse/THRIFT-1716) - max allowed connections should be PIPE_UNLIMITED_INSTANCES +- [THRIFT-1715](https://issues.apache.org/jira/browse/THRIFT-1715) - Allow excluding python parts when building contrib/fb303 +- [THRIFT-1733](https://issues.apache.org/jira/browse/THRIFT-1733) - Fix RPM build issues on RHEL6/OL6 systems +- [THRIFT-1728](https://issues.apache.org/jira/browse/THRIFT-1728) - Upgradation of httpcomponents +- [THRIFT-1876](https://issues.apache.org/jira/browse/THRIFT-1876) - Use enum names instead of casted integers in assignments +- [THRIFT-1874](https://issues.apache.org/jira/browse/THRIFT-1874) - timeout for the server-side end of a named pipe +- [THRIFT-1897](https://issues.apache.org/jira/browse/THRIFT-1897) - Support validation of required fields +- [THRIFT-1896](https://issues.apache.org/jira/browse/THRIFT-1896) - Add TBase protocol for Cocoa +- [THRIFT-1880](https://issues.apache.org/jira/browse/THRIFT-1880) - Make named pipes server work asynchronously (overlapped) to allow for clean server stops +- [THRIFT-1878](https://issues.apache.org/jira/browse/THRIFT-1878) - Add the possibility to send custom headers +- [THRIFT-1882](https://issues.apache.org/jira/browse/THRIFT-1882) - Use single include +- [THRIFT-1793](https://issues.apache.org/jira/browse/THRIFT-1793) - C#: Use static read instead of instance read +- [THRIFT-1799](https://issues.apache.org/jira/browse/THRIFT-1799) - Option to generate HTML in "standalone mode" +- [THRIFT-1815](https://issues.apache.org/jira/browse/THRIFT-1815) - Code generators line buffer output +- [THRIFT-1890](https://issues.apache.org/jira/browse/THRIFT-1890) - C++: Make named pipes server work asynchronously +- [THRIFT-474](https://issues.apache.org/jira/browse/THRIFT-474) - Generating Ruby on Rails friendly code + +### New Feature +- [THRIFT-801](https://issues.apache.org/jira/browse/THRIFT-801) - Provide an interactive shell (irb) when generating ruby bindings +- [THRIFT-2292](https://issues.apache.org/jira/browse/THRIFT-2292) - Android Library Project +- [THRIFT-2012](https://issues.apache.org/jira/browse/THRIFT-2012) - Modernizing Go +- [THRIFT-1969](https://issues.apache.org/jira/browse/THRIFT-1969) - C#: Tests not properly linked from the solution +- [THRIFT-1785](https://issues.apache.org/jira/browse/THRIFT-1785) - C#: Add TMemoryBuffer serializer/deserializer +- [THRIFT-1780](https://issues.apache.org/jira/browse/THRIFT-1780) - Add option to generate nullable values +- [THRIFT-1786](https://issues.apache.org/jira/browse/THRIFT-1786) - C# Union Typing +- [THRIFT-591](https://issues.apache.org/jira/browse/THRIFT-591) - Make the C++ runtime library be compatible with Windows and Visual Studio +- [THRIFT-514](https://issues.apache.org/jira/browse/THRIFT-514) - Add option to configure compiler output directory + +### Question +- [THRIFT-1764](https://issues.apache.org/jira/browse/THRIFT-1764) - how to get the context of client when on a rpc call in server side? +- [THRIFT-1791](https://issues.apache.org/jira/browse/THRIFT-1791) - thrift's namespace directive when generating haskell code + +### Sub-task +- [THRIFT-1594](https://issues.apache.org/jira/browse/THRIFT-1594) - Java test clients should have a return codes that reflect whether it succeeds or not. +- [THRIFT-1595](https://issues.apache.org/jira/browse/THRIFT-1595) - Java test server should follow the documented behavior as of THRIFT-1590 +- [THRIFT-986](https://issues.apache.org/jira/browse/THRIFT-986) - st: add version Info to the library +- [THRIFT-985](https://issues.apache.org/jira/browse/THRIFT-985) - php: add version Info to the library +- [THRIFT-984](https://issues.apache.org/jira/browse/THRIFT-984) - ocaml: add version Info to the library +- [THRIFT-1924](https://issues.apache.org/jira/browse/THRIFT-1924) - Delphi: Inconsistency in serialization of optional fields +- [THRIFT-1922](https://issues.apache.org/jira/browse/THRIFT-1922) - C#: Inconsistency in serialization of optional fields +- [THRIFT-1961](https://issues.apache.org/jira/browse/THRIFT-1961) - C# tests should be in lib/csharp/test/... +- [THRIFT-1822](https://issues.apache.org/jira/browse/THRIFT-1822) - PHP unit test does not work +- [THRIFT-1902](https://issues.apache.org/jira/browse/THRIFT-1902) - C++: Support for Multiplexing Services on any Transport, Protocol and Server +- [THRIFT-1901](https://issues.apache.org/jira/browse/THRIFT-1901) - C#: Support for Multiplexing Services on any Transport, Protocol and Server +- [THRIFT-1899](https://issues.apache.org/jira/browse/THRIFT-1899) - Delphi: Support for Multiplexing Services on any Transport, Protocol and Server +- [THRIFT-563](https://issues.apache.org/jira/browse/THRIFT-563) - Support for Multiplexing Services on any Transport, Protocol and Server + +## 0.9 + +### Bug +- [THRIFT-1438](https://issues.apache.org/jira/browse/THRIFT-1438) - lib/cpp/src/windows/config.h should read version from configure.ac rather than a #define +- [THRIFT-1446](https://issues.apache.org/jira/browse/THRIFT-1446) - Compile error with Delphi 2009 in constant initializer +- [THRIFT-1450](https://issues.apache.org/jira/browse/THRIFT-1450) - Problems building thrift 0.8.0 for Python and Ruby +- [THRIFT-1449](https://issues.apache.org/jira/browse/THRIFT-1449) - Ruby client does not work on solaris (?) +- [THRIFT-1447](https://issues.apache.org/jira/browse/THRIFT-1447) - NullpointerException in ProcessFunction.class :in "oneway" method +- [THRIFT-1433](https://issues.apache.org/jira/browse/THRIFT-1433) - TServerSocket fix for MSVC +- [THRIFT-1429](https://issues.apache.org/jira/browse/THRIFT-1429) - The nonblocking servers is supposed to use TransportFactory to read the data +- [THRIFT-1427](https://issues.apache.org/jira/browse/THRIFT-1427) - PHP library uses non-multibyte safe functions with mbstring function overloading +- [THRIFT-1421](https://issues.apache.org/jira/browse/THRIFT-1421) - Debian Packages can not be built +- [THRIFT-1394](https://issues.apache.org/jira/browse/THRIFT-1394) - Treatment of optional fields is not consistent between C++ and Java +- [THRIFT-1511](https://issues.apache.org/jira/browse/THRIFT-1511) - Server with oneway support ( JAVA ) +- [THRIFT-1496](https://issues.apache.org/jira/browse/THRIFT-1496) - PHP compiler not namespacing enums +- [THRIFT-1495](https://issues.apache.org/jira/browse/THRIFT-1495) - PHP TestClient fatals on missing class +- [THRIFT-1508](https://issues.apache.org/jira/browse/THRIFT-1508) - TServerSocket does not allow for the user to specify the IP address to bind to +- [THRIFT-1504](https://issues.apache.org/jira/browse/THRIFT-1504) - Cocoa Generator should use local file imports for base Thrift headers +- [THRIFT-1512](https://issues.apache.org/jira/browse/THRIFT-1512) - Thrift socket support for Windows XP +- [THRIFT-1502](https://issues.apache.org/jira/browse/THRIFT-1502) - TSimpleServer::serve(): Do not print out error message if server was stopped. +- [THRIFT-1501](https://issues.apache.org/jira/browse/THRIFT-1501) - PHP old namespaces not generated for enums +- [THRIFT-1483](https://issues.apache.org/jira/browse/THRIFT-1483) - java compiler does not generate type parameters for services in extended clauses +- [THRIFT-1479](https://issues.apache.org/jira/browse/THRIFT-1479) - Compiled PHP process functions missing writeMessageEnd() +- [THRIFT-1492](https://issues.apache.org/jira/browse/THRIFT-1492) - enabling c_glib render thrift unusable (even for C++ code) +- [THRIFT-1491](https://issues.apache.org/jira/browse/THRIFT-1491) - Uninitialize processorFactory_ member in TServer.h +- [THRIFT-1475](https://issues.apache.org/jira/browse/THRIFT-1475) - Incomplete records generation for Erlang +- [THRIFT-1486](https://issues.apache.org/jira/browse/THRIFT-1486) - Javascript manual testserver not returning content types +- [THRIFT-1488](https://issues.apache.org/jira/browse/THRIFT-1488) - src/concurrency/Thread.h:91:58: error: invalid conversion from 'pthread_t {aka _opaque_pthread_t*}' to 'apache::thrift::concurrency::Thread::id_t {aka long long unsigned int}' [-fpermissive] +- [THRIFT-1490](https://issues.apache.org/jira/browse/THRIFT-1490) - Windows-specific header files - fixes & tweaks +- [THRIFT-1526](https://issues.apache.org/jira/browse/THRIFT-1526) - Union TupleSchemeFactory returns StandardSchemes +- [THRIFT-1527](https://issues.apache.org/jira/browse/THRIFT-1527) - Generated implementation of tupleReadStruct in unions return null when the setfield is unrecognized +- [THRIFT-1524](https://issues.apache.org/jira/browse/THRIFT-1524) - TNonBlockingServer does not compile in Visual Studio 2010 +- [THRIFT-1529](https://issues.apache.org/jira/browse/THRIFT-1529) - TupleProtocol can unintentionally include an extra byte in bit vectors when number of optional fields is an integral of 8 +- [THRIFT-1473](https://issues.apache.org/jira/browse/THRIFT-1473) - JSON context stack may be left in an incorrect state when an exception is thrown during read or write operations +- [THRIFT-1456](https://issues.apache.org/jira/browse/THRIFT-1456) - System.Net.HttpWebRequest' does not contain a definition for 'Proxy' +- [THRIFT-1468](https://issues.apache.org/jira/browse/THRIFT-1468) - Memory leak in TSaslServerTransport +- [THRIFT-1461](https://issues.apache.org/jira/browse/THRIFT-1461) - Recent TNonblockingServer changes broke --enable-boostthreads=yes, Windows +- [THRIFT-1460](https://issues.apache.org/jira/browse/THRIFT-1460) - why not add unicode strings support to python directly? +- [THRIFT-1464](https://issues.apache.org/jira/browse/THRIFT-1464) - AbstractNonblockingServer.FrameBuffer TNonblockingTransport accessor changed from public to private +- [THRIFT-1467](https://issues.apache.org/jira/browse/THRIFT-1467) - Possible AV with empty strings when using JSON protocol +- [THRIFT-1523](https://issues.apache.org/jira/browse/THRIFT-1523) - clientTimeout not worked as expected in TServerSocket created by TSSLTransportFactory +- [THRIFT-1537](https://issues.apache.org/jira/browse/THRIFT-1537) - TFramedTransport issues +- [THRIFT-1519](https://issues.apache.org/jira/browse/THRIFT-1519) - Thirft Build Failure referencing rb_intern2 symbol +- [THRIFT-1518](https://issues.apache.org/jira/browse/THRIFT-1518) - Generated C++ code only sends the first optional field in the write() function for a struct. +- [THRIFT-1515](https://issues.apache.org/jira/browse/THRIFT-1515) - NameError: global name 'TApplicationException' is not defined +- [THRIFT-1554](https://issues.apache.org/jira/browse/THRIFT-1554) - Inherited service methods are not resolved in derived service implementations +- [THRIFT-1553](https://issues.apache.org/jira/browse/THRIFT-1553) - thrift nodejs service side can't read map structure, key as enum, value as Object +- [THRIFT-1575](https://issues.apache.org/jira/browse/THRIFT-1575) - Typo in server/TThreadPoolServer.h +- [THRIFT-1327](https://issues.apache.org/jira/browse/THRIFT-1327) - Fix Spec Suite under Ruby-1.8.7 (works for MRI Ruby-1.9.2) +- [THRIFT-1326](https://issues.apache.org/jira/browse/THRIFT-1326) - on some platforms, #include is necessary to be included in Thrift.h +- [THRIFT-1159](https://issues.apache.org/jira/browse/THRIFT-1159) - THttpClient->Flush() issue (connection thru proxy) +- [THRIFT-1277](https://issues.apache.org/jira/browse/THRIFT-1277) - Node.js serializes false booleans as null +- [THRIFT-1224](https://issues.apache.org/jira/browse/THRIFT-1224) - Cannot insert UTF-8 text +- [THRIFT-1267](https://issues.apache.org/jira/browse/THRIFT-1267) - Node.js can't throw exceptions. +- [THRIFT-1338](https://issues.apache.org/jira/browse/THRIFT-1338) - Do not use an unpatched autoconf 2.65 to generate release tarball +- [THRIFT-1128](https://issues.apache.org/jira/browse/THRIFT-1128) - MAC OS X: thrift.h incompatibility with Thrift.h +- [THRIFT-1631](https://issues.apache.org/jira/browse/THRIFT-1631) - Fix C++ server constructor typos +- [THRIFT-1602](https://issues.apache.org/jira/browse/THRIFT-1602) - PHP C Extension is not Compatible with PHP 5.4 +- [THRIFT-1610](https://issues.apache.org/jira/browse/THRIFT-1610) - IWebProxy not available on WP7 platform +- [THRIFT-1606](https://issues.apache.org/jira/browse/THRIFT-1606) - Race condition in BoostThreadFactory.cpp +- [THRIFT-1604](https://issues.apache.org/jira/browse/THRIFT-1604) - Python exception handeling for changes from PEP 3110 +- [THRIFT-1607](https://issues.apache.org/jira/browse/THRIFT-1607) - Incorrect file modes for several source files +- [THRIFT-1583](https://issues.apache.org/jira/browse/THRIFT-1583) - c_glib leaks memory +- [THRIFT-1582](https://issues.apache.org/jira/browse/THRIFT-1582) - Bad includes of nested thrift files in c_glib +- [THRIFT-1578](https://issues.apache.org/jira/browse/THRIFT-1578) - C_GLib generated code does not compile +- [THRIFT-1597](https://issues.apache.org/jira/browse/THRIFT-1597) - TJSONProtocol.php is missing from Makefile.am +- [THRIFT-1591](https://issues.apache.org/jira/browse/THRIFT-1591) - Enable TCP_NODELAY for ruby gem +- [THRIFT-1624](https://issues.apache.org/jira/browse/THRIFT-1624) - Isset Generated differently on different platforms +- [THRIFT-1622](https://issues.apache.org/jira/browse/THRIFT-1622) - Incorrect size returned on read +- [THRIFT-1621](https://issues.apache.org/jira/browse/THRIFT-1621) - Memory leaks +- [THRIFT-1612](https://issues.apache.org/jira/browse/THRIFT-1612) - Base64 encoding is broken +- [THRIFT-1627](https://issues.apache.org/jira/browse/THRIFT-1627) - compiler built using compilers.vcxproj cannot be used to build some test .thrift files +- [THRIFT-1571](https://issues.apache.org/jira/browse/THRIFT-1571) - Update Ruby HTTP transport for recent Ruby versions +- [THRIFT-1023](https://issues.apache.org/jira/browse/THRIFT-1023) - Thrift encoding (UTF-8) issue with Ruby 1.9.2 +- [THRIFT-1090](https://issues.apache.org/jira/browse/THRIFT-1090) - Document the generation of a file called "Constants.java" +- [THRIFT-1082](https://issues.apache.org/jira/browse/THRIFT-1082) - Thrift::FramedTransport sometimes calls close() on an undefined value +- [THRIFT-956](https://issues.apache.org/jira/browse/THRIFT-956) - Python module's version meta-data should be updated +- [THRIFT-973](https://issues.apache.org/jira/browse/THRIFT-973) - Cocoa library won't compile using clang +- [THRIFT-1632](https://issues.apache.org/jira/browse/THRIFT-1632) - ruby: data corruption in thrift_native implementation of MemoryBufferTransport +- [THRIFT-1665](https://issues.apache.org/jira/browse/THRIFT-1665) - TBinaryProtocol: exceeded message length raises generic TException +- [THRIFT-1664](https://issues.apache.org/jira/browse/THRIFT-1664) - Reference to non-existing variable in build script +- [THRIFT-1663](https://issues.apache.org/jira/browse/THRIFT-1663) - Java Thrift server is not throwing exceptions +- [THRIFT-1662](https://issues.apache.org/jira/browse/THRIFT-1662) - "removeObject:" should be "removeObserver:" in [-TSocketServer dealloc]? +- [THRIFT-1643](https://issues.apache.org/jira/browse/THRIFT-1643) - Denial of Service attack in TBinaryProtocol.readString +- [THRIFT-1674](https://issues.apache.org/jira/browse/THRIFT-1674) - Update Thrift D library to be compatible with 2.060 +- [THRIFT-1673](https://issues.apache.org/jira/browse/THRIFT-1673) - Ruby compile flags for extension for multi arch builds (os x) +- [THRIFT-1655](https://issues.apache.org/jira/browse/THRIFT-1655) - Configure still trying to use thrift_generators in output +- [THRIFT-1654](https://issues.apache.org/jira/browse/THRIFT-1654) - c_glib thrift_socket_read() returns corrupted data +- [THRIFT-1653](https://issues.apache.org/jira/browse/THRIFT-1653) - TThreadedSelectorServer leaks CLOSE_WAIT sockets +- [THRIFT-1658](https://issues.apache.org/jira/browse/THRIFT-1658) - Java thrift server is not throwing TApplicationException +- [THRIFT-1656](https://issues.apache.org/jira/browse/THRIFT-1656) - Setting proper headers in THttpServer.cpp so that "Cross-Origin Resource Sharing" on js client can work. +- [THRIFT-1652](https://issues.apache.org/jira/browse/THRIFT-1652) - TSaslTransport does not log the error when kerberos auth fails +- [THRIFT-2272](https://issues.apache.org/jira/browse/THRIFT-2272) - CLONE - Denial of Service attack in TBinaryProtocol.readString +- [THRIFT-2086](https://issues.apache.org/jira/browse/THRIFT-2086) - Invalid generated code for Node.JS when using namespaces +- [THRIFT-1686](https://issues.apache.org/jira/browse/THRIFT-1686) - t_php_generator.cc uses "and" instead of "&&", and causes compiler errors with Visual Studio +- [THRIFT-1693](https://issues.apache.org/jira/browse/THRIFT-1693) - libthrift has dependency on two different versions of httpcore +- [THRIFT-1689](https://issues.apache.org/jira/browse/THRIFT-1689) - don't exit(-1) in TNonblockingServer +- [THRIFT-1679](https://issues.apache.org/jira/browse/THRIFT-1679) - NodeJS: protocol readString() should treat string as utf8, not binary +- [THRIFT-1721](https://issues.apache.org/jira/browse/THRIFT-1721) - Dist broken due to 0.8.0 to 0.9.0 changes +- [THRIFT-1710](https://issues.apache.org/jira/browse/THRIFT-1710) - Minor issues in test case code +- [THRIFT-1709](https://issues.apache.org/jira/browse/THRIFT-1709) - Warning "Bitwise-or operator used on a sign-extended operand; consider casting to a smaller unsigned type first" in TBinaryProtocol.cs at ReadInt64() +- [THRIFT-1707](https://issues.apache.org/jira/browse/THRIFT-1707) - [ruby] Adjust server_spec.rb for RSpec 2.11.x and Ruby 1.9.3 +- [THRIFT-1671](https://issues.apache.org/jira/browse/THRIFT-1671) - Cocoa code generator does not put keywords into generated method calls +- [THRIFT-1670](https://issues.apache.org/jira/browse/THRIFT-1670) - Incompatibilities between different versions of a Thrift interface +- [THRIFT-1669](https://issues.apache.org/jira/browse/THRIFT-1669) - NameError: global name 'TApplicationException' is not defined +- [THRIFT-1668](https://issues.apache.org/jira/browse/THRIFT-1668) - Compile error in contrib/fb303, thrift/TDispatchProcessor.h: No such file or directory +- [THRIFT-1845](https://issues.apache.org/jira/browse/THRIFT-1845) - Fix compiler warning caused by implicit string conversion with Xcode 4.6 +- [THRIFT-304](https://issues.apache.org/jira/browse/THRIFT-304) - Building the Python library requires development headers +- [THRIFT-369](https://issues.apache.org/jira/browse/THRIFT-369) - sets and maps break equality +- [THRIFT-556](https://issues.apache.org/jira/browse/THRIFT-556) - Ruby compiler does not correctly referred to top-level modules when a submodule masks the top-level name +- [THRIFT-481](https://issues.apache.org/jira/browse/THRIFT-481) - indentation of ruby classes is off by a few + +### Improvement +- [THRIFT-1498](https://issues.apache.org/jira/browse/THRIFT-1498) - Allow TThreadedPoolServer.Args to pass a ExecutorService +- [THRIFT-1444](https://issues.apache.org/jira/browse/THRIFT-1444) - FunctionRunner - add syntactic sugar to create shared_ptrs +- [THRIFT-1443](https://issues.apache.org/jira/browse/THRIFT-1443) - define a TProcessor helper class to implement process() +- [THRIFT-1441](https://issues.apache.org/jira/browse/THRIFT-1441) - Generate constructor with parameters for exception class to let it update message property automatically. +- [THRIFT-1520](https://issues.apache.org/jira/browse/THRIFT-1520) - Embed version number in erlang .app file +- [THRIFT-1480](https://issues.apache.org/jira/browse/THRIFT-1480) - python: remove tabs, adjust whitespace and address PEP8 warnings +- [THRIFT-1485](https://issues.apache.org/jira/browse/THRIFT-1485) - Performance: pass large and/or refcounted arguments as "const" +- [THRIFT-1484](https://issues.apache.org/jira/browse/THRIFT-1484) - Introduce phpunit test suite +- [THRIFT-1532](https://issues.apache.org/jira/browse/THRIFT-1532) - The type specifications in the generated Erlang code should include "undefined" where it's used as a default value +- [THRIFT-1534](https://issues.apache.org/jira/browse/THRIFT-1534) - Required fields in the Delphi code generator. +- [THRIFT-1469](https://issues.apache.org/jira/browse/THRIFT-1469) - Java isset space optimization +- [THRIFT-1465](https://issues.apache.org/jira/browse/THRIFT-1465) - Visibility of methods in generated java code +- [THRIFT-1453](https://issues.apache.org/jira/browse/THRIFT-1453) - Don't change types of arguments when serializing with thrift php extension +- [THRIFT-1452](https://issues.apache.org/jira/browse/THRIFT-1452) - generate a swap() method for all generated structs +- [THRIFT-1451](https://issues.apache.org/jira/browse/THRIFT-1451) - FramedTransport: Prevent infinite loop when writing +- [THRIFT-1521](https://issues.apache.org/jira/browse/THRIFT-1521) - Two patches for more Performance +- [THRIFT-1555](https://issues.apache.org/jira/browse/THRIFT-1555) - Delphi version of the tutorial code +- [THRIFT-1535](https://issues.apache.org/jira/browse/THRIFT-1535) - Why thrift don't use wrapped class for optional fields ? +- [THRIFT-1204](https://issues.apache.org/jira/browse/THRIFT-1204) - Ruby autogenerated files should require 'thrift' gem +- [THRIFT-1344](https://issues.apache.org/jira/browse/THRIFT-1344) - Using the httpc module directly rather than the deprecated http layer +- [THRIFT-1343](https://issues.apache.org/jira/browse/THRIFT-1343) - no_auto_import min/2 to avoid compile warning +- [THRIFT-1340](https://issues.apache.org/jira/browse/THRIFT-1340) - Add support of ARC to Objective-C +- [THRIFT-1611](https://issues.apache.org/jira/browse/THRIFT-1611) - Improved code generation for typedefs +- [THRIFT-1593](https://issues.apache.org/jira/browse/THRIFT-1593) - Pass on errors like "connection closed" to the handler module +- [THRIFT-1615](https://issues.apache.org/jira/browse/THRIFT-1615) - PHP Namespace +- [THRIFT-1567](https://issues.apache.org/jira/browse/THRIFT-1567) - Thrift/cpp: Allow alternate classes to be used for +- [THRIFT-1072](https://issues.apache.org/jira/browse/THRIFT-1072) - Missing - (id) initWithSharedProcessor in TSharedProcessorFactory.h +- [THRIFT-1650](https://issues.apache.org/jira/browse/THRIFT-1650) - [ruby] Update clean items and svn:ignore entries for OS X artifacts +- [THRIFT-1661](https://issues.apache.org/jira/browse/THRIFT-1661) - [PATCH] Add --with-qt4 configure option +- [THRIFT-1675](https://issues.apache.org/jira/browse/THRIFT-1675) - Do we have any plan to support scala? +- [THRIFT-1645](https://issues.apache.org/jira/browse/THRIFT-1645) - Replace Object#tee with more conventional Object#tap in specs +- [THRIFT-1644](https://issues.apache.org/jira/browse/THRIFT-1644) - Upgrade RSpec to 2.10.x and refactor specs as needed +- [THRIFT-1672](https://issues.apache.org/jira/browse/THRIFT-1672) - MonoTouch (and Mono for Android) compatibility +- [THRIFT-1702](https://issues.apache.org/jira/browse/THRIFT-1702) - a thrift manual +- [THRIFT-1694](https://issues.apache.org/jira/browse/THRIFT-1694) - Re-Enable serialization for WP7 Silverlight +- [THRIFT-1691](https://issues.apache.org/jira/browse/THRIFT-1691) - Serializer/deserializer support for Delphi +- [THRIFT-1688](https://issues.apache.org/jira/browse/THRIFT-1688) - Update IDL page markup +- [THRIFT-1725](https://issues.apache.org/jira/browse/THRIFT-1725) - Tutorial web pages for Delphi and C# +- [THRIFT-1714](https://issues.apache.org/jira/browse/THRIFT-1714) - [ruby] Explicitly add CWD to Ruby test_suites.rb +- [THRIFT-317](https://issues.apache.org/jira/browse/THRIFT-317) - Issues with Java struct validation +- [THRIFT-164](https://issues.apache.org/jira/browse/THRIFT-164) - Build web tutorial on Incubator web site +- [THRIFT-541](https://issues.apache.org/jira/browse/THRIFT-541) - Cocoa code generator doesn't put keywords before all arguments. +- [THRIFT-681](https://issues.apache.org/jira/browse/THRIFT-681) - The HTML generator does not handle JavaDoc style comments very well + +### New Feature +- [THRIFT-1500](https://issues.apache.org/jira/browse/THRIFT-1500) - D programming language support +- [THRIFT-1510](https://issues.apache.org/jira/browse/THRIFT-1510) - There should be an implementation of the JsonProtocol for ruby +- [THRIFT-1115](https://issues.apache.org/jira/browse/THRIFT-1115) - python TBase class for dynamic (de)serialization, and __slots__ option for memory savings +- [THRIFT-1953](https://issues.apache.org/jira/browse/THRIFT-1953) - support for asp.net mvc 3 + +### Question +- [THRIFT-1235](https://issues.apache.org/jira/browse/THRIFT-1235) - How could I use THttpServerTransportFactory withTNonBlockingServer +- [THRIFT-1368](https://issues.apache.org/jira/browse/THRIFT-1368) - TNonblockingServer usage +- [THRIFT-1061](https://issues.apache.org/jira/browse/THRIFT-1061) - Read an invalid frame size of 0. Are you using TFramedTransport on the client side? +- [THRIFT-491](https://issues.apache.org/jira/browse/THRIFT-491) - Ripping raw pthreads out of TFileTransport and associated test issues + +### Sub-task +- [THRIFT-1596](https://issues.apache.org/jira/browse/THRIFT-1596) - Delphi: Test clients should have a return codes that reflect whether they succeeded or not +- [THRIFT-982](https://issues.apache.org/jira/browse/THRIFT-982) - javame: add version Info to the library +- [THRIFT-1722](https://issues.apache.org/jira/browse/THRIFT-1722) - C# WP7 Assembly addition beaks mono build +- [THRIFT-336](https://issues.apache.org/jira/browse/THRIFT-336) - Compact Protocol in C# + +### Test +- [THRIFT-1613](https://issues.apache.org/jira/browse/THRIFT-1613) - Add code back into empty source file ToStringTest.java +- [THRIFT-1718](https://issues.apache.org/jira/browse/THRIFT-1718) - Incorrect check in TFileTransportTest + +### Wish +- [THRIFT-1463](https://issues.apache.org/jira/browse/THRIFT-1463) - Decouple Thrift IDL from generators +- [THRIFT-1466](https://issues.apache.org/jira/browse/THRIFT-1466) - Proper Documentation for Thrift C Glib +- [THRIFT-1539](https://issues.apache.org/jira/browse/THRIFT-1539) - Build and distribute the fb303 python libraries along with thrift +- [THRIFT-1685](https://issues.apache.org/jira/browse/THRIFT-1685) - Please add "aereo.com" to "Powered by Apache Thrift" list in about page +- [THRIFT-330](https://issues.apache.org/jira/browse/THRIFT-330) - TProcessor - additional method to called when connection is broken + +## 0.8 + +### Bug +- [THRIFT-1436](https://issues.apache.org/jira/browse/THRIFT-1436) - pip install thrift fails on Windows with "Unable to find vcvarsall.bat" +- [THRIFT-1432](https://issues.apache.org/jira/browse/THRIFT-1432) - Javascript struct constants declared in the same file as their struct definition will cause an error +- [THRIFT-1428](https://issues.apache.org/jira/browse/THRIFT-1428) - shared.thrft does not include namespace for php, so thrift compiler generate incorrect name +- [THRIFT-1426](https://issues.apache.org/jira/browse/THRIFT-1426) - Dist package missing files for release 0.8 +- [THRIFT-1425](https://issues.apache.org/jira/browse/THRIFT-1425) - The Node package is incompatible with latest node (0.6) & npm (1.0.27) +- [THRIFT-1416](https://issues.apache.org/jira/browse/THRIFT-1416) - Python Unit test is broken on ci +- [THRIFT-1419](https://issues.apache.org/jira/browse/THRIFT-1419) - AbstractNonBlockingServer does not catch errors when invoking the processor +- [THRIFT-1424](https://issues.apache.org/jira/browse/THRIFT-1424) - Ruby specs fail when run with rake +- [THRIFT-1420](https://issues.apache.org/jira/browse/THRIFT-1420) - Nonblocking and HsHa server should make sure to close all their socket connections when the selector exits +- [THRIFT-1413](https://issues.apache.org/jira/browse/THRIFT-1413) - Generated code does not read MapEnd / ListEnd / SetEnd +- [THRIFT-1409](https://issues.apache.org/jira/browse/THRIFT-1409) - Name conflict check does not work properly for exception object(Delphi). +- [THRIFT-1408](https://issues.apache.org/jira/browse/THRIFT-1408) - Delphi Test Server: Exception test case fails due to naming conflict with e.message +- [THRIFT-1407](https://issues.apache.org/jira/browse/THRIFT-1407) - Typo in Python socket server causes Thrift to fail when we enable a global socket timout +- [THRIFT-1397](https://issues.apache.org/jira/browse/THRIFT-1397) - CI server fails during build due to unused parameters in delphi generator +- [THRIFT-1404](https://issues.apache.org/jira/browse/THRIFT-1404) - Delphi compiler generates struct reader code with problem. +- [THRIFT-1400](https://issues.apache.org/jira/browse/THRIFT-1400) - Ruby native extension aborts with __stack_chk_fail in OSX +- [THRIFT-1399](https://issues.apache.org/jira/browse/THRIFT-1399) - One of the TServerImpl.Create CTORs lacks implementation +- [THRIFT-1390](https://issues.apache.org/jira/browse/THRIFT-1390) - Debian packages build fix for Squeeze (build from the official 0.7.0 tarball) +- [THRIFT-1393](https://issues.apache.org/jira/browse/THRIFT-1393) - TTransportException's thrown from THttpClient contain superfluous slashes in the Exception message +- [THRIFT-1392](https://issues.apache.org/jira/browse/THRIFT-1392) - Enabling both namespaces and autoloading in generated PHP code won't work. +- [THRIFT-1406](https://issues.apache.org/jira/browse/THRIFT-1406) - Build error after applying THRIFT-1395 +- [THRIFT-1405](https://issues.apache.org/jira/browse/THRIFT-1405) - Delphi compiler does not generates container serializer properly. +- [THRIFT-1411](https://issues.apache.org/jira/browse/THRIFT-1411) - java generator does not provide type parameter for TBaseProcessor +- [THRIFT-1473](https://issues.apache.org/jira/browse/THRIFT-1473) - JSON context stack may be left in an incorrect state when an exception is thrown during read or write operations +- [THRIFT-1331](https://issues.apache.org/jira/browse/THRIFT-1331) - Ruby library deserializes an empty map to nil +- [THRIFT-1330](https://issues.apache.org/jira/browse/THRIFT-1330) - PHP Namespaces no longer generated +- [THRIFT-1328](https://issues.apache.org/jira/browse/THRIFT-1328) - TBaseHelper.toString(...) appends ByteBuffer data outside of valid buffer range +- [THRIFT-1322](https://issues.apache.org/jira/browse/THRIFT-1322) - OCaml lib fail to compile: Thrift.ml line 305, int vs int32 mismatch +- [THRIFT-1143](https://issues.apache.org/jira/browse/THRIFT-1143) - Build doesn't detect correct architecture type on 64bit osx +- [THRIFT-1205](https://issues.apache.org/jira/browse/THRIFT-1205) - port server unduly fragile with arbitrary input +- [THRIFT-1279](https://issues.apache.org/jira/browse/THRIFT-1279) - type set is handled incorrectly when writing object +- [THRIFT-1298](https://issues.apache.org/jira/browse/THRIFT-1298) - Standard scheme doesn't read or write metadata along with field values +- [THRIFT-1265](https://issues.apache.org/jira/browse/THRIFT-1265) - C++ container deserialize +- [THRIFT-1263](https://issues.apache.org/jira/browse/THRIFT-1263) - publish ruby client to rubygems +- [THRIFT-1384](https://issues.apache.org/jira/browse/THRIFT-1384) - Java help menu missing newline near javame flag +- [THRIFT-1382](https://issues.apache.org/jira/browse/THRIFT-1382) - Bundle install doesnot work because thrift crashes +- [THRIFT-1381](https://issues.apache.org/jira/browse/THRIFT-1381) - Thrift C++ libs have incorrectly versioned names +- [THRIFT-1350](https://issues.apache.org/jira/browse/THRIFT-1350) - Go library code does not build as of r60 (most recent release) +- [THRIFT-1365](https://issues.apache.org/jira/browse/THRIFT-1365) - TupleProtocol#writeBitSet unintentionally writes a variable length byte array +- [THRIFT-1359](https://issues.apache.org/jira/browse/THRIFT-1359) - --gen-cob cpp:cob_style does not compile anymore +- [THRIFT-1319](https://issues.apache.org/jira/browse/THRIFT-1319) - Mismatch between how a union reads and writes a container +- [THRIFT-1309](https://issues.apache.org/jira/browse/THRIFT-1309) - libfb303-0.7.0.jar missing in maven repository +- [THRIFT-1238](https://issues.apache.org/jira/browse/THRIFT-1238) - Thrift JS client cannot read map of structures +- [THRIFT-1254](https://issues.apache.org/jira/browse/THRIFT-1254) - Code can't be compiled against a regular JRE: Object.clone() override has a different return type +- [THRIFT-1367](https://issues.apache.org/jira/browse/THRIFT-1367) - Mac OSX build fails with "no such file to load -- spec/rake/spectask" +- [THRIFT-1355](https://issues.apache.org/jira/browse/THRIFT-1355) - Running make in lib/rb doesn't build the native extensions +- [THRIFT-1370](https://issues.apache.org/jira/browse/THRIFT-1370) - Debian packaging should Build-Depend on libglib2.0-dev +- [THRIFT-1342](https://issues.apache.org/jira/browse/THRIFT-1342) - Compilation problem on Windows of fastbinary.c +- [THRIFT-1341](https://issues.apache.org/jira/browse/THRIFT-1341) - TProtocol.h endian detection wrong with boost +- [THRIFT-1583](https://issues.apache.org/jira/browse/THRIFT-1583) - c_glib leaks memory +- [THRIFT-1582](https://issues.apache.org/jira/browse/THRIFT-1582) - Bad includes of nested thrift files in c_glib +- [THRIFT-1578](https://issues.apache.org/jira/browse/THRIFT-1578) - C_GLib generated code does not compile +- [THRIFT-1027](https://issues.apache.org/jira/browse/THRIFT-1027) - 'make -j 16' fails with "unterminated #ifdef" error +- [THRIFT-1121](https://issues.apache.org/jira/browse/THRIFT-1121) - Java server performance regression in 0.6 +- [THRIFT-857](https://issues.apache.org/jira/browse/THRIFT-857) - tests run by "make install" fail if generators are disabled +- [THRIFT-380](https://issues.apache.org/jira/browse/THRIFT-380) - Use setuptools for python build + +### Dependency upgrade +- [THRIFT-1257](https://issues.apache.org/jira/browse/THRIFT-1257) - thrift's dependency scope on javax.servlet:servlet-api should be 'provided' + +### Improvement +- [THRIFT-1445](https://issues.apache.org/jira/browse/THRIFT-1445) - minor C++ generator variable cleanup +- [THRIFT-1435](https://issues.apache.org/jira/browse/THRIFT-1435) - make TException.Message property conformant to the usual expectations +- [THRIFT-1431](https://issues.apache.org/jira/browse/THRIFT-1431) - Rename 'sys' module to 'util' +- [THRIFT-1396](https://issues.apache.org/jira/browse/THRIFT-1396) - Dephi generator has dependacy on boost 1.42 later. +- [THRIFT-1395](https://issues.apache.org/jira/browse/THRIFT-1395) - Patch to prevent warnings for integer types in some cases +- [THRIFT-1275](https://issues.apache.org/jira/browse/THRIFT-1275) - thrift: always prefix namespaces with " ::" +- [THRIFT-1274](https://issues.apache.org/jira/browse/THRIFT-1274) - thrift: fail compilation if an unexpected token is +- [THRIFT-1271](https://issues.apache.org/jira/browse/THRIFT-1271) - thrift: fix missing namespace in generated local +- [THRIFT-1270](https://issues.apache.org/jira/browse/THRIFT-1270) - thrift: add --allow-neg-keys argument to allow +- [THRIFT-1345](https://issues.apache.org/jira/browse/THRIFT-1345) - Allow building without tests +- [THRIFT-1286](https://issues.apache.org/jira/browse/THRIFT-1286) - Modernize the Thrift Ruby Library Dev Environment +- [THRIFT-1284](https://issues.apache.org/jira/browse/THRIFT-1284) - thrift: fix processor inheritance +- [THRIFT-1283](https://issues.apache.org/jira/browse/THRIFT-1283) - thrift: wrap t_cpp_generator::generate_process_function() to 80 +- [THRIFT-1282](https://issues.apache.org/jira/browse/THRIFT-1282) - Upgrade httpclient to 4.1.2 (from 4.0.1) +- [THRIFT-1281](https://issues.apache.org/jira/browse/THRIFT-1281) - add @generated to the docblock +- [THRIFT-1280](https://issues.apache.org/jira/browse/THRIFT-1280) - Thrift: Improve Monitor exception-free interfaces +- [THRIFT-1278](https://issues.apache.org/jira/browse/THRIFT-1278) - javadoc warnings - compilation +- [THRIFT-1227](https://issues.apache.org/jira/browse/THRIFT-1227) - Erlang implementation of thrift JSON protocol +- [THRIFT-1295](https://issues.apache.org/jira/browse/THRIFT-1295) - Duplicate include in TSocket.cpp +- [THRIFT-1294](https://issues.apache.org/jira/browse/THRIFT-1294) - thrift: fix log message typos in TSimpleServer +- [THRIFT-1293](https://issues.apache.org/jira/browse/THRIFT-1293) - thrift: improve handling of exceptions thrown by +- [THRIFT-1292](https://issues.apache.org/jira/browse/THRIFT-1292) - thrift: silence log spew from TThreadedServer +- [THRIFT-1288](https://issues.apache.org/jira/browse/THRIFT-1288) - Allow typedefed exceptions in throws clauses +- [THRIFT-1290](https://issues.apache.org/jira/browse/THRIFT-1290) - thrift: TNonblockingServer: clean up state in the +- [THRIFT-1287](https://issues.apache.org/jira/browse/THRIFT-1287) - thrift: start refactoring some of the C++ processor +- [THRIFT-1289](https://issues.apache.org/jira/browse/THRIFT-1289) - thrift: implement TNonblockingServer::stop() +- [THRIFT-1305](https://issues.apache.org/jira/browse/THRIFT-1305) - thrift: make TConnection a private inner class of +- [THRIFT-1304](https://issues.apache.org/jira/browse/THRIFT-1304) - TNonblockingServer: pass in the connection context to +- [THRIFT-1302](https://issues.apache.org/jira/browse/THRIFT-1302) - thrift: raise an exception if send() times out in +- [THRIFT-1301](https://issues.apache.org/jira/browse/THRIFT-1301) - thrift: consolidate common code in TNonblockingServer +- [THRIFT-1377](https://issues.apache.org/jira/browse/THRIFT-1377) - abort PHP deserialization on unknown field type +- [THRIFT-1379](https://issues.apache.org/jira/browse/THRIFT-1379) - fix uninitialized enum values in thrift C++ objects +- [THRIFT-1376](https://issues.apache.org/jira/browse/THRIFT-1376) - Make port specification option in thrift remote +- [THRIFT-1375](https://issues.apache.org/jira/browse/THRIFT-1375) - fixed a hex char conversion bug in TJSONProtocol +- [THRIFT-1373](https://issues.apache.org/jira/browse/THRIFT-1373) - Fix user-defined exception generation in thrift (python) +- [THRIFT-1361](https://issues.apache.org/jira/browse/THRIFT-1361) - Optional replacement of pthread by boost::thread +- [THRIFT-1320](https://issues.apache.org/jira/browse/THRIFT-1320) - Consistency of configure generated config.h +- [THRIFT-1317](https://issues.apache.org/jira/browse/THRIFT-1317) - Remove copy constructibility from +- [THRIFT-1316](https://issues.apache.org/jira/browse/THRIFT-1316) - thrift: update server classes to accept +- [THRIFT-1315](https://issues.apache.org/jira/browse/THRIFT-1315) - thrift: generate server interface factory classes +- [THRIFT-1314](https://issues.apache.org/jira/browse/THRIFT-1314) - thrift: add TProcessorFactory +- [THRIFT-1335](https://issues.apache.org/jira/browse/THRIFT-1335) - Add accept timeout to TServerSocket +- [THRIFT-1334](https://issues.apache.org/jira/browse/THRIFT-1334) - Add more info to IllegalStateException +- [THRIFT-1333](https://issues.apache.org/jira/browse/THRIFT-1333) - Make RWGuard not copyable +- [THRIFT-1332](https://issues.apache.org/jira/browse/THRIFT-1332) - TSSLTransportParameters class uses hard coded value keyManagerType: SunX509 +- [THRIFT-1251](https://issues.apache.org/jira/browse/THRIFT-1251) - Generated java code should indicate which fields are required and which are optional +- [THRIFT-1387](https://issues.apache.org/jira/browse/THRIFT-1387) - Build MSVC libraries with Boost Threads instead of Pthreads +- [THRIFT-1339](https://issues.apache.org/jira/browse/THRIFT-1339) - Extend Tuple Protocol to TUnions +- [THRIFT-1031](https://issues.apache.org/jira/browse/THRIFT-1031) - Patch to compile Thrift for vc++ 9.0 and 10.0 +- [THRIFT-1130](https://issues.apache.org/jira/browse/THRIFT-1130) - Add the ability to specify symbolic default value for optional boolean +- [THRIFT-1123](https://issues.apache.org/jira/browse/THRIFT-1123) - Patch to compile Thrift server and client for vc++ 9.0 and 10.0 +- [THRIFT-386](https://issues.apache.org/jira/browse/THRIFT-386) - Make it possible to build the Python library without the extension + +### New Feature +- [THRIFT-1401](https://issues.apache.org/jira/browse/THRIFT-1401) - JSON-protocol for Delphi XE Libraries +- [THRIFT-1167](https://issues.apache.org/jira/browse/THRIFT-1167) - Java nonblocking server with more than one thread for select and handling IO +- [THRIFT-1366](https://issues.apache.org/jira/browse/THRIFT-1366) - Delphi generator, lirbrary and unit test. +- [THRIFT-1354](https://issues.apache.org/jira/browse/THRIFT-1354) - Add rake task to build just the gem file +- [THRIFT-769](https://issues.apache.org/jira/browse/THRIFT-769) - Pluggable Serializers + +### Sub-task +- [THRIFT-1415](https://issues.apache.org/jira/browse/THRIFT-1415) - delphi: add version Info to the library +- [THRIFT-1391](https://issues.apache.org/jira/browse/THRIFT-1391) - Improved Delphi XE test cases + +## 0.7 + +### Bug +- [THRIFT-1140](https://issues.apache.org/jira/browse/THRIFT-1140) - Framed Transport Client using C (Glib) Library hangs when connecting to Ruby Server +- [THRIFT-1154](https://issues.apache.org/jira/browse/THRIFT-1154) - HttpClient does not specify the connection close parameter +- [THRIFT-1153](https://issues.apache.org/jira/browse/THRIFT-1153) - HttpClient does not specify the connection close parameter +- [THRIFT-1149](https://issues.apache.org/jira/browse/THRIFT-1149) - Nonblocking server fails when client connection is reset +- [THRIFT-1146](https://issues.apache.org/jira/browse/THRIFT-1146) - Android Incompatibility : in Android < 2.3 java.io.IOException doesn't support for Throwable parameter in constructor +- [THRIFT-1133](https://issues.apache.org/jira/browse/THRIFT-1133) - Java and JavaScript tutorial is broken since we have Java maven deployment +- [THRIFT-1132](https://issues.apache.org/jira/browse/THRIFT-1132) - Deserialization error in TApplicationException C# +- [THRIFT-1131](https://issues.apache.org/jira/browse/THRIFT-1131) - C# JSON Protocol is unable to decode escaped characters in string +- [THRIFT-1208](https://issues.apache.org/jira/browse/THRIFT-1208) - python TCompactProtocol.py writeBool and readBool not follow the compact-proto-spec-2.txt spec for CONTAINER_WRITE, CONTAINER_READ +- [THRIFT-1200](https://issues.apache.org/jira/browse/THRIFT-1200) - JS compiler generates code that clobbers existing namespaces +- [THRIFT-1183](https://issues.apache.org/jira/browse/THRIFT-1183) - Pure-ruby CompactProtocol raises ArgumentError when deserializing under Ruby 1.9 +- [THRIFT-1182](https://issues.apache.org/jira/browse/THRIFT-1182) - Native deserializer segfaults on incorrect list element type +- [THRIFT-1181](https://issues.apache.org/jira/browse/THRIFT-1181) - AS3 compiler generates incorrect code for setting default values in constructor +- [THRIFT-1234](https://issues.apache.org/jira/browse/THRIFT-1234) - thrift --help is missing doc on py:utf8strings +- [THRIFT-1180](https://issues.apache.org/jira/browse/THRIFT-1180) - AS3 compiler generates uncompilable code for binary types. +- [THRIFT-1194](https://issues.apache.org/jira/browse/THRIFT-1194) - Java lib does not install artifacts to local dir correctly +- [THRIFT-1193](https://issues.apache.org/jira/browse/THRIFT-1193) - Potential infinite loop in nonblocking_server +- [THRIFT-1192](https://issues.apache.org/jira/browse/THRIFT-1192) - Typo: TProtocol.h tests for HAVE_SYS_PARAM_H_ +- [THRIFT-1190](https://issues.apache.org/jira/browse/THRIFT-1190) - readBufferBytesAllocated in TNonblockingServer.java should be AtomicLong to fix FD leakage and general server malfunction +- [THRIFT-1187](https://issues.apache.org/jira/browse/THRIFT-1187) - nonblocking_server shutdown race under Ruby 1.9 +- [THRIFT-1178](https://issues.apache.org/jira/browse/THRIFT-1178) - Java: TBase signature should be T extends TBase +- [THRIFT-1164](https://issues.apache.org/jira/browse/THRIFT-1164) - Segmentation fault on NULL pointer in t_js_generator::generate_const +- [THRIFT-1171](https://issues.apache.org/jira/browse/THRIFT-1171) - Perl write/readDouble assumes little-endian platform +- [THRIFT-1222](https://issues.apache.org/jira/browse/THRIFT-1222) - Unhandled exception for TEvhttpServer request +- [THRIFT-1220](https://issues.apache.org/jira/browse/THRIFT-1220) - TProcessor::process never returns false +- [THRIFT-1285](https://issues.apache.org/jira/browse/THRIFT-1285) - Stable 0.7.0 Windows compiler exe available on the webside is not the good one +- [THRIFT-1218](https://issues.apache.org/jira/browse/THRIFT-1218) - c_glib uses wrong name in pkg-config +- [THRIFT-1215](https://issues.apache.org/jira/browse/THRIFT-1215) - Undefined property Thirft in lib/js/thrift.js +- [THRIFT-1211](https://issues.apache.org/jira/browse/THRIFT-1211) - When using THttpClient, non 200 responses leave the connection open +- [THRIFT-1228](https://issues.apache.org/jira/browse/THRIFT-1228) - The php accelerator module calls flush incorrectly +- [THRIFT-1308](https://issues.apache.org/jira/browse/THRIFT-1308) - libfb303-0.7.0.jar missing in maven repository +- [THRIFT-1255](https://issues.apache.org/jira/browse/THRIFT-1255) - Mismatch of method name between JavaME's lib and generated code (compareTo/compareObjects) +- [THRIFT-1253](https://issues.apache.org/jira/browse/THRIFT-1253) - Code generated for maps is not compiling +- [THRIFT-1252](https://issues.apache.org/jira/browse/THRIFT-1252) - Segfault in Ruby deserializer +- [THRIFT-1094](https://issues.apache.org/jira/browse/THRIFT-1094) - bug in TCompactProto python readMessageEnd method and updated test cases +- [THRIFT-1093](https://issues.apache.org/jira/browse/THRIFT-1093) - several bugs in python TCompactProtocol +- [THRIFT-1092](https://issues.apache.org/jira/browse/THRIFT-1092) - generated validate() method has wrong indentation +- [THRIFT-1011](https://issues.apache.org/jira/browse/THRIFT-1011) - Error generating package imports when using classes from other packages +- [THRIFT-1050](https://issues.apache.org/jira/browse/THRIFT-1050) - Declaring an argument named "manager" to a service method produces code that fails compile due to name conflicts with protected ivars in TAsyncClient +- [THRIFT-1074](https://issues.apache.org/jira/browse/THRIFT-1074) - .keystore and .truststore are missing from the 0.6.0 distribution +- [THRIFT-1067](https://issues.apache.org/jira/browse/THRIFT-1067) - Tons of bugs in php implementation +- [THRIFT-1065](https://issues.apache.org/jira/browse/THRIFT-1065) - Unexpected exceptions not proper handled on JS +- [THRIFT-1076](https://issues.apache.org/jira/browse/THRIFT-1076) - Erlang Thrift socket server has a bug that causes java thrift client of framed binary client to throw "out of sequence" exception +- [THRIFT-1057](https://issues.apache.org/jira/browse/THRIFT-1057) - casts in TBinaryProtocol.tcc causing "dereferencing type-punned pointer will break strict-aliasing rules" warnings from gcc +- [THRIFT-1055](https://issues.apache.org/jira/browse/THRIFT-1055) - csharp TServerSocket and TSocket do not disable Nagle via Socket.NoDelay = true like cpp and java do +- [THRIFT-1054](https://issues.apache.org/jira/browse/THRIFT-1054) - explicit call to PKG_PROG_PKG_CONFIG is missing and first use of PKG_CHECK_MODULES may not happen, causes mono detection to fail +- [THRIFT-1117](https://issues.apache.org/jira/browse/THRIFT-1117) - JavaScript Unit Test does not work anymore because libthrift*.jar where moved by Maven Deployment +- [THRIFT-1111](https://issues.apache.org/jira/browse/THRIFT-1111) - The HTML generator does not distinguish between string and binary types +- [THRIFT-1032](https://issues.apache.org/jira/browse/THRIFT-1032) - "make dist" fails due to c_glib problem +- [THRIFT-1036](https://issues.apache.org/jira/browse/THRIFT-1036) - Auto-generated C++ code fails to compile with "-Werror -Wextra -Wall" g++ compiler flags +- [THRIFT-1041](https://issues.apache.org/jira/browse/THRIFT-1041) - TDeserializer holds onto a reference of the array it reads after it is done deserializing +- [THRIFT-1106](https://issues.apache.org/jira/browse/THRIFT-1106) - C++ code TAsyncProtocolProcessor.h & TAsyncBufferProcessor.h dont have virtual functions but no virtual destructor. Causes warnings on -Wall +- [THRIFT-1105](https://issues.apache.org/jira/browse/THRIFT-1105) - OCaml generator does not prefix methods of included structs with their type +- [THRIFT-1104](https://issues.apache.org/jira/browse/THRIFT-1104) - INSTALLDIRS should be included in configure script +- [THRIFT-1102](https://issues.apache.org/jira/browse/THRIFT-1102) - typo in configure.ac: "==" operator in 'test' (instead of"'=") +- [THRIFT-1101](https://issues.apache.org/jira/browse/THRIFT-1101) - bytebuffer length calculation in TBinaryProtocol writeBinary +- [THRIFT-1098](https://issues.apache.org/jira/browse/THRIFT-1098) - Undefined properties in TBinaryProtocolFactory +- [THRIFT-1081](https://issues.apache.org/jira/browse/THRIFT-1081) - PHP tests broken and somewhat incomplete +- [THRIFT-1080](https://issues.apache.org/jira/browse/THRIFT-1080) - erlang test's 'make' fails on Mac OSX +- [THRIFT-1078](https://issues.apache.org/jira/browse/THRIFT-1078) - ThriftTest.thrift generates invalid PHP library +- [THRIFT-1120](https://issues.apache.org/jira/browse/THRIFT-1120) - proto.WriteListEnd being called in the wrong place +- [THRIFT-1119](https://issues.apache.org/jira/browse/THRIFT-1119) - TJSONProtocol fails to UTF8 decode strings +- [THRIFT-867](https://issues.apache.org/jira/browse/THRIFT-867) - PHP accelerator module's output transport is incompatible with TFramedTransport +- [THRIFT-826](https://issues.apache.org/jira/browse/THRIFT-826) - PHP TSocket Write Timeout +- [THRIFT-835](https://issues.apache.org/jira/browse/THRIFT-835) - Bad AS3 syntax in constructors that set default values +- [THRIFT-788](https://issues.apache.org/jira/browse/THRIFT-788) - thrift_protocol.so: multiget/multiget_slice does not handle more than 17 keys correctly +- [THRIFT-125](https://issues.apache.org/jira/browse/THRIFT-125) - OCaml libraries don't compile with 32-bit ocaml +- [THRIFT-342](https://issues.apache.org/jira/browse/THRIFT-342) - PHP: can't have sets of complex types +- [THRIFT-731](https://issues.apache.org/jira/browse/THRIFT-731) - configure doesn't check for ant >= 1.7 +- [THRIFT-690](https://issues.apache.org/jira/browse/THRIFT-690) - Update TApplicationException codes +- [THRIFT-638](https://issues.apache.org/jira/browse/THRIFT-638) - BufferedTransport + C extensions block until recv timeout is reached on last fread call + +### Dependency upgrade +- [THRIFT-1177](https://issues.apache.org/jira/browse/THRIFT-1177) - Update thrift to reflect changes in Go's networking libraries + +### Improvement +- [THRIFT-1155](https://issues.apache.org/jira/browse/THRIFT-1155) - Remove log4j dependency from java client +- [THRIFT-1151](https://issues.apache.org/jira/browse/THRIFT-1151) - Produce more informative runtime error in case of schema and data mismatch during serialization +- [THRIFT-1207](https://issues.apache.org/jira/browse/THRIFT-1207) - Support DESTDIR on "make install" of ruby libs +- [THRIFT-1199](https://issues.apache.org/jira/browse/THRIFT-1199) - Union structs should have generated methods to test whether a specific field is currently set +- [THRIFT-1233](https://issues.apache.org/jira/browse/THRIFT-1233) - Remove unused include in generated C++ code +- [THRIFT-1189](https://issues.apache.org/jira/browse/THRIFT-1189) - Ruby deserializer speed improvements +- [THRIFT-1170](https://issues.apache.org/jira/browse/THRIFT-1170) - Thrift Generated Code and Java 5 +- [THRIFT-1174](https://issues.apache.org/jira/browse/THRIFT-1174) - Publish as3 client implementation via Maven for use by flex-mojos users +- [THRIFT-1225](https://issues.apache.org/jira/browse/THRIFT-1225) - TCompactProtocol for PHP +- [THRIFT-1221](https://issues.apache.org/jira/browse/THRIFT-1221) - Remove SimpleCallback.h +- [THRIFT-1217](https://issues.apache.org/jira/browse/THRIFT-1217) - Use evutil_socketpair instead of pipe (Windows port) +- [THRIFT-1216](https://issues.apache.org/jira/browse/THRIFT-1216) - build Java Library behind a proxy +- [THRIFT-1231](https://issues.apache.org/jira/browse/THRIFT-1231) - Remove bogus include +- [THRIFT-1213](https://issues.apache.org/jira/browse/THRIFT-1213) - Membuffer should provide a way to get back the buffer +- [THRIFT-1237](https://issues.apache.org/jira/browse/THRIFT-1237) - Java fb303 missing some methods +- [THRIFT-1063](https://issues.apache.org/jira/browse/THRIFT-1063) - Fix Erlang Tutorial Files +- [THRIFT-1053](https://issues.apache.org/jira/browse/THRIFT-1053) - Make remote client's IP address available for all socket related transports +- [THRIFT-1109](https://issues.apache.org/jira/browse/THRIFT-1109) - Deploy fb303 along side libthrift to maven repo +- [THRIFT-1107](https://issues.apache.org/jira/browse/THRIFT-1107) - improvement for compiler-generated python for 'None' object comparisons +- [THRIFT-1069](https://issues.apache.org/jira/browse/THRIFT-1069) - Add command line option to prevent thrift from inserting gen-* directories +- [THRIFT-1049](https://issues.apache.org/jira/browse/THRIFT-1049) - Allow for TServerSocket python library to bind to a specific host +- [THRIFT-1126](https://issues.apache.org/jira/browse/THRIFT-1126) - Extending struct_info for erlang bindings +- [THRIFT-1100](https://issues.apache.org/jira/browse/THRIFT-1100) - python TSSLSocket improvements, including certificate validation +- [THRIFT-994](https://issues.apache.org/jira/browse/THRIFT-994) - Don't try to invoke phpize if we don't have it +- [THRIFT-993](https://issues.apache.org/jira/browse/THRIFT-993) - Some improvements in C++ stubs for oneway operations +- [THRIFT-997](https://issues.apache.org/jira/browse/THRIFT-997) - Using valueOf for base types in getFieldValue +- [THRIFT-418](https://issues.apache.org/jira/browse/THRIFT-418) - Don't do runtime sorting of struct fields +- [THRIFT-151](https://issues.apache.org/jira/browse/THRIFT-151) - TSSLServerSocket and TSSLSocket implementation +- [THRIFT-27](https://issues.apache.org/jira/browse/THRIFT-27) - Generated erlang types don't contain default values for records +- [THRIFT-113](https://issues.apache.org/jira/browse/THRIFT-113) - to-string methods should omit optional null fields from output +- [THRIFT-363](https://issues.apache.org/jira/browse/THRIFT-363) - Maven Deploy +- [THRIFT-447](https://issues.apache.org/jira/browse/THRIFT-447) - Make an abstract base Client class so we can generate less code +- [THRIFT-627](https://issues.apache.org/jira/browse/THRIFT-627) - should c++ have setters for optional fields? + +### New Feature +- [THRIFT-1236](https://issues.apache.org/jira/browse/THRIFT-1236) - Erlang Reconnecting Thrift Client +- [THRIFT-1021](https://issues.apache.org/jira/browse/THRIFT-1021) - Framed transport support for OCaml +- [THRIFT-1068](https://issues.apache.org/jira/browse/THRIFT-1068) - Python SSL Socket Support +- [THRIFT-1103](https://issues.apache.org/jira/browse/THRIFT-1103) - TZlibTransport for python, a zlib compressed transport +- [THRIFT-1083](https://issues.apache.org/jira/browse/THRIFT-1083) - Preforking python process pool server +- [THRIFT-999](https://issues.apache.org/jira/browse/THRIFT-999) - Add TForkingServer + +### Sub-task +- [THRIFT-1152](https://issues.apache.org/jira/browse/THRIFT-1152) - Attributes from private to protected +- [THRIFT-1038](https://issues.apache.org/jira/browse/THRIFT-1038) - Generated Java code for structures containing binary fields (or collections thereof) are not serializable (in the Java sense) even though they implement java.io.Serializable + +### Task +- [THRIFT-892](https://issues.apache.org/jira/browse/THRIFT-892) - Refactor erlang build system with rebar + +### Wish +- [THRIFT-625](https://issues.apache.org/jira/browse/THRIFT-625) - Add support for 'Go' + +## 0.6.1 + +### Bug +- [THRIFT-1133](https://issues.apache.org/jira/browse/THRIFT-1133) - Java and JavaScript tutorial is broken since we have Java maven deployment +- [THRIFT-1131](https://issues.apache.org/jira/browse/THRIFT-1131) - C# JSON Protocol is unable to decode escaped characters in string +- [THRIFT-1074](https://issues.apache.org/jira/browse/THRIFT-1074) - .keystore and .truststore are missing from the 0.6.0 distribution + +### Improvement +- [THRIFT-1109](https://issues.apache.org/jira/browse/THRIFT-1109) - Deploy fb303 along side libthrift to maven repo +- [THRIFT-363](https://issues.apache.org/jira/browse/THRIFT-363) - Maven Deploy + +### Question +- [THRIFT-1206](https://issues.apache.org/jira/browse/THRIFT-1206) - did the THRIFT 0.6.1 merge THRIFT-563 ? + +### Sub-task +- [THRIFT-1163](https://issues.apache.org/jira/browse/THRIFT-1163) - How can i use multi service in one program? + +### Task +- [THRIFT-1112](https://issues.apache.org/jira/browse/THRIFT-1112) - Apply THRIFT-363 to 0.6 branch +- [THRIFT-1113](https://issues.apache.org/jira/browse/THRIFT-1113) - Apply THRIFT-1074 to 0.6 branch + +## 0.6 + +### Bug +- [THRIFT-1020](https://issues.apache.org/jira/browse/THRIFT-1020) - OCaml compiler generates invalid OCaml +- [THRIFT-1015](https://issues.apache.org/jira/browse/THRIFT-1015) - TUnion does not handle ByteBuffer in toString +- [THRIFT-1013](https://issues.apache.org/jira/browse/THRIFT-1013) - generated java code may have name clashes with thrift library +- [THRIFT-1009](https://issues.apache.org/jira/browse/THRIFT-1009) - TUnion does not correctly deep copy a ByteBuffer +- [THRIFT-1032](https://issues.apache.org/jira/browse/THRIFT-1032) - "make dist" fails due to c_glib problem +- [THRIFT-868](https://issues.apache.org/jira/browse/THRIFT-868) - Referencing constant values doesn't work with with typedef types +- [THRIFT-971](https://issues.apache.org/jira/browse/THRIFT-971) - java module can't be compiled without ivy and network connection +- [THRIFT-970](https://issues.apache.org/jira/browse/THRIFT-970) - Under heavy load, THttpClient may fail with "too many open files" +- [THRIFT-969](https://issues.apache.org/jira/browse/THRIFT-969) - Java Tutorial broken, move CalculatorHandler to a separate file +- [THRIFT-807](https://issues.apache.org/jira/browse/THRIFT-807) - JavaScript: Initialization of Base Types with 0 instead of null +- [THRIFT-955](https://issues.apache.org/jira/browse/THRIFT-955) - Thrift compiler for Windows uses lowercase names and directories which is inconsistent with compiling on other platforms +- [THRIFT-992](https://issues.apache.org/jira/browse/THRIFT-992) - Naming convention in C# constructor is not consistent with other fields causes compile errors +- [THRIFT-1008](https://issues.apache.org/jira/browse/THRIFT-1008) - byte[] accessors throw NPE on unset field +- [THRIFT-1006](https://issues.apache.org/jira/browse/THRIFT-1006) - Impossible to correctly qualify an enum constant in an external thrift file +- [THRIFT-950](https://issues.apache.org/jira/browse/THRIFT-950) - Haskell bindings treat 'byte' as unsigned 8-bit int (Data.Word.Word8), java/cpp as signed (byte/int8_t). +- [THRIFT-975](https://issues.apache.org/jira/browse/THRIFT-975) - lib/c_glib/README is missing => breaks make dist +- [THRIFT-944](https://issues.apache.org/jira/browse/THRIFT-944) - Support all version-4s of base +- [THRIFT-939](https://issues.apache.org/jira/browse/THRIFT-939) - optional binary fields throw NPE on default byte[] getters +- [THRIFT-935](https://issues.apache.org/jira/browse/THRIFT-935) - PHP Extension aborts the build if php-config is not installed +- [THRIFT-933](https://issues.apache.org/jira/browse/THRIFT-933) - Haskell's Thrift.cabal has warnings +- [THRIFT-932](https://issues.apache.org/jira/browse/THRIFT-932) - Haskell tests need to be run through 'make check' (and probably 'cabal check') too +- [THRIFT-904](https://issues.apache.org/jira/browse/THRIFT-904) - C# TSocket should disable nagle and linger +- [THRIFT-941](https://issues.apache.org/jira/browse/THRIFT-941) - Make PHP C Extension use the defined Protocol writeMessageBegin function +- [THRIFT-940](https://issues.apache.org/jira/browse/THRIFT-940) - 'make check' fails if boost is not in the std include and link paths +- [THRIFT-924](https://issues.apache.org/jira/browse/THRIFT-924) - Fix generated php structure constants +- [THRIFT-979](https://issues.apache.org/jira/browse/THRIFT-979) - ruby bindings used to work on jruby +- [THRIFT-977](https://issues.apache.org/jira/browse/THRIFT-977) - Hex Conversion Bug in C++ TJSONProtocol +- [THRIFT-347](https://issues.apache.org/jira/browse/THRIFT-347) - PHP TSocket Timeout Issues +- [THRIFT-517](https://issues.apache.org/jira/browse/THRIFT-517) - TExceptions thrown by server result in cryptic error message on client - Tried to read 4 bytes, but only got 0 bytes + +### Improvement +- [THRIFT-1024](https://issues.apache.org/jira/browse/THRIFT-1024) - Add Python Twisted example to the Tutorial +- [THRIFT-958](https://issues.apache.org/jira/browse/THRIFT-958) - Change accessmodifer on trans_ field in the FrameBuffer class to public. +- [THRIFT-957](https://issues.apache.org/jira/browse/THRIFT-957) - THsHaServer: Change access modifier of the invoker field. +- [THRIFT-1002](https://issues.apache.org/jira/browse/THRIFT-1002) - CodeStyle: t_c_glib_generator.cc +- [THRIFT-1005](https://issues.apache.org/jira/browse/THRIFT-1005) - Give unions byte[] signature methods to go along with their ByteBuffer counterparts +- [THRIFT-951](https://issues.apache.org/jira/browse/THRIFT-951) - Add a new isServing() method to TServer +- [THRIFT-943](https://issues.apache.org/jira/browse/THRIFT-943) - Silly readme typo fix. +- [THRIFT-961](https://issues.apache.org/jira/browse/THRIFT-961) - JavaScript TestSuite using ant/ivy and Java's ServerTestBase Handler +- [THRIFT-960](https://issues.apache.org/jira/browse/THRIFT-960) - add TestServer, TestNonblockingServer and TestClient again +- [THRIFT-949](https://issues.apache.org/jira/browse/THRIFT-949) - Modify the TEnum interface so it defines a method similar to findByValue +- [THRIFT-946](https://issues.apache.org/jira/browse/THRIFT-946) - Augment FieldValueMetaData so it differentiates 'string' and 'binary' fields. +- [THRIFT-903](https://issues.apache.org/jira/browse/THRIFT-903) - custom ThreadFactory in THsHaServer +- [THRIFT-913](https://issues.apache.org/jira/browse/THRIFT-913) - Test Case for Url encoded strings + simple enhancement to lib/js/test/RunTestServer.sh +- [THRIFT-926](https://issues.apache.org/jira/browse/THRIFT-926) - Miscellaneous C++ improvements +- [THRIFT-929](https://issues.apache.org/jira/browse/THRIFT-929) - Improvements to the C++ test suite +- [THRIFT-893](https://issues.apache.org/jira/browse/THRIFT-893) - add JavaScript to the tutorial examples +- [THRIFT-1003](https://issues.apache.org/jira/browse/THRIFT-1003) - Polishing c_glib code +- [THRIFT-71](https://issues.apache.org/jira/browse/THRIFT-71) - Debian packaging for thrift + +### New Feature +- [THRIFT-1033](https://issues.apache.org/jira/browse/THRIFT-1033) - Node.js language target +- [THRIFT-947](https://issues.apache.org/jira/browse/THRIFT-947) - Provide a helper method to determine the TProtocol used to serialize some data. +- [THRIFT-928](https://issues.apache.org/jira/browse/THRIFT-928) - Make more statistics available in C++ servers +- [THRIFT-922](https://issues.apache.org/jira/browse/THRIFT-922) - Templatized [de]serialization code for C++ +- [THRIFT-923](https://issues.apache.org/jira/browse/THRIFT-923) - Event-driven client and server support for C++ +- [THRIFT-925](https://issues.apache.org/jira/browse/THRIFT-925) - Provide name<->value map for enums in C++ +- [THRIFT-927](https://issues.apache.org/jira/browse/THRIFT-927) - Add option to modify the PHP include path +- [THRIFT-377](https://issues.apache.org/jira/browse/THRIFT-377) - TFileTransport port in Java +- [THRIFT-106](https://issues.apache.org/jira/browse/THRIFT-106) - TSSLServerSocket +- [THRIFT-582](https://issues.apache.org/jira/browse/THRIFT-582) - C implementation of Thrift +- [THRIFT-745](https://issues.apache.org/jira/browse/THRIFT-745) - Make it easier to instantiate servers + +### Sub-task +- [THRIFT-1038](https://issues.apache.org/jira/browse/THRIFT-1038) - Generated Java code for structures containing binary fields (or collections thereof) are not serializable (in the Java sense) even though they implement java.io.Serializable + +### Task +- [THRIFT-862](https://issues.apache.org/jira/browse/THRIFT-862) - Async client issues / improvements + +### Test +- [THRIFT-581](https://issues.apache.org/jira/browse/THRIFT-581) - Add a testsuite for txThrift (Twisted) + +## Incubating Versions + +Thrift 0.5.0 - Incubating +-------------------------------------------------------------------------------- +THRIFT-505 Build Make configure give a summary of the enabled components (David Reiss) +THRIFT-506 Build Allow Thrift to be built without the C++ library (David Reiss) +THRIFT-844 Build Build Requirements state autoconf 2.59+ is required, but 2.60+ is needed (Harlan Lieberman-Berg) +THRIFT-850 Build Perl runtime requires Bit::Vector which may not be installed by default, but configure does not fail (Michael Lum) +THRIFT-854 Build Provide configure option and make rules to build/install php extension (Anthony Molinaro) +THRIFT-858 Build Have bootstrap.sh check for a suitable autoconf version before running (David Reiss) +THRIFT-871 Build Thrift compiler for WIndows (binary distribution) (David Reiss) +THRIFT-323 C# TJSONProtocol (Roger Meier) +THRIFT-634 C# C# Compiler Generates Incorrect Code For Fields which begin with an uppercase letter (Jon S Akhtar) +THRIFT-881 C# add csharp to the tutorial (Roger Meier) +THRIFT-856 C++ Building cpp library fails on OS X with malloc and free not being declared in scope (James Clarke) +THRIFT-865 C++ C++ compiler build depends on libfl even when flex/lex not detected (David Reiss) +THRIFT-900 C++ Unix domain socket (Roger Meier) +THRIFT-920 C++ C++ Test and Tutorial does not compile anymore due to the change within Enum handling (Roger Meier) +THRIFT-567 C++ Can't immediately stop a TSimpleServer thread that is idle (Rush Manbert) +THRIFT-756 C++ Exposing TSocket(int) constructor to public (Rajat Goel) +THRIFT-798 C++ TNonblockingServer leaks resources when destroyed (David Reiss) +THRIFT-812 C++, Python Demo of Thrift over ZeroMQ (David Reiss) +THRIFT-629 Cocoa Unused Field In TSocketServer Appears To Break iPhone Build (Jon S Akhtar) +THRIFT-838 Cocoa Generated Cocoa classes have useless @dynamic declarations (Kevin Ballard) +THRIFT-805 Cocoa Don't generate process_XXXX methods for oneway methods (Brad Taylor) +THRIFT-507 Compiler Remove the compiler's dependency on Boost (David Reiss) +THRIFT-895 Compiler (General) Thrift compiler does not allow two different enumerations to have the same key name for one of the enum values (David Reiss) +THRIFT-852 Compiler (General) Missing newline causes many compiler warnings (Anthony Molinaro) +THRIFT-877 Compiler (General) smalltalk namespace doesn't work (Bruce Lowekamp) +THRIFT-897 Compiler (General) Don't allow unqualified constant access to enum values (Bryan Duxbury) +THRIFT-9 Compiler (General) Add a default namespace declaration for all languages (David Reiss) +THRIFT-599 Erlang Don't use unnecessary processes in the Erlang transports and clients (David Reiss) +THRIFT-646 Erlang Erlang library is missing install target (David Reiss) +THRIFT-698 Erlang Generated module list should contain atoms, not strings (Anthony Molinaro) +THRIFT-866 Erlang term() in spec definitions seems to not work in erlang R12 (Anthony Molinaro) +THRIFT-886 Erlang Dialyzer warning (Anthony Molinaro) +THRIFT-785 Erlang Framed transport server problems (Anthony Molinaro) +THRIFT-884 HTML HTML Generator: add Key attribute to the Data Types Tables (Roger Meier) +THRIFT-652 Haskell Generated field name for strut is not capitalized correctly (Christian Lavoie) +THRIFT-743 Haskell compile error with GHC 6.12.1 (Christian Lavoie) +THRIFT-901 Haskell Allow the bindings to compile without -fglasgow-exts and with -Wall -Werror (Christian Lavoie) +THRIFT-905 Haskell Make haskell thrift bindings use automake to compile and install (Christian Lavoie) +THRIFT-906 Haskell Improve type mappings (Christian Lavoie) +THRIFT-914 Haskell Make haskell bindings 'easily' compilable (Christian Lavoie) +THRIFT-918 Haskell Make haskell tests run again (Christian Lavoie) +THRIFT-919 Haskell Update Haskell bindings README (Christian Lavoie) +THRIFT-787 Haskell Enums are not read correctly (Christian Lavoie) +THRIFT-250 Java ExecutorService as a constructor parameter for TServer (Ed Ceaser) +THRIFT-693 Java Thrift compiler generated java code that throws compiler warnings about deprecated methods. (Bryan Duxbury) +THRIFT-843 Java TNonblockingSocket connects without a timeout (Bryan Duxbury) +THRIFT-845 Java async client does not respect timeout (Ning Liang) +THRIFT-870 Java Java constants don't get Javadoc comments (Bryan Duxbury) +THRIFT-873 Java Java tests fail due to Too many open files (Todd Lipcon) +THRIFT-876 Java Add SASL support (Aaron T. Myers) +THRIFT-879 Java Remove @Override from TUnion.clear (Dave Engberg) +THRIFT-882 Java deep copy of binary fields does not copy ByteBuffer characteristics (arrayOffset, position) (Bryan Duxbury) +THRIFT-888 Java async client should also have nonblocking connect (Eric Jensen) +THRIFT-890 Java Java tutorial doesn't work (Todd Lipcon) +THRIFT-894 Java Make default accessors for binary fields return byte[]; provide new accessors to get ByteBuffer version (Bryan Duxbury) +THRIFT-896 Java TNonblockingSocket.isOpen() returns true even after close() (Eric Jensen) +THRIFT-907 Java libfb303 doesn't compile in 0.4.0 (Todd Lipcon) +THRIFT-912 Java Improvements and bug fixes to SASL implementation (Todd Lipcon) +THRIFT-917 Java THsHaServer should not accept an ExecutorService without catching RejectedExecutionException (Ed Ceaser) +THRIFT-931 Java Use log4j for Java tests (Todd Lipcon) +THRIFT-880 JavaME JavaME code generator and runtime library (Dave Engberg) +THRIFT-846 JavaScript JavaScript Test Framwork: extended Testcases (Roger Meier) +THRIFT-885 JavaScript Url encoded strings never get decoded? How do we fix this? (T Jake Luciani) +THRIFT-911 JavaScript (JavaScript compiler) Const structs, maps, sets, and lists generate a trailing comma (T Jake Luciani) +THRIFT-860 OCaml copy method and reset method (Lev Walkin) +THRIFT-682 PHP PHP extension doesn't compile on Mac OS X (Bryan Duxbury) +THRIFT-851 PHP php extension fails to compile on centos 5.x (Todd Lipcon) +THRIFT-840 Perl Perl protocol handler could be more robust against unrecognised types (Conrad Hughes) +THRIFT-758 Perl incorrect deference in exception handling (Yann Kerherve) +THRIFT-257 Python Support validation of required fields (Esteve Fernandez) +THRIFT-335 Python Compact Protocol for Python (David Reiss) +THRIFT-596 Python Make Python's TBufferedTransport use a configurable input buffer (David Reiss) +THRIFT-597 Python Python THttpServer performance improvements (David Reiss) +THRIFT-598 Python Allow Python's threading servers to use daemon threads (David Reiss) +THRIFT-666 Python Allow the handler to override HTTP responses in THttpServer (David Reiss) +THRIFT-673 Python Generated Python code has whitespace issues (Ian Eure) +THRIFT-721 Python THttpClient ignores url parameters (Thomas Kho) +THRIFT-824 Python TApplicationException.__str__() refers to class constants as globals (Peter Schuller) +THRIFT-855 Python Include optimized compiled python objects in install (Anthony Molinaro) +THRIFT-859 Python Allow py:twisted to be generated in different namespace than py (Bruce Lowekamp) +THRIFT-869 Python TSocket.py on Mac (and FreeBSD) doesn't handle ECONNRESET from recv() (Steven Knight) +THRIFT-875 Python Include python setup.cfg in dist (Anthony Molinaro) +THRIFT-610 Ruby binary_protocol.rb segfaults [line 86] (Unassigned) +THRIFT-899 Ruby Ruby read timeouts can sometimes be 2x what they should be (Ryan King) +THRIFT-909 Ruby allow block argument to struct constructor (Michael Stockton) +THRIFT-456 Test Suite Bad IP address string in test/cpp/src/main.cpp (Rush Manbert) + + +Thrift 0.4.0 - Incubating +-------------------------------------------------------------------------------- +THRIFT-650 Build Make Check fails on Centos/OSX with 0.2.0 tarball (Anthony Molinaro) +THRIFT-770 Build Get 'make dist' to work without first compiling source code (Anthony Molinaro) +THRIFT-160 C# Created THttpTransport for the C# library based on WebHttpRequest (Michael Greene) +THRIFT-834 C# THttpClient resends contents of message after transport errors (Anatoly Fayngelerin) +THRIFT-247 C++ THttpServer Transport (Unassigned) +THRIFT-676 C++ Change C++ code generator so that generated classes can be wrapped with SWIG (Unassigned) +THRIFT-570 Compiler Thrift compiler does not error when duplicate method names are present (Bruce Simpson) +THRIFT-808 Compiler Segfault when constant declaration references a struct field that doesn't exist (Bryan Duxbury) +THRIFT-646 Erlang Erlang library is missing install target (Anthony Molinaro) +THRIFT-544 General multiple enums with the same key generate invalid code (Ben Taitelbaum) +THRIFT-434 General ruby compiler should warn when a reserved word is used (Michael Stockton) +THRIFT-799 General Files missing proper Apache license header (Bryan Duxbury) +THRIFT-832 HTML HTML generator shows unspecified struct fields as 'required' (Bryan Duxbury) +THRIFT-226 Java Collections with binary keys or values break equals() (Bryan Duxbury) +THRIFT-484 Java Ability to use a slice of a buffer instead of a direct byte[] for binary fields (Bryan Duxbury) +THRIFT-714 Java maxWorkerThreads parameter to THsHaServer has no effect (Bryan Duxbury) +THRIFT-751 Java Add clear() method to TBase (Bryan Duxbury) +THRIFT-765 Java Improved string encoding and decoding performance (Bryan Duxbury) +THRIFT-768 Java Async client for Java (Bryan Duxbury) +THRIFT-774 Java TDeserializer should provide a partialDeserialize method for primitive types (Piotr Kozikowski) +THRIFT-783 Java .equals java method is broken on structs containing binary-type fields (Unassigned) +THRIFT-804 Java CompareTo is broken for unions set to map, set, or list (Bryan Duxbury) +THRIFT-814 Java Include a TServlet in the standard Thrift distribution (Mathias Herberts) +THRIFT-818 Java Async client doesn't send method args (Bryan Duxbury) +THRIFT-830 Java Switch binary field implementation from byte[] to ByteBuffer (Bryan Duxbury) +THRIFT-831 Java FramedTransport implementation that reuses its buffers (Bryan Duxbury) +THRIFT-833 Java build.xml in lib/java is missing a classpathref attribute for the javadoc task (Bryan Duxbury) +THRIFT-836 Java Race condition causes CancelledKeyException in TAsyncClientManager (Bryan Duxbury) +THRIFT-842 Java Upgrade to current version of commons-lang (2.5 instead of 2.4) and/or change dependency in ivy.xml to not be exact (Bryan Duxbury) +THRIFT-815 JavaScript Deserialization of lists is critically broken. (T Jake Luciani) +THRIFT-827 OCaml OCaml generator to take default values into account (Lev Walkin) +THRIFT-647 PHP PHP library is missing install target (Anthony Molinaro) +THRIFT-682 PHP PHP extension doesn't compile on Mac OS X (Bryan Duxbury) +THRIFT-718 PHP Thrift PHP library includes closing tags and extraneous whitespace (Nicholas Telford) +THRIFT-778 PHP PHP socket listening server (Nick Jones) +THRIFT-780 PHP PHP extension sometimes causes an abort with two exceptions at the same time (David Reiss) +THRIFT-837 PHP PHP accelerator bug for writes > 8k (Thomas Kho) +THRIFT-782 Perl Perl code for writing containers doesn't count length of write*Begin or write*End (Conrad Hughes) +THRIFT-395 Python Python library + compiler does not support unicode strings (Unassigned) +THRIFT-133 Ruby 'namespace ruby' should error out, or be an alias to 'namespace rb' (Bryan Duxbury) +THRIFT-664 Ruby Ruby extension fails to build with Ruby 1.9.1 (Rajesh Malepati) +THRIFT-699 Ruby Excise unused "native protocol method table" stuff from thrift_native (Bryan Duxbury) +THRIFT-767 Ruby ruby compiler does not keep comments for enum values (Bryan Duxbury) +THRIFT-811 Ruby http_client_transport.rb: allow custom http headers (Tony Kamenick) +THRIFT-459 Ruby Ruby installation always tries to write to /Library/Ruby/site (Matthieu Imbert) + + +Thrift 0.1.0 - Incubating (not released) +-------------------------------------------------------------------------------- +Compatibility Breaking Changes: + C++: +- It's quite possible that regenerating code and rebuilding will be + required. Make sure your headers match your libs! + + Java: + + Python: + + Ruby: +- Generated files now have underscored names [THRIFT-421] +- The library has been rearranged to be more Ruby-like [THRIFT-276] + + Erlang: +- Generated code will have to be regenerated, and the new code will + have to be deployed atomically with the new library code [THRIFT-136] + +New Features and Bug Fixes: + C++: +- Support for TCompactProtocol [THRIFT-333] + + Java: +- Support for TCompactProtocol [THRIFT-110] + + Python: +- Support for Twisted [THRIFT-148] + + Ruby: +- Support for TCompactProtocol [THRIFT-332] + diff --git a/CMakeLists.txt b/CMakeLists.txt index 9f57a66c4d5..127ceaf2660 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -17,33 +17,25 @@ # under the License. # -cmake_minimum_required(VERSION 3.1) +cmake_minimum_required(VERSION 3.4) -# CMake 3.1 supports C++ standards selection with CMAKE_CXX_STANDARD -# If you need CMake 3.1+ for Ubuntu 14.04, try -# https://launchpad.net/~george-edison55/+archive/ubuntu/cmake-3.x -# If you need CMake 3.1+ for debian "jessie", get it from jessie-backports -# Otherwise -# http://cmake.org +if(POLICY CMP0048) + cmake_policy(SET CMP0048 NEW) # package version behavior added in cmake 3.0 +endif() +if(POLICY CMP0074) + cmake_policy(SET CMP0074 NEW) # find_package behavior added in cmake 3.12 +endif() -project("Apache Thrift") +# PACKAGE_VERSION is used by cpack scripts currently +# Both thrift_VERSION and PACKAGE_VERSION should be the same for now +set(thrift_VERSION "0.14.0") +set(PACKAGE_VERSION ${thrift_VERSION}) -set(CMAKE_MODULE_PATH "${CMAKE_MODULE_PATH}" "${CMAKE_CURRENT_SOURCE_DIR}/build/cmake") +project("thrift" VERSION ${PACKAGE_VERSION}) +message(STATUS "Configuring ${CMAKE_PROJECT_NAME} ${thrift_VERSION}") -# TODO: add `git rev-parse --short HEAD` -# Read the version information from the Autoconf file -file (STRINGS "${CMAKE_CURRENT_SOURCE_DIR}/configure.ac" CONFIGURE_AC REGEX "AC_INIT\\(.*\\)" ) -# The following variable is used in the version.h.in file -string(REGEX REPLACE "AC_INIT\\(\\[.*\\], \\[([0-9]+\\.[0-9]+\\.[0-9]+(-dev)?)\\]\\)" "\\1" PACKAGE_VERSION ${CONFIGURE_AC}) -message(STATUS "Parsed Thrift package version: ${PACKAGE_VERSION}") - -# These are internal to CMake -string(REGEX REPLACE "([0-9]+\\.[0-9]+\\.[0-9]+)(-dev)?" "\\1" thrift_VERSION ${PACKAGE_VERSION}) -string(REGEX REPLACE "([0-9]+)\\.[0-9]+\\.[0-9]+" "\\1" thrift_VERSION_MAJOR ${thrift_VERSION}) -string(REGEX REPLACE "[0-9]+\\.([0-9])+\\.[0-9]+" "\\1" thrift_VERSION_MINOR ${thrift_VERSION}) -string(REGEX REPLACE "[0-9]+\\.[0-9]+\\.([0-9]+)" "\\1" thrift_VERSION_PATCH ${thrift_VERSION}) -message(STATUS "Parsed Thrift version: ${thrift_VERSION} (${thrift_VERSION_MAJOR}.${thrift_VERSION_MINOR}.${thrift_VERSION_PATCH})") +set(CMAKE_MODULE_PATH "${CMAKE_MODULE_PATH}" "${CMAKE_CURRENT_SOURCE_DIR}/build/cmake") # Some default settings include(DefineCMakeDefaults) @@ -55,16 +47,23 @@ include(DefineInstallationPaths) # Based on the options set some platform specifics include(DefinePlatformSpecifc) +# Add CMake targets for static code analysis +include(StaticCodeAnalysis) + # Generate the config.h file include(ConfigureChecks) -# Package it -include(CPackConfig) +# Generate the ThriftConfig.cmake module +include(GenerateConfigModule) +# Packaging +include(CPackConfig) +# Dependencies +include(BoostMacros) find_package(Threads) - include(CTest) + if(BUILD_TESTING) message(STATUS "Building with unittests") @@ -99,14 +98,29 @@ if(BUILD_CPP) endif() endif() +if(BUILD_AS3) + add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/lib/as3) +endif() + if(BUILD_C_GLIB) add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/lib/c_glib) + if(BUILD_TESTING) + add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/test/c_glib) + endif() endif() if(BUILD_JAVA) add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/lib/java) endif() +if(BUILD_JAVASCRIPT) + add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/lib/js) +endif() + +if(BUILD_NODEJS) + add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/lib/nodejs) +endif() + if(BUILD_PYTHON) add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/lib/py) if(BUILD_TESTING) @@ -121,4 +135,7 @@ if(BUILD_HASKELL) endif() endif() +# Create the uninstall target +add_custom_target(uninstall "${CMAKE_COMMAND}" -P "${PROJECT_SOURCE_DIR}/build/cmake/uninstall.cmake") + PRINT_CONFIG_SUMMARY() diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 7a1d710aeb9..7a199f73c2e 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -13,17 +13,17 @@ it activates the continuous integration (CI) build systems at Appveyor and Travi on a variety of Linux and Windows configurations and run all the test suites. Follow these requirements for a successful pull request: - 1. All code changes require an [Apache Jira THRIFT Issue](http://issues.apache.org/jira/browse/THRIFT) ticket. + 1. All significant changes require an [Apache Jira THRIFT Issue](http://issues.apache.org/jira/browse/THRIFT) ticket. Trivial changes such as fixing a typo or a compiler warning do not. 1. All pull requests should contain a single commit per issue, or we will ask you to squash it. - 1. The pull request title must begin with the Jira THRIFT ticket identifier, for example: + 1. The pull request title must begin with the Jira THRIFT ticket identifier if it has an associated ticket, for example: THRIFT-9999: an example pull request title 1. Commit messages must follow this pattern for code changes (deviations will not be merged): THRIFT-9999: [summary of fix, one line if possible] - Client: [language(s) affected, comma separated, use lib/ directory names please] + Client: [language(s) affected, comma separated, for example: "cpp,erl,perl"] Instructions: @@ -64,11 +64,11 @@ For unix systems, see our detailed instructions on the [Docker README](/build/do ## Contributing via Patch ## -Some changes do not require a build, for example in documentation. For changes that are not code or build related, you can submit a patch on Jira for review. To create a patch from changes in your local directory: +To create a patch from changes in your local directory: git diff > ../THRIFT-NNNN.patch -then wait for contributors or committers to review your changes, and then for a committer to apply your patch. +then wait for contributors or committers to review your changes, and then for a committer to apply your patch. This is not the preferred way to submit changes and incurs additional overhead for committers who must then create a pull request for you. ## GitHub recipes for Pull Requests ## diff --git a/LANGUAGES.md b/LANGUAGES.md index acf908343fb..08c431d38d9 100644 --- a/LANGUAGES.md +++ b/LANGUAGES.md @@ -1,14 +1,38 @@ # Apache Thrift Language Support # -Last Modified: 2018-12-17 +Guidance For: 0.13.0 | +[0.12.0](https://github.com/apache/thrift/blob/v0.12.0/LANGUAGES.md) | +[0.11.0](https://github.com/apache/thrift/blob/0.11.0/LANGUAGES.md) -Guidance For: 0.12.0 or later +Thrift supports many programming languages and has an impressive test suite that +exercises most of the languages, protocols, and transports. Each build exercises +a matrix of thousands of possible combinations. Each language typically has a +minimum required version as well as support libraries - some mandatory and some +optional. The information provided below will help you assess whether you can +use Apache Thrift with your project. Obviously this is a complex matrix to +maintain and may not be correct in all cases - if you spot an error please inform +the developers using the mailing list, or better yet, +[Edit on GitHub](https://github.com/apache/thrift/edit/master/LANGUAGES.md). -Thrift supports many programming languages and has an impressive test suite that exercises most of the languages, protocols, and transports that represents a matrix of thousands of possible combinations. Each language typically has a minimum required version as well as support libraries - some mandatory and some optional. All of this information is provided below to help you assess whether you can use Apache Thrift with your project. Obviously this is a complex matrix to maintain and may not be correct in all cases - if you spot an error please inform the developers using the mailing list. +Apache Thrift currently uses two build systems. The `autoconf` build system is +the most complete and builds all supported languages, however it does not support +Windows.. The `cmake` build system works on Linux and Windows, and has been +designated by the project to replace `autoconf` however this transition will +take quite some time to complete. During that transition, the cmake build will +not support all languages. -Apache Thrift has a choice of two build systems. The `autoconf` build system is the most complete build and is used to build all supported languages. The `cmake` build system has been designated by the project to replace `autoconf` however this transition will take quite some time to complete. +The Language/Library Levels indicate the minimum and maximum versions that are +used in the [continuous integration environments](build/docker/README.md) +(Appveyor, Travis) for Apache Thrift. Other language levels may be supported +for each language, however tested less thoroughly; check the README file inside +each lib directory for additional details. Note: while a language may contain +support for protocols, transports, and servers, the extent to which each is tested +as part of the overall build process varies. The definitive integration test for +the project is called the "cross" test which executes a test matrix with clients +and servers communicating across languages. -The Language/Library Levels indicate the minimum and maximum versions that are used in the [continuous integration environments](build/docker/README.md) (Appveyor, Travis) for Apache Thrift. Other language levels may be supported for each language, however tested less thoroughly; check the README file inside each lib directory for additional details. Note that while a language may contain support for protocols, transports, and servers, the extent to which each is tested as part of the overall build process varies. The definitive integration test for the project is called the "cross" test which executes a test matrix with clients and servers communicating across languages. +Thrift's core transport (supported by all languages) is TSocket. +Thrift's core protocol is TBinary, supported by all languages except for JavaScript. @@ -18,7 +42,7 @@ The Language/Library Levels indicate the minimum and maximum versions that are u - + @@ -27,308 +51,297 @@ The Language/Library Levels indicate the minimum and maximum versions that are u - + - + - - + + - + - + - + - - - + + + - + - + - + - + - - - - - - - - - - - - - - - - - - - - - - - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - - + + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - - - + + + - + - + - + - + - + - + - + - + - + - + - + - + + + + + + + + + + + + @@ -338,7 +351,7 @@ The Language/Library Levels indicate the minimum and maximum versions that are u - + @@ -347,9 +360,10 @@ The Language/Library Levels indicate the minimum and maximum versions that are u - + -
Build Systems Lang/Lib Levels (Tested) Low-Level TransportsTransport WrappersTransport Wrappers Protocols Servers Open Issuesautoconfcmake MinMax Domain File Memory Pipe Socket TLS Framed http  zlib FramedHeader http  zlib  BinaryCompact JSON Multiplex ForkingNonblockingSimpleThreadedThreadPool
ActionScriptActionScript 0.3.0ActionScript 3YesYesFLEX SDK 4.6 YesYesYes Yes ActionScriptActionScript
C (glib)C (glib) 0.6.0 YesYes2.48.22.54.0YesYesYesYesYes2.48.22.56.4YesYesYesYesYesYes YesYesYes YesC (glib)C (glib)
C++C++ 0.2.0 YesYesC++98C++11 YesYesYesYesYesYesYesYesYesYesYesYesYes YesYesYesYes YesYesYesYesC++
C#0.2.0Yes.NET 3.5 / mono 3.2.8.0.NET 4.6.1 / mono 4.6.2.7YesYesYesYesYesYesYesYesYesYesYesYesYesC# (.NET)
Cocoa0.2.0unknownYesYesYesYesYesYesYesYesYesYesCocoaC++
Common LispCommon LISP 0.12.0 YesSBCL 1.4.5SBCL 1.4.9SBCL 1.4.xSBCL 1.5.3 YesYesYesYes YesYesYes YesCommon LispCommon LISP
DlangDlang 0.9.0 Yes2.075.12.081.02.075.12.087.0 YesYesYesYesYesYesYesYesYesYesYes YesYesYes YesYesYesYesDD
DartDart 0.10.0 Yes1.22.11.24.32.0.02.4.0 YesYesYesYesYes YesYesYesYes DartDart
DelphiDelphi 0.8.0 2010unknown YesYesYesYesYes YesYesYesYes YesDelphiDelphi
.NET Core0.11.0.NET Standard0.13.0 Yes2.1.4.NET 4.5+, .NET Standard 2.x YesYesYesYesYesYesYesYes YesYesYesYes Yes.NET Core.NET Standard
ErlangErlang 0.3.0 Yes18.320.0.418.322.0 YesYesYesYesYesYesYesYes YesYesYesYes YesErlangErlang
GoGo 0.7.0 Yes1.7.61.10.31.14.141.15.7 YesYesYesYesYesYesYesYesYes YesYesYesYes YesGoGo
HaskellHaskell 0.5.0 YesYes 7.10.38.0.2 YesYesYesYesYesYesYesYes YesYesYes YesYesHaskellHaskell
HaxeHaxe 0.9.3 Yes 3.2.13.4.4 YesYesYesYesYesYes YesYesYesYes YesHaxeHaxe
Java (SE)Java (SE) 0.2.0 YesYes1.8.0_1511.8.0_15111.0.3 YesYesYesYesYesYesYesYesYesYes YesYesYesYes YesYesYesYesJava SEJava SE
Java (ME)Java (ME) 0.5.0 unknown YesYesYesYesYes YesYes Java MEJava ME
JavascriptJavascript 0.3.0 YesunknownES5ES6 YesYesYesYes YesYes JavascriptJavascript
LuaLua 0.9.2 Yes 5.1.55.2.4 YesYesYesYesYesYes YesYesYes YesLuaLua
node.jsnode.js 0.6.0 Yes6.x8.11.310.x10.x YesYesYesYesYesYesYesYes YesYesYesYes Yesnode.jsnode.js
node.tsnode.ts 0.12.0 Yes 3.1.6 YesYes Yes Yesnode.tsnode.ts
OCamlOCaml 0.2.0 4.04.0 YesYesYes Yes YesYesOCamlOCaml
PerlPerl 0.2.0 Yes5.22.15.26.05.22.15.26.1 YesYesYesYesYesYesYesYes YesYesYes YesYesPerlPerl
PHPPHP 0.2.0 Yes7.0.227.1.87.0.227.2.19 YesYesYesYesYesYesYes YesYesYesYes YesYesPHPPHP
PythonPython 0.2.0 YesYes2.7.12, 3.5.22.7.14, 3.6.3YesYesYesYes2.7.12, 3.5.22.7.15, 3.6.8YesYesYesYesYesYes YesYesYesYes YesYesYesPythonPython
RubyRuby 0.2.0 Yes2.3.1p1122.3.3p2222.3.1p1122.5.1p57 YesYesYesYesYesYesYesYes YesYesYesYes YesYesYesYesRubyRuby
RustRust 0.11.0 Yes1.17.01.21.01.40.01.xx.x YesYesYesYes YesYesYes YesRustRust
SmalltalkSmalltalk 0.2.0 unknown Yes Yes SmalltalkSmalltalk
Swift0.12.0Yes4.2.1YesYesYesYesYesYesYesYesYesYesSwift
autoconfcmake MinMax Domain File Memory Pipe Socket TLS Framed http  zlib FramedHeader http  zlib  BinaryCompact JSON Multiplex ForkingNonblockingSimpleThreadedThreadPool Open IssuesBuild Systems Lang/Lib Levels (Tested) Low-Level TransportsTransport WrappersTransport Wrappers Protocols Servers
+ diff --git a/LICENSE b/LICENSE index 3b6d7d74cc9..2bc6fbbf65c 100644 --- a/LICENSE +++ b/LICENSE @@ -236,4 +236,71 @@ For the lib/nodejs/lib/thrift/json_parse.js: */ (By Douglas Crockford ) + +-------------------------------------------------- +For lib/cpp/src/thrift/windows/SocketPair.cpp + +/* socketpair.c + * Copyright 2007 by Nathan C. Myers ; 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. + */ + + +-------------------------------------------------- +For lib/py/compat/win32/stdint.h + +// ISO C9x compliant stdint.h for Microsoft Visual Studio +// Based on ISO/IEC 9899:TC2 Committee draft (May 6, 2005) WG14/N1124 +// +// Copyright (c) 2006-2008 Alexander Chemeris +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// +// 3. The name of the author may be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED +// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO +// EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; +// OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR +// OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF +// ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +/////////////////////////////////////////////////////////////////////////////// + + -------------------------------------------------- +Codegen template in t_html_generator.h + +* Bootstrap v2.0.3 +* +* Copyright 2012 Twitter, Inc +* Licensed under the Apache License v2.0 +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Designed and built with all the love in the world @twitter by @mdo and @fat. + +--------------------------------------------------- +For t_cl_generator.cc + + * Copyright (c) 2008- Patrick Collison + * Copyright (c) 2006- Facebook + +--------------------------------------------------- diff --git a/Makefile.am b/Makefile.am index 511452dfc85..c5d9c41dba1 100755 --- a/Makefile.am +++ b/Makefile.am @@ -19,15 +19,7 @@ ACLOCAL_AMFLAGS = -I ./aclocal -if WITH_PLUGIN -# To enable bootstrap, build order is lib/cpp -> compiler -> others -SUBDIRS = lib/cpp compiler/cpp lib -if WITH_TESTS -SUBDIRS += lib/cpp/test -endif -else SUBDIRS = compiler/cpp lib -endif if WITH_TESTS SUBDIRS += test @@ -37,8 +29,40 @@ if WITH_TUTORIAL SUBDIRS += tutorial endif +clean-local: + $(RM) -r vendor/ + +distclean-local: + $(RM) -r .dub/ + $(RM) -r autom4te.cache/ + +CLEANFILES = \ + composer.lock \ + dub.selections.json + +DISTCLEANFILES = \ + Makefile \ + Makefile.in \ + aclocal.m4 \ + apache-thrift-test-library \ + autoscan.log \ + compile \ + config.guess \ + config.hin \ + config.hin~ \ + config.log \ + config.status \ + config.sub \ + configure \ + configure.scan \ + debcomp \ + install-sh \ + ltmain.sh \ + missing \ + ylwrap + dist-hook: - find $(distdir) -type f \( -iname ".DS_Store" -or -iname "._*" -or -iname ".gitignore" \) | xargs rm -rf + find $(distdir) -type f \( -iname ".DS_Store" -or -iname "._*" -or -iname ".gitignore" \) | xargs rm -f find $(distdir) -type d \( -iname ".deps" -or -iname ".libs" \) | xargs rm -rf find $(distdir) -type d \( -iname ".svn" -or -iname ".git" \) | xargs rm -rf @@ -54,7 +78,7 @@ empty := space := $(empty) $(empty) comma := , -CROSS_LANGS = @MAYBE_CPP@ @MAYBE_C_GLIB@ @MAYBE_CL@ @MAYBE_D@ @MAYBE_JAVA@ @MAYBE_CSHARP@ @MAYBE_PYTHON@ @MAYBE_PY3@ @MAYBE_RUBY@ @MAYBE_HASKELL@ @MAYBE_PERL@ @MAYBE_PHP@ @MAYBE_GO@ @MAYBE_NODEJS@ @MAYBE_DART@ @MAYBE_ERLANG@ @MAYBE_LUA@ @MAYBE_RS@ @MAYBE_DOTNETCORE@ @MAYBE_NODETS@ +CROSS_LANGS = @MAYBE_CPP@ @MAYBE_C_GLIB@ @MAYBE_CL@ @MAYBE_D@ @MAYBE_JAVA@ @MAYBE_PYTHON@ @MAYBE_PY3@ @MAYBE_RUBY@ @MAYBE_HASKELL@ @MAYBE_PERL@ @MAYBE_PHP@ @MAYBE_GO@ @MAYBE_NODEJS@ @MAYBE_DART@ @MAYBE_ERLANG@ @MAYBE_LUA@ @MAYBE_RS@ @MAYBE_NETSTD@ @MAYBE_NODETS@ CROSS_LANGS_COMMA_SEPARATED = $(subst $(space),$(comma),$(CROSS_LANGS)) if WITH_PY3 @@ -108,28 +132,38 @@ style-local: codespell --write-changes --skip=$(skipped_files) --disable-colors EXTRA_DIST = \ + .asf.yaml \ .clang-format \ + .dockerignore \ .editorconfig \ + .eslintignore \ + .eslintrc.json \ + .flake8 \ + .gitattributes \ + .gitignore \ .travis.yml \ .rustfmt.toml \ - .dockerignore \ + ApacheThrift.nuspec \ appveyor.yml \ + bootstrap.sh \ bower.json \ build \ - bootstrap.sh \ - cleanup.sh \ + CHANGES.md \ CMakeLists.txt \ composer.json \ contrib \ CONTRIBUTING.md \ debian \ - doc \ doap.rdf \ - package.json \ - sonar-project.properties \ + doc \ + dub.json \ + jitpack.yml \ LANGUAGES.md \ LICENSE \ - CHANGES \ NOTICE \ + package.json \ + package-lock.json \ + phpcs.xml.dist \ README.md \ + sonar-project.properties \ Thrift.podspec diff --git a/NOTICE b/NOTICE index 902dc8d3140..37824e7fb66 100644 --- a/NOTICE +++ b/NOTICE @@ -1,5 +1,5 @@ Apache Thrift -Copyright 2006-2017 The Apache Software Foundation. +Copyright (C) 2006 - 2019, The Apache Software Foundation This product includes software developed at The Apache Software Foundation (http://www.apache.org/). diff --git a/README.md b/README.md index d09e6e104d0..a22f222cfcb 100644 --- a/README.md +++ b/README.md @@ -4,22 +4,27 @@ Apache Thrift Introduction ============ -Thrift is a lightweight, language-independent software stack with an -associated code generation mechanism for point-to-point RPC. Thrift provides -clean abstractions for data transport, data serialization, and application -level processing. The code generation system takes a simple definition -language as input and generates code across programming languages that -uses the abstracted stack to build interoperable RPC clients and servers. +Thrift is a lightweight, language-independent software stack for +point-to-point RPC implementation. +Thrift provides clean abstractions and implementations for data transport, +data serialization, and application level processing. The code generation +system takes a simple definition language as input and generates code +across programming languages that uses the abstracted stack to build +interoperable RPC clients and servers. ![Apache Thrift Layered Architecture](doc/images/thrift-layers.png) Thrift makes it easy for programs written in different programming languages to share data and call remote procedures. With support -for [25 programming languages](LANGUAGES.md), chances are Thrift +for [28 programming languages](LANGUAGES.md), chances are Thrift supports the languages that you currently use. Thrift is specifically designed to support non-atomic version changes -across client and server code. +across client and server code. This allows you to upgrade your +server while still being able to service older clients; or have newer +clients issue requests to older servers. An excellent community-provided +write-up about thrift and compatibility when versioning an API can be +found in the [Thrift Missing Guide](https://diwakergupta.github.io/thrift-missing-guide/#_versioning_compatibility). For more details on Thrift's design and implementation, see the Thrift whitepaper included in this distribution, or at the README.md file @@ -30,7 +35,9 @@ Status | Branch | Travis | Appveyor | Coverity Scan | codecov.io | Website | | :----- | :----- | :------- | :------------ | :--------- | :------ | -| [`master`](https://github.com/apache/thrift/tree/master) | [![Build Status](https://travis-ci.org/apache/thrift.svg?branch=master)](https://travis-ci.org/apache/thrift) | [![Build status](https://ci.appveyor.com/api/projects/status/github/apache/thrift?branch=master&svg=true)](https://ci.appveyor.com/project/ApacheSoftwareFoundation/thrift/history) | [![Coverity Scan Build Status](https://scan.coverity.com/projects/1345/badge.svg)](https://scan.coverity.com/projects/thrift) | | [![Website](https://img.shields.io/badge/official-website-brightgreen.svg)](https://thrift.apache.org/) | +| [`master`](https://github.com/apache/thrift/tree/master) | [![Build Status](https://travis-ci.org/apache/thrift.svg?branch=master)](https://travis-ci.org/apache/thrift/branches) | [![Build status](https://ci.appveyor.com/api/projects/status/github/apache/thrift?branch=master&svg=true)](https://ci.appveyor.com/project/ApacheSoftwareFoundation/thrift/history) | [![Coverity Scan Build Status](https://scan.coverity.com/projects/1345/badge.svg)](https://scan.coverity.com/projects/thrift) | | [![Website](https://img.shields.io/badge/official-website-brightgreen.svg)](https://thrift.apache.org/) | +| [`0.13.0`](https://github.com/apache/thrift/tree/0.13.0) | [![Build Status](https://travis-ci.org/apache/thrift.svg?branch=0.13.0)](https://travis-ci.org/apache/thrift/branches) | | | | | +| [`0.12.0`](https://github.com/apache/thrift/tree/0.12.0) | [![Build Status](https://travis-ci.org/apache/thrift.svg?branch=0.12.0)](https://travis-ci.org/apache/thrift/branches) | | | | | Releases ======== @@ -100,7 +107,7 @@ We have [comprehensive building instructions for docker](build/docker/README.md) Requirements ============ -See http://thrift.apache.org/docs/install for a list of build requirements (may be stale). Alternatively see the docker build environments for a list of prerequisites. +See http://thrift.apache.org/docs/install for a list of build requirements (may be stale). Alternatively, see the docker build environments for a list of prerequisites. Resources ========= @@ -130,7 +137,7 @@ From the top directory, do: ./configure You may need to specify the location of the boost files explicitly. -If you installed boost in /usr/local, you would run configure as follows: +If you installed boost in `/usr/local`, you would run configure as follows: ./configure --with-boost=/usr/local @@ -150,17 +157,21 @@ Run ./configure --help to see other configuration options Please be aware that the Python library will ignore the --prefix option and just install wherever Python's distutils puts it (usually along -the lines of /usr/lib/pythonX.Y/site-packages/). If you need to control +the lines of `/usr/lib/pythonX.Y/site-packages/`). If you need to control where the Python modules are installed, set the PY_PREFIX variable. (DESTDIR is respected for Python and C++.) Make thrift: - make + make From the top directory, become superuser and do: - make install + make install + +Uninstall thrift: + + make uninstall Note that some language packages must be installed manually using build tools better suited to those languages (at the time of this writing, this applies @@ -169,13 +180,21 @@ to Java, Ruby, PHP). Look for the README.md file in the lib// folder for more details on the installation of each language library package. +Package Managers +================ + +Apache Thrift is available via a number of package managers, a list which is +is steadily growing. A more detailed overview can be found +[at the Apache Thrift web site under "Libraries"](http://thrift.apache.org/lib/) +and/or in the respective READMEs for each language under /lib + Testing ======= There are a large number of client library tests that can all be run from the top-level directory. - make -k check + make -k check This will make all of the libraries (as necessary), and run through the unit tests defined in each of the client libraries. If a single @@ -184,7 +203,7 @@ at the end. To run the cross-language test suite, please run: - make cross + make cross This will run a set of tests that use different language clients and servers. diff --git a/Thrift-swift3.podspec b/Thrift-swift3.podspec deleted file mode 100644 index 542d81057bc..00000000000 --- a/Thrift-swift3.podspec +++ /dev/null @@ -1,16 +0,0 @@ -Pod::Spec.new do |s| - s.name = "Thrift-swift3" - s.version = "0.12.0" - s.summary = "Apache Thrift is a lightweight, language-independent software stack with an associated code generation mechanism for RPC." - s.description = <<-DESC -The Apache Thrift software framework, for scalable cross-language services development, combines a software stack with a code generation engine to build services that work efficiently and seamlessly between C++, Java, Python, PHP, Ruby, Erlang, Perl, Haskell, C#, Cocoa, JavaScript, Node.js, Smalltalk, OCaml and Delphi and other languages. - DESC - s.homepage = "http://thrift.apache.org" - s.license = { :type => 'Apache License, Version 2.0', :url => 'https://www.apache.org/licenses/LICENSE-2.0' } - s.author = { "Apache Thrift Developers" => "dev@thrift.apache.org" } - s.ios.deployment_target = '9.0' - s.osx.deployment_target = '10.10' - s.requires_arc = true - s.source = { :git => "https://github.com/apache/thrift.git", :tag => "0.12.0" } - s.source_files = "lib/swift/Sources/*.swift" -end diff --git a/Thrift.podspec b/Thrift.podspec index e5b8149b6d2..46de2b40547 100644 --- a/Thrift.podspec +++ b/Thrift.podspec @@ -1,18 +1,15 @@ Pod::Spec.new do |s| - s.name = "Thrift" - s.version = "0.12.0" + s.name = 'Thrift' + s.version = '0.14.0' s.summary = "Apache Thrift is a lightweight, language-independent software stack with an associated code generation mechanism for RPC." s.description = <<-DESC -The Apache Thrift software framework, for scalable cross-language services development, combines a software stack with a code generation engine to build services that work efficiently and seamlessly between C++, Java, Python, PHP, Ruby, Erlang, Perl, Haskell, C#, Cocoa, JavaScript, Node.js, Smalltalk, OCaml and Delphi and other languages. +The Apache Thrift scalable cross-language software framework for networked services development combines a software stack with a code generation engine to build services that work efficiently and seamlessly between many programming languages. DESC - s.homepage = "http://thrift.apache.org" + s.homepage = 'https://thrift.apache.org' s.license = { :type => 'Apache License, Version 2.0', :url => 'https://www.apache.org/licenses/LICENSE-2.0' } - s.author = { "Apache Thrift Developers" => "dev@thrift.apache.org" } - s.requires_arc = true - s.ios.deployment_target = '7.0' - s.osx.deployment_target = '10.8' - s.ios.framework = 'CFNetwork' - s.osx.framework = 'CoreServices' - s.source = { :git => "https://github.com/apache/thrift.git", :tag => "0.12.0" } - s.source_files = 'lib/cocoa/src/**/*.{h,m,swift}' + s.author = { 'Apache Thrift Developers' => 'dev@thrift.apache.org' } + s.ios.deployment_target = '9.0' + s.osx.deployment_target = '10.10' + s.source = { :git => 'https://github.com/apache/thrift.git', :tag => 'v0.14.0' } + s.source_files = 'lib/swift/Sources/*.swift' end diff --git a/aclocal/tar.m4 b/aclocal/tar.m4 new file mode 100644 index 00000000000..4e16b6b2c9a --- /dev/null +++ b/aclocal/tar.m4 @@ -0,0 +1,132 @@ +# Check how to create a tarball. -*- Autoconf -*- + +# Copyright (C) 2004-2018 Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# _AM_PROG_TAR(FORMAT) +# -------------------- +# Check how to create a tarball in format FORMAT. +# FORMAT should be one of 'v7', 'ustar', or 'pax'. +# +# Substitute a variable $(am__tar) that is a command +# writing to stdout a FORMAT-tarball containing the directory +# $tardir. +# tardir=directory && $(am__tar) > result.tar +# +# Substitute a variable $(am__untar) that extract such +# a tarball read from stdin. +# $(am__untar) < result.tar +# +AC_DEFUN([_AM_PROG_TAR], +[# Always define AMTAR for backward compatibility. Yes, it's still used +# in the wild :-( We should find a proper way to deprecate it ... +AC_SUBST([AMTAR], ['$${TAR-tar}']) + +# We'll loop over all known methods to create a tar archive until one works. +_am_tools='gnutar m4_if([$1], [ustar], [plaintar]) pax cpio none' + +m4_if([$1], [v7], + [am__tar='$${TAR-tar} chof - "$$tardir"' am__untar='$${TAR-tar} xf -'], + + [m4_case([$1], + [ustar], + [# The POSIX 1988 'ustar' format is defined with fixed-size fields. + # There is notably a 21 bits limit for the UID and the GID. In fact, + # the 'pax' utility can hang on bigger UID/GID (see automake bug#8343 + # and bug#13588). + am_max_uid=2097151 # 2^21 - 1 + am_max_gid=$am_max_uid + # The $UID and $GID variables are not portable, so we need to resort + # to the POSIX-mandated id(1) utility. Errors in the 'id' calls + # below are definitely unexpected, so allow the users to see them + # (that is, avoid stderr redirection). + am_uid=`id -u || echo unknown` + am_gid=`id -g || echo unknown` + AC_MSG_CHECKING([whether UID '$am_uid' is supported by ustar format]) + if test $am_uid -le $am_max_uid; then + AC_MSG_RESULT([yes]) + else + AC_MSG_RESULT([no]) + _am_tools=none + fi + AC_MSG_CHECKING([whether GID '$am_gid' is supported by ustar format]) + if test $am_gid -le $am_max_gid; then + AC_MSG_RESULT([yes]) + else + AC_MSG_RESULT([no]) + _am_tools=none + fi], + + [pax], + [], + + [m4_fatal([Unknown tar format])]) + + AC_MSG_CHECKING([how to create a $1 tar archive]) + + # Go ahead even if we have the value already cached. We do so because we + # need to set the values for the 'am__tar' and 'am__untar' variables. + _am_tools=${am_cv_prog_tar_$1-$_am_tools} + + for _am_tool in $_am_tools; do + case $_am_tool in + gnutar) + for _am_tar in tar gnutar gtar; do + AM_RUN_LOG([$_am_tar --version]) && break + done + am__tar="$_am_tar --hard-dereference --format=m4_if([$1], [pax], [posix], [$1]) -chf - "'"$$tardir"' + am__tar_="$_am_tar --hard-dereference --format=m4_if([$1], [pax], [posix], [$1]) -chf - "'"$tardir"' + am__untar="$_am_tar -xf -" + ;; + plaintar) + # Must skip GNU tar: if it does not support --format= it doesn't create + # ustar tarball either. + (tar --version) >/dev/null 2>&1 && continue + am__tar='tar chf --hard-dereference - "$$tardir"' + am__tar_='tar chf --hard-dereference - "$tardir"' + am__untar='tar xf -' + ;; + pax) + am__tar='pax -L -x $1 -w "$$tardir"' + am__tar_='pax -L -x $1 -w "$tardir"' + am__untar='pax -r' + ;; + cpio) + am__tar='find "$$tardir" -print | cpio -o -H $1 -L' + am__tar_='find "$tardir" -print | cpio -o -H $1 -L' + am__untar='cpio -i -H $1 -d' + ;; + none) + am__tar=false + am__tar_=false + am__untar=false + ;; + esac + + # If the value was cached, stop now. We just wanted to have am__tar + # and am__untar set. + test -n "${am_cv_prog_tar_$1}" && break + + # tar/untar a dummy directory, and stop if the command works. + rm -rf conftest.dir + mkdir conftest.dir + echo GrepMe > conftest.dir/file + AM_RUN_LOG([tardir=conftest.dir && eval $am__tar_ >conftest.tar]) + rm -rf conftest.dir + if test -s conftest.tar; then + AM_RUN_LOG([$am__untar /dev/null 2>&1 && break + fi + done + rm -rf conftest.dir + + AC_CACHE_VAL([am_cv_prog_tar_$1], [am_cv_prog_tar_$1=$_am_tool]) + AC_MSG_RESULT([$am_cv_prog_tar_$1])]) + +AC_SUBST([am__tar]) +AC_SUBST([am__untar]) +]) # _AM_PROG_TAR diff --git a/appveyor.yml b/appveyor.yml index b126dc9edc0..96fe30cf3a4 100755 --- a/appveyor.yml +++ b/appveyor.yml @@ -19,16 +19,19 @@ # build Apache Thrift on AppVeyor - https://ci.appveyor.com -version: '0.12.0.{build}' +version: '0.14.0.{build}' shallow_clone: true +branches: + only: + - master + - /^(release/)?\d+\.\d+\.\d+$/ + os: - Visual Studio 2017 matrix: - allow_failures: - - PROFILE: CYGWIN fast_finish: true environment: @@ -36,38 +39,33 @@ environment: - PROFILE: MSVC2017 PLATFORM: x64 CONFIGURATION: Release - BOOST_VERSION: 1.65.1 + BUILD_SHARED_LIBS: ON + BOOST_VERSION: 1.67.0 LIBEVENT_VERSION: 2.1.8 PYTHON_VERSION: 3.6 QT_VERSION: 5.10 ZLIB_VERSION: 1.2.11 - DISABLED_TESTS: StressTestNonBlocking - - PROFILE: MSVC2013 + - PROFILE: MSVC2015 PLATFORM: x86 - CONFIGURATION: Release - BOOST_VERSION: 1.58.0 + CONFIGURATION: Debug + BUILD_SHARED_LIBS: OFF + BOOST_VERSION: 1.62.0 LIBEVENT_VERSION: 2.0.22 PYTHON_VERSION: 3.5 QT_VERSION: 5.8 ZLIB_VERSION: 1.2.8 - DISABLED_TESTS: StressTestNonBlocking APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2015 - PROFILE: MINGW PLATFORM: x64 CONFIGURATION: RelWithDebInfo - DISABLED_TESTS: StressTestNonBlocking + DISABLED_TESTS: (StalenessCheckTest) - PROFILE: CYGWIN - PLATFORM: x86 + PLATFORM: x64 CONFIGURATION: RelWithDebInfo - DISABLED_TESTS: (ZlibTest|OpenSSLManualInitTest|TNonblockingServerTest|StressTestNonBlocking) - -# - PROFILE: CYGWIN -# PLATFORM: x64 -# CONFIGURATION: RelWithDebInfo -# DISABLED_TESTS: (ZlibTest|OpenSSLManualInitTest|TNonblockingServerTest|StressTestNonBlocking) + DISABLED_TESTS: (ZlibTest|OpenSSLManualInitTest|TNonblockingServerTest) install: - cd %APPVEYOR_BUILD_FOLDER% @@ -102,8 +100,8 @@ test_script: # # enables RDP at the end of the build job so you can login and re-run # commands to see why something failed... -#on_finish: -# - ps: $blockRdp = $true; iex ((new-object net.webclient).DownloadString('https://raw.githubusercontent.com/appveyor/ci/master/scripts/enable-rdp.ps1')) +# on_finish: +# - ps: $blockRdp = $true; iex ((new-object net.webclient).DownloadString('https://raw.githubusercontent.com/appveyor/ci/master/scripts/enable-rdp.ps1')) # # also need: # environment: diff --git a/bootstrap.sh b/bootstrap.sh index 2452eea4486..1989437a041 100755 --- a/bootstrap.sh +++ b/bootstrap.sh @@ -19,7 +19,10 @@ # under the License. # -./cleanup.sh +echo -n "make distclean... " +make -k distclean >/dev/null 2>&1 +echo "ok" + if test -d lib/php/src/ext/thrift_protocol ; then if phpize -v >/dev/null 2>/dev/null ; then (cd lib/php/src/ext/thrift_protocol && phpize) diff --git a/bower.json b/bower.json index 5f407606ee0..68f2c8d2c78 100644 --- a/bower.json +++ b/bower.json @@ -1,7 +1,7 @@ { "name": "thrift", - "version": "0.12.0", - "homepage": "https://git-wip-us.apache.org/repos/asf/thrift.git", + "version": "0.14.0", + "homepage": "https://github.com/apache/thrift.git", "authors": [ "Apache Thrift " ], @@ -11,6 +11,5 @@ "thrift" ], "license": "Apache v2", - "ignore": [ - ] + "ignore": [] } diff --git a/build/appveyor/CYGW-appveyor-build.bat b/build/appveyor/CYGW-appveyor-build.bat index c226222941e..7f332871433 100644 --- a/build/appveyor/CYGW-appveyor-build.bat +++ b/build/appveyor/CYGW-appveyor-build.bat @@ -23,13 +23,12 @@ SET CMAKEARGS=^ -G'%GENERATOR%' ^ -DCMAKE_BUILD_TYPE=%CONFIGURATION% ^ -DCMAKE_INSTALL_PREFIX=%INSTDIR% ^ - -DCMAKE_CXX_EXTENSIONS=ON ^ -DCMAKE_CXX_FLAGS="-D_GNU_SOURCE" ^ - -DCMAKE_CXX_STANDARD=11 ^ - -DWITH_PYTHON=OFF ^ - -DWITH_SHARED_LIB=OFF ^ - -DWITH_STATIC_LIB=ON ^ - -DWITH_STDTHREADS=ON + -DWITH_JAVA=OFF ^ + -DWITH_PYTHON=OFF + +:: -DCMAKE_CXX_EXTENSIONS=ON ^ +:: -DCMAKE_CXX_STANDARD=11 ^ @ECHO ON %BASH% -lc "mkdir -p %BUILDDIR% && cd %BUILDDIR% && cmake.exe %SRCDIR% %CMAKEARGS% && cmake --build . --config %CONFIGURATION% --target install" || EXIT /B diff --git a/build/appveyor/CYGW-appveyor-install.bat b/build/appveyor/CYGW-appveyor-install.bat index 77db7d40a7e..69a159fd871 100644 --- a/build/appveyor/CYGW-appveyor-install.bat +++ b/build/appveyor/CYGW-appveyor-install.bat @@ -25,10 +25,26 @@ CALL cl_banner_install.bat || EXIT /B CALL cl_setenv.bat || EXIT /B CALL cl_showenv.bat || EXIT /B +:: +:: Upgrades cygwin to the latest, if you want... +:: +:: appveyor DownloadFile "https://cygwin.com/setup-x86_64.exe" +:: setup-x86_64.exe --quiet-mode --wait --upgrade-also --packages="gcc-g++" + :: :: Install apt-cyg for package management :: %BASH% -lc "wget rawgit.com/transcode-open/apt-cyg/master/apt-cyg && install apt-cyg /bin && rm -f apt-cyg" || EXIT /B %BASH% -lc "apt-cyg update" || EXIT /B -%BASH% -lc "apt-cyg install bison cmake flex gcc-g++ libboost-devel libevent-devel make openssl-devel zlib-devel" +%BASH% -lc "apt-cyg install bison cmake flex gcc-g++ libboost-devel libevent-devel make openssl-devel xz zlib-devel" + +:: +:: We need a newer version of cmake, the one cygwin provides is too old +:: to recognize the version of boost. Luckily there is a pre-release +:: one available, however cygwin's own setup program has no command line +:: option to allow access to test packages! +:: + +%BASH% -lc "apt-cyg remove cmake" +%BASH% -lc "cd / && wget http://mirror.clarkson.edu/cygwin/x86_64/release/cmake/cmake-3.14.5-1.tar.xz && tar xJf cmake-3.14.5-1.tar.xz && hash -r && cmake --version" diff --git a/build/appveyor/MING-appveyor-build.bat b/build/appveyor/MING-appveyor-build.bat index b37a95a79dc..eec65f897f6 100644 --- a/build/appveyor/MING-appveyor-build.bat +++ b/build/appveyor/MING-appveyor-build.bat @@ -27,9 +27,7 @@ SET CMAKEARGS=^ -DCMAKE_C_COMPILER=/mingw%NORM_PLATFORM%/bin/gcc.exe ^ -DCMAKE_CXX_COMPILER=/mingw%NORM_PLATFORM%/bin/g++.exe ^ -DOPENSSL_ROOT_DIR=/mingw%NORM_PLATFORM% ^ - -DWITH_PYTHON=OFF ^ - -DWITH_SHARED_LIB=OFF ^ - -DWITH_STATIC_LIB=ON + -DWITH_PYTHON=OFF @ECHO ON %BASH% -lc "mkdir -p %BUILDDIR% && cd %BUILDDIR% && cmake.exe %SRCDIR% %CMAKEARGS% && cmake --build . --config %CONFIGURATION% --target install" || EXIT /B diff --git a/build/appveyor/MING-appveyor-install.bat b/build/appveyor/MING-appveyor-install.bat index 1069b058b4b..862cb1c051f 100644 --- a/build/appveyor/MING-appveyor-install.bat +++ b/build/appveyor/MING-appveyor-install.bat @@ -36,10 +36,24 @@ SET PACKAGES=^ ::mingw-w64-%MINGWPLAT%-qt5 : WAY too large (1GB download!) - tested in cygwin builds anyway +:: the following uninstall and system upgrade was causing issues; appveyor's is relatively new :: Remove old packages that no longer exist to avoid an error -%BASH% -lc "pacman --noconfirm --remove libcatgets catgets || true" || EXIT /B +:: %BASH% -lc "pacman --noconfirm --remove libcatgets catgets || true" || EXIT /B +:: Remove incompatible packages 8.2.0-3 and 7.3.0-2 (mingw packaging bugs if you ask me!) +:: %BASH% -lc "pacman --noconfirm --remove mingw-w64-x86_64-gcc-ada mingw-w64-x86_64-gcc-objc || true" || EXIT /B +:: %BASH% -lc "pacman --noconfirm --remove mingw-w64-x86_64-gcc-ada mingw-w64-x86_64-gcc-objc || true" || EXIT /B + +:: Upgrade things +:: %BASH% -lc "pacman --noconfirm -Syu %IGNORE%" || EXIT /B +:: %BASH% -lc "pacman --noconfirm -Su %IGNORE%" || EXIT /B + +:: Updata the new key +%BASH% -lc "curl -O http://repo.msys2.org/msys/x86_64/msys2-keyring-r21.b39fb11-1-any.pkg.tar.xz" || EXIT /B +%BASH% -lc "curl -O http://repo.msys2.org/msys/x86_64/msys2-keyring-r21.b39fb11-1-any.pkg.tar.xz.sig" || EXIT /B +%BASH% -lc "pacman-key --verify msys2-keyring-r21.b39fb11-1-any.pkg.tar.xz.sig" || EXIT /B +%BASH% -lc "pacman --noconfirm -U --config <(echo) msys2-keyring-r21.b39fb11-1-any.pkg.tar.xz" || EXIT /B :: Upgrade things -%BASH% -lc "pacman --noconfirm -Syu %IGNORE%" || EXIT /B -%BASH% -lc "pacman --noconfirm -Su %IGNORE%" || EXIT /B -%BASH% -lc "pacman --noconfirm %PACKAGES%" || EXIT /B \ No newline at end of file +%BASH% -lc "pacman --noconfirm -Sy" || EXIT /B +%BASH% -lc "pacman --noconfirm -Udd https://repo.msys2.org/msys/x86_64/pacman-5.2.2-5-x86_64.pkg.tar.xz" || EXIT /B +%BASH% -lc "pacman --noconfirm %PACKAGES%" || EXIT /B diff --git a/build/appveyor/MSVC-appveyor-build.bat b/build/appveyor/MSVC-appveyor-build.bat index a4b92a29cd2..3d2bbeed308 100644 --- a/build/appveyor/MSVC-appveyor-build.bat +++ b/build/appveyor/MSVC-appveyor-build.bat @@ -21,25 +21,37 @@ CALL cl_setenv.bat || EXIT /B MKDIR "%BUILDDIR%" || EXIT /B CD "%BUILDDIR%" || EXIT /B +:: When libraries cannot be found, things might have been updated +:: so uncomment this and submit a pull request to see what's there +:: now... +:: DIR C:\Libraries +:: DIR C:\Libraries\boost_1_69_0\lib* +:: DIR C:\Libraries\boost_1_68_0\lib* +:: DIR C:\Libraries\boost_1_67_0\lib* +:: DIR C:\Libraries\boost_1_66_0\lib* +:: DIR C:\Libraries\boost_1_65_0\lib* +:: DIR C:\Libraries\boost_1_64_0\lib* +:: DIR C:\Libraries\boost_1_63_0\lib* +:: DIR C:\Libraries\boost_1_62_0\lib* +:: DIR C:\Libraries\boost_1_61_0\lib* +:: DIR C:\Libraries\boost_1_60_0\lib* + @ECHO ON cmake "%SRCDIR%" ^ -G"%GENERATOR%" ^ -DBISON_EXECUTABLE=C:\ProgramData\chocolatey\lib\winflexbison3\tools\win_bison.exe ^ -DBOOST_ROOT="%BOOST_ROOT%" ^ -DBOOST_LIBRARYDIR="%BOOST_LIBRARYDIR%" ^ + -DBUILD_SHARED_LIBS="%BUILD_SHARED_LIBS%" ^ -DCMAKE_BUILD_TYPE="%CONFIGURATION%" ^ -DCMAKE_INSTALL_PREFIX="%INSTDIR%" ^ -DFLEX_EXECUTABLE=C:\ProgramData\chocolatey\lib\winflexbison3\tools\win_flex.exe ^ - -DINTTYPES_ROOT="%WIN3P%\msinttypes" ^ -DLIBEVENT_ROOT="%WIN3P%\libevent-%LIBEVENT_VERSION%-stable" ^ -DOPENSSL_ROOT_DIR="%OPENSSL_ROOT%" ^ -DOPENSSL_USE_STATIC_LIBS=OFF ^ -DZLIB_LIBRARY="%WIN3P%\zlib-inst\lib\zlib%ZLIB_LIB_SUFFIX%.lib" ^ -DZLIB_ROOT="%WIN3P%\zlib-inst" ^ - -DWITH_PYTHON=%WITH_PYTHON% ^ - -DWITH_%THREADMODEL%THREADS=ON ^ - -DWITH_SHARED_LIB=OFF ^ - -DWITH_STATIC_LIB=ON || EXIT /B + -DWITH_PYTHON=%WITH_PYTHON% || EXIT /B @ECHO OFF cmake --build . ^ diff --git a/build/appveyor/MSVC-appveyor-install.bat b/build/appveyor/MSVC-appveyor-install.bat index f973d29cff8..09b7cc49445 100644 --- a/build/appveyor/MSVC-appveyor-install.bat +++ b/build/appveyor/MSVC-appveyor-install.bat @@ -56,4 +56,12 @@ pip.exe ^ tornado ^ twisted || EXIT /B -cinst -y ghc || EXIT /B +cinst -y cabal --version 2.4.1.0 || EXIT /B +cinst -y ghc --version 8.6.5 || EXIT /B + +:: Adobe Flex SDK 4.6 for ActionScript +MKDIR "C:\Adobe\Flex\SDK\4.6" || EXIT /B +appveyor DownloadFile http://download.macromedia.com/pub/flex/sdk/flex_sdk_4.6.zip -FileName C:\Adobe\Flex\SDK\4.6\SDK.zip || EXIT /B +CD "C:\Adobe\Flex\SDK\4.6" || EXIT /B +7z x SDK.zip || EXIT /B +SETX FLEX_HOME "C:\Adobe\Flex\SDK\4.6" diff --git a/build/appveyor/cl_setenv.bat b/build/appveyor/cl_setenv.bat index 62856cba0c1..98931a6031d 100644 --- a/build/appveyor/cl_setenv.bat +++ b/build/appveyor/cl_setenv.bat @@ -14,9 +14,7 @@ @ECHO OFF - IF "%PROFILE%" == "MSVC2010" ( - CALL "C:\Program Files (x86)\Microsoft Visual Studio 10.0\VC\vcvarsall.bat" %PLATFORM% -) ELSE IF "%PROFILE%" == "MSVC2012" ( +IF "%PROFILE%" == "MSVC2012" ( CALL "C:\Program Files (x86)\Microsoft Visual Studio 11.0\VC\vcvarsall.bat" %PLATFORM% ) ELSE IF "%PROFILE%" == "MSVC2013" ( CALL "C:\Program Files (x86)\Microsoft Visual Studio 12.0\VC\vcvarsall.bat" %PLATFORM% @@ -54,13 +52,6 @@ IF "%PROFILE:~0,4%" == "MSVC" ( SET OPENSSL_ROOT=C:\OpenSSL-Win%NORM_PLATFORM% SET WIN3P=%APPVEYOR_BUILD_FOLDER%\thirdparty - :: MSVC2010 doesn't "do" std::thread - IF "%COMPILER%" == "vc100" ( - SET THREADMODEL=BOOST - ) ELSE ( - SET THREADMODEL=STD - ) - IF "%PYTHON_VERSION%" == "" ( SET WITH_PYTHON=OFF ) ELSE ( @@ -114,15 +105,18 @@ IF "%PROFILE:~0,4%" == "MSVC" ( GOTO :EOF :SETUPNEWERMSVC - FOR /F "USEBACKQ TOKENS=*" %%i IN (`call "%ProgramFiles(x86)%\Microsoft Visual Studio\Installer\vswhere.exe" -version "[15.0,16.0)" -property installationPath`) DO ( - IF "%MSVCROOT%" == "" (SET MSVCROOT=%%i) + :: If VsDevCmd.bat has already executed, as is the case in the + :: msvc2017 docker container, skip this... + IF NOT DEFINED VSCMD_VER ( + FOR /F "USEBACKQ TOKENS=*" %%i IN (`call "%ProgramFiles(x86)%\Microsoft Visual Studio\Installer\vswhere.exe" -version "[15.0,16.0)" -property installationPath`) DO ( + IF "%MSVCROOT%" == "" (SET MSVCROOT=%%i) + ) + SET MSVCPLAT=x86 + IF "%PLATFORM%" == "x64" (SET MSVCPLAT=amd64) + + SET CURRENTDIR=%CD% + CALL "!MSVCROOT!\Common7\Tools\VsDevCmd.bat" -arch=!MSVCPLAT! || EXIT /B + CD %CURRENTDIR% + EXIT /B ) - SET MSVCPLAT=x86 - IF "%PLATFORM%" == "x64" (SET MSVCPLAT=amd64) - - SET CURRENTDIR=%CD% - CALL "!MSVCROOT!\Common7\Tools\VsDevCmd.bat" -arch=!MSVCPLAT! || EXIT /B - CD %CURRENTDIR% - EXIT /B - :EOF diff --git a/build/cmake/BoostMacros.cmake b/build/cmake/BoostMacros.cmake new file mode 100644 index 00000000000..cc50b53db09 --- /dev/null +++ b/build/cmake/BoostMacros.cmake @@ -0,0 +1,49 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +set(BOOST_MINREV 1.56) + +# we are not ready for the new style link targets introduced in +# boost 1.70.0 and cmake 3.14.2 which showed up on appveyor in +# mingw builds +set(Boost_NO_BOOST_CMAKE ON) + +macro(REQUIRE_BOOST_HEADERS) + find_package(Boost ${BOOST_MINREV} QUIET REQUIRED) + if (NOT Boost_FOUND) + string(CONCAT fatal_message + "Boost ${BOOST_MINREV} or later is required to build sources in ${CMAKE_CURRENT_SOURCE_DIR}") + message(FATAL_ERROR, ${fatal_message}) + endif() + if (DEFINED Boost_INCLUDE_DIRS) + # pre-boost 1.70.0 aware cmake, otherwise it is using targets + include_directories(SYSTEM "${Boost_INCLUDE_DIRS}") + endif() +endmacro() + +macro(REQUIRE_BOOST_LIBRARIES libs) + message(STATUS "Locating boost libraries required by sources in ${CMAKE_CURRENT_SOURCE_DIR}") + find_package(Boost ${BOOST_MINREV} REQUIRED COMPONENTS ${${libs}}) + if (NOT Boost_FOUND) + string(CONCAT fatal_message + "Boost ${BOOST_MINREV} or later is required to build sources in ${CMAKE_CURRENT_SOURCE_DIR}, " + "or use -DBUILD_TESTING=OFF") + message(FATAL_ERROR, ${fatal_message}) + endif() +endmacro() diff --git a/build/cmake/BuildType.cmake b/build/cmake/BuildType.cmake new file mode 100644 index 00000000000..b3bf353064a --- /dev/null +++ b/build/cmake/BuildType.cmake @@ -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. +# + +# originally from: +# https://raw.githubusercontent.com/OpenChemistry/tomviz/master/cmake/BuildType.cmake + +# Set a default build type if none was specified +set(default_build_type "RelWithDebInfo") +if(EXISTS "${CMAKE_SOURCE_DIR}/.git") + set(default_build_type "Debug") +endif() + +if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) + message(STATUS "Setting build type to '${default_build_type}' as none was specified.") + set(CMAKE_BUILD_TYPE "${default_build_type}" CACHE + STRING "Choose the type of build." FORCE) + # Set the possible values of build type for cmake-gui + set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS "Debug" "Release" + "MinSizeRel" "RelWithDebInfo") +endif() diff --git a/build/cmake/ConfigureChecks.cmake b/build/cmake/ConfigureChecks.cmake index 457bfe05daf..276d8ef5e83 100644 --- a/build/cmake/ConfigureChecks.cmake +++ b/build/cmake/ConfigureChecks.cmake @@ -64,7 +64,7 @@ include(CheckCXXSourceCompiles) check_cxx_source_compiles( " #include - int main(){char b;char *a = strerror_r(0, &b, 0); return(0);} + int main(){char b;char *a = strerror_r(0, &b, 0); static_cast(a); return(0);} " STRERROR_R_CHAR_P) diff --git a/build/cmake/DefineCMakeDefaults.cmake b/build/cmake/DefineCMakeDefaults.cmake index dc2cc299ac5..2b0cdbb94b3 100644 --- a/build/cmake/DefineCMakeDefaults.cmake +++ b/build/cmake/DefineCMakeDefaults.cmake @@ -34,21 +34,12 @@ set(CMAKE_INCLUDE_DIRECTORIES_PROJECT_BEFORE ON) # since cmake 2.4.0 set(CMAKE_COLOR_MAKEFILE ON) -# Define the generic version of the libraries here -set(GENERIC_LIB_VERSION "0.12.0") -set(GENERIC_LIB_SOVERSION "0") - -# Set the default build type to release with debug info -if (NOT CMAKE_BUILD_TYPE) - set(CMAKE_BUILD_TYPE RelWithDebInfo - CACHE STRING - "Choose the type of build, options are: None Debug Release RelWithDebInfo MinSizeRel." - ) -endif (NOT CMAKE_BUILD_TYPE) - # Create the compile command database for clang by default set(CMAKE_EXPORT_COMPILE_COMMANDS ON) +# Set the CMAKE_BUILD_TYPE if it is not already defined +include(BuildType) + # Put the libraries and binaries that get built into directories at the # top of the build tree rather than in hard-to-find leaf # directories. This simplifies manual testing and the use of the build @@ -73,21 +64,29 @@ set(CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE) # C++ Language Level Defaults - this depends on the compiler capabilities # if (NOT DEFINED CMAKE_CXX_STANDARD) - if (MSVC AND MSVC_VERSION LESS 1800) - # MSVC 2012 and earlier don't support template aliases so you have to use C++98 - set(CMAKE_CXX_STANDARD 98) - message(STATUS "Setting C++98 as the default language level (for an older MSVC compiler).") - else() - set(CMAKE_CXX_STANDARD 11) # C++11 - message(STATUS "Setting C++11 as the default language level.") - endif() + set(CMAKE_CXX_STANDARD 11) # C++11 + message(STATUS "Setting C++11 as the default language level.") message(STATUS "To specify a different C++ language level, set CMAKE_CXX_STANDARD") endif() -if (NOT DEFINED CMAKE_CXX_STANDARD_REQUIRED) - set(CMAKE_CXX_STANDARD_REQUIRED OFF) # can degrade to C++98 if compiler does not support C++11 +if (CMAKE_CXX_STANDARD EQUAL 98) + message(FATAL_ERROR "only C++11 or above C++ standard is supported") +elseif (CMAKE_CXX_STANDARD EQUAL 11) + # should not fallback to C++98 + set(CMAKE_CXX_STANDARD_REQUIRED ON) endif() if (NOT DEFINED CMAKE_CXX_EXTENSIONS) set(CMAKE_CXX_EXTENSIONS OFF) # use standards compliant language level for portability endif() + +if(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC") + include(CheckCXXCompilerFlag) + set(CMAKE_REQUIRED_QUIET ON) + check_cxx_compiler_flag("/Zc:__cplusplus" res_var) + if (res_var) + # Make MSVC reporting correct value for __cplusplus + # See https://blogs.msdn.microsoft.com/vcblog/2018/04/09/msvc-now-correctly-reports-__cplusplus/ + add_compile_options("/Zc:__cplusplus") + endif() +endif() diff --git a/build/cmake/DefineInstallationPaths.cmake b/build/cmake/DefineInstallationPaths.cmake index 122f0f6a937..23962b442d0 100644 --- a/build/cmake/DefineInstallationPaths.cmake +++ b/build/cmake/DefineInstallationPaths.cmake @@ -20,7 +20,17 @@ # Define the default install paths set(BIN_INSTALL_DIR "bin" CACHE PATH "The binary install dir (default: bin)") -set(LIB_INSTALL_DIR "lib${LIB_SUFFIX}" CACHE PATH "The library install dir (default: lib${LIB_SUFFIX})") +if(MSVC) + set(LIB_INSTALL_DIR "bin${LIB_SUFFIX}" CACHE PATH "The library install dir (default: bin${LIB_SUFFIX})") +else() + set(LIB_INSTALL_DIR "lib${LIB_SUFFIX}" CACHE PATH "The library install dir (default: lib${LIB_SUFFIX})") +endif() set(INCLUDE_INSTALL_DIR "include" CACHE PATH "The library install dir (default: include)") -set(CMAKE_INSTALL_DIR "cmake" CACHE PATH "The subdirectory to install cmake config files (default: cmake)") +set(CMAKE_INSTALL_DIR "lib/cmake" CACHE PATH "The subdirectory to install cmake config files (default: cmake)") +set(PKGCONFIG_INSTALL_DIR "lib/pkgconfig" CACHE PATH "The subdirectory to install pkgconfig config files (default: lib/pkgconfig)") set(DOC_INSTALL_DIR "share/doc" CACHE PATH "The subdirectory to install documentation files (default: share/doc)") +set(prefix "${CMAKE_INSTALL_PREFIX}") +set(exec_prefix "${CMAKE_INSTALL_PREFIX}/bin") +set(libdir "${CMAKE_INSTALL_PREFIX}/lib") +set(includedir "${CMAKE_INSTALL_PREFIX}/include") +set(cmakedir "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_DIR}") diff --git a/build/cmake/DefineOptions.cmake b/build/cmake/DefineOptions.cmake index f419229a0aa..e16e5649c78 100644 --- a/build/cmake/DefineOptions.cmake +++ b/build/cmake/DefineOptions.cmake @@ -29,7 +29,6 @@ if(BUILD_COMPILER OR EXISTS ${THRIFT_COMPILER}) set(HAVE_COMPILER ON) endif() CMAKE_DEPENDENT_OPTION(BUILD_TESTING "Build with unit tests" ON "HAVE_COMPILER" OFF) -CMAKE_DEPENDENT_OPTION(BUILD_EXAMPLES "Build examples" ON "HAVE_COMPILER" OFF) CMAKE_DEPENDENT_OPTION(BUILD_TUTORIALS "Build Thrift tutorials" ON "HAVE_COMPILER" OFF) option(BUILD_LIBRARIES "Build Thrift libraries" ON) @@ -40,27 +39,23 @@ option(BUILD_LIBRARIES "Build Thrift libraries" ON) # and enables the library if all are found. This means the default is to build as # much as possible but leaving out libraries if their dependencies are not met. -option(WITH_BOOST_FUNCTIONAL "Use boost/tr1/functional.hpp even under C++11 or later" OFF) -if (WITH_BOOST_FUNCTIONAL) - add_definitions(-DFORCE_BOOST_FUNCTIONAL) -endif() - -option(WITH_BOOST_SMART_PTR "Use boost/smart_ptr.hpp even under C++11 or later" OFF) -if (WITH_BOOST_SMART_PTR) - add_definitions(-DFORCE_BOOST_SMART_PTR) -endif() - -option(WITH_BOOST_STATIC "Build with Boost static link library" OFF) -set(Boost_USE_STATIC_LIBS ${WITH_BOOST_STATIC}) -if (NOT WITH_BOOST_STATIC) +if (NOT Boost_USE_STATIC_LIBS) add_definitions(-DBOOST_ALL_DYN_LINK) add_definitions(-DBOOST_TEST_DYN_LINK) endif() +# as3 +option(WITH_AS3 "Build ActionScript 3 Thrift Library" ON) +if (WITH_AS3) + set(POSSIBLE_PATHS "${FLEX_HOME}/bin" "$ENV{FLEX_HOME}/bin") + find_program(HAVE_COMPC NAMES compc HINTS ${POSSIBLE_PATHS}) +endif () +CMAKE_DEPENDENT_OPTION(BUILD_AS3 "Build as3 library" ON + "BUILD_LIBRARIES;WITH_AS3;HAVE_COMPC" OFF) + # C++ option(WITH_CPP "Build C++ Thrift library" ON) if(WITH_CPP) - find_package(Boost 1.53 QUIET) # NOTE: Currently the following options are C++ specific, # but in future other libraries might reuse them. # So they are not dependent on WITH_CPP but setting them without WITH_CPP currently @@ -75,27 +70,12 @@ if(WITH_CPP) find_package(Libevent QUIET) CMAKE_DEPENDENT_OPTION(WITH_LIBEVENT "Build with libevent support" ON "Libevent_FOUND" OFF) - find_package(Qt4 QUIET COMPONENTS QtCore QtNetwork) - CMAKE_DEPENDENT_OPTION(WITH_QT4 "Build with Qt4 support" ON - "QT4_FOUND" OFF) find_package(Qt5 QUIET COMPONENTS Core Network) CMAKE_DEPENDENT_OPTION(WITH_QT5 "Build with Qt5 support" ON "Qt5_FOUND" OFF) - if(${WITH_QT4} AND ${WITH_QT5} AND ${CMAKE_MAJOR_VERSION} LESS 3) - # cmake < 3.0.0 causes conflict when building both Qt4 and Qt5 - set(WITH_QT4 OFF) - endif() - find_package(OpenSSL QUIET) - CMAKE_DEPENDENT_OPTION(WITH_OPENSSL "Build with OpenSSL support" ON - "OPENSSL_FOUND" OFF) - option(WITH_STDTHREADS "Build with C++ std::thread support" OFF) - CMAKE_DEPENDENT_OPTION(WITH_BOOSTTHREADS "Build with Boost threads support" OFF - "NOT WITH_STDTHREADS;Boost_FOUND" OFF) endif() CMAKE_DEPENDENT_OPTION(BUILD_CPP "Build C++ library" ON - "BUILD_LIBRARIES;WITH_CPP;Boost_FOUND" OFF) -CMAKE_DEPENDENT_OPTION(WITH_PLUGIN "Build compiler plugin support" OFF - "BUILD_COMPILER;BUILD_CPP" OFF) + "BUILD_LIBRARIES;WITH_CPP" OFF) # C GLib option(WITH_C_GLIB "Build C (GLib) Thrift library" ON) @@ -105,19 +85,11 @@ endif() CMAKE_DEPENDENT_OPTION(BUILD_C_GLIB "Build C (GLib) library" ON "BUILD_LIBRARIES;WITH_C_GLIB;GLIB_FOUND" OFF) -if(BUILD_CPP) - set(boost_components) - if(WITH_BOOSTTHREADS OR BUILD_TESTING) - list(APPEND boost_components system thread) - endif() - if(BUILD_TESTING) - list(APPEND boost_components unit_test_framework filesystem chrono program_options) - endif() - if(boost_components) - find_package(Boost 1.53 REQUIRED COMPONENTS ${boost_components}) - endif() -elseif(BUILD_C_GLIB AND BUILD_TESTING) - find_package(Boost 1.53 REQUIRED) +# OpenSSL +if(WITH_CPP OR WITH_C_GLIB) + find_package(OpenSSL QUIET) + CMAKE_DEPENDENT_OPTION(WITH_OPENSSL "Build with OpenSSL support" ON + "OPENSSL_FOUND" OFF) endif() # Java @@ -133,12 +105,22 @@ else() "BUILD_LIBRARIES;WITH_JAVA;JAVA_FOUND;GRADLEW_FOUND" OFF) endif() +# Javascript +option(WITH_JAVASCRIPT "Build Javascript Thrift library" ON) +CMAKE_DEPENDENT_OPTION(BUILD_JAVASCRIPT "Build Javascript library" ON + "BUILD_LIBRARIES;WITH_JAVASCRIPT;NOT WIN32; NOT CYGWIN" OFF) + +# NodeJS +option(WITH_NODEJS "Build NodeJS Thrift library" ON) +CMAKE_DEPENDENT_OPTION(BUILD_NODEJS "Build NodeJS library" ON + "BUILD_LIBRARIES;WITH_NODEJS" OFF) + # Python option(WITH_PYTHON "Build Python Thrift library" ON) find_package(PythonInterp QUIET) # for Python executable find_package(PythonLibs QUIET) # for Python.h CMAKE_DEPENDENT_OPTION(BUILD_PYTHON "Build Python library" ON - "BUILD_LIBRARIES;WITH_PYTHON;PYTHONLIBS_FOUND" OFF) + "BUILD_LIBRARIES;WITH_PYTHON;PYTHONINTERP_FOUND;PYTHONLIBS_FOUND" OFF) # Haskell option(WITH_HASKELL "Build Haskell Thrift library" ON) @@ -148,22 +130,34 @@ CMAKE_DEPENDENT_OPTION(BUILD_HASKELL "Build GHC library" ON "BUILD_LIBRARIES;WITH_HASKELL;GHC_FOUND;CABAL_FOUND" OFF) # Common library options -option(WITH_SHARED_LIB "Build shared libraries" ON) -option(WITH_STATIC_LIB "Build static libraries" ON) -if (NOT WITH_SHARED_LIB AND NOT WITH_STATIC_LIB) - message(FATAL_ERROR "Cannot build with both shared and static outputs disabled!") +# https://cmake.org/cmake/help/latest/variable/BUILD_SHARED_LIBS.html +# Default on Windows is static, shared mode library support needs work... +if(WIN32) + set(DEFAULT_BUILD_SHARED_LIBS ON) +else() + set(DEFAULT_BUILD_SHARED_LIBS OFF) endif() - -#NOTE: C++ compiler options are defined in the lib/cpp/CMakeLists.txt +option(BUILD_SHARED_LIBS "Build shared libraries" ${DEFAULT_BUILD_SHARED_LIBS}) + +if (WITH_SHARED_LIB) + message(WARNING "WITH_SHARED_LIB is deprecated; use -DBUILD_SHARED_LIBS=ON instead") + set(BUILD_SHARED_LIBS ON) +elseif (WITH_STATIC_LIB) + if (WITH_SHARED_LIB) + message(FATAL_ERROR "Cannot build shared and static together; set BUILD_SHARED_LIBS instead.") + endif () + message(WARNING "WITH_STATIC_LIB is deprecated; use -DBUILD_SHARED_LIBS=OFF instead") + set(BUILD_SHARED_LIBS OFF) +endif () # Visual Studio only options if(MSVC) -option(WITH_MT "Build using MT instead of MD (MSVC only)" OFF) + option(WITH_MT "Build using MT instead of MD (MSVC only)" OFF) endif(MSVC) macro(MESSAGE_DEP flag summary) if(NOT ${flag}) - message(STATUS " - ${summary}") + message(STATUS " - ${summary}") endif() endmacro(MESSAGE_DEP flag summary) @@ -171,22 +165,39 @@ macro(PRINT_CONFIG_SUMMARY) message(STATUS "----------------------------------------------------------") message(STATUS "Thrift version: ${thrift_VERSION} (${thrift_VERSION_MAJOR}.${thrift_VERSION_MINOR}.${thrift_VERSION_PATCH})") message(STATUS "Thrift package version: ${PACKAGE_VERSION}") -message(STATUS "Build configuration Summary") -message(STATUS " Build Thrift compiler: ${BUILD_COMPILER}") -message(STATUS " Build compiler plugin support: ${WITH_PLUGIN}") -message(STATUS " Build with unit tests: ${BUILD_TESTING}") -MESSAGE_DEP(HAVE_COMPILER "Disabled because BUILD_THRIFT=OFF and no valid THRIFT_COMPILER is given") -message(STATUS " Build examples: ${BUILD_EXAMPLES}") -MESSAGE_DEP(HAVE_COMPILER "Disabled because BUILD_THRIFT=OFF and no valid THRIFT_COMPILER is given") -message(STATUS " Build Thrift libraries: ${BUILD_LIBRARIES}") -message(STATUS " Language libraries:") +message(STATUS) +message(STATUS "Build configuration summary") +message(STATUS " Build compiler: ${BUILD_COMPILER}") +message(STATUS " Build libraries: ${BUILD_LIBRARIES}") +message(STATUS " Build tests: ${BUILD_TESTING}") +MESSAGE_DEP(HAVE_COMPILER "Disabled because BUILD_COMPILER=OFF and no valid THRIFT_COMPILER is given") +message(STATUS " Build type: ${CMAKE_BUILD_TYPE}") +message(STATUS) +message(STATUS "Language libraries:") +message(STATUS) +message(STATUS " Build as3 library: ${BUILD_AS3}") +MESSAGE_DEP(WITH_AS3 "Disabled by WITH_AS3=OFF") +MESSAGE_DEP(HAVE_COMPC "Adobe Flex compc was not found - did you set env var FLEX_HOME?") +message(STATUS) +message(STATUS " Build with OpenSSL: ${WITH_OPENSSL}") +if(WITH_OPENSSL) + message(STATUS " Version: ${OPENSSL_VERSION}") +endif() +message(STATUS) message(STATUS " Build C++ library: ${BUILD_CPP}") MESSAGE_DEP(WITH_CPP "Disabled by WITH_CPP=OFF") -MESSAGE_DEP(Boost_FOUND "Boost headers missing") -message(STATUS " C++ Language Level: ${CXX_LANGUAGE_LEVEL}") +if (BUILD_CPP) + message(STATUS " C++ Language Level: ${CXX_LANGUAGE_LEVEL}") + message(STATUS " Build shared libraries: ${BUILD_SHARED_LIBS}") + message(STATUS " Build with libevent support: ${WITH_LIBEVENT}") + message(STATUS " Build with Qt5 support: ${WITH_QT5}") + message(STATUS " Build with ZLIB support: ${WITH_ZLIB}") +endif () +message(STATUS) message(STATUS " Build C (GLib) library: ${BUILD_C_GLIB}") MESSAGE_DEP(WITH_C_GLIB "Disabled by WITH_C_GLIB=OFF") MESSAGE_DEP(GLIB_FOUND "GLib missing") +message(STATUS) message(STATUS " Build Java library: ${BUILD_JAVA}") MESSAGE_DEP(WITH_JAVA "Disabled by WITH_JAVA=OFF") if(ANDROID) @@ -195,25 +206,19 @@ else() MESSAGE_DEP(JAVA_FOUND "Java Runtime missing") MESSAGE_DEP(GRADLEW_FOUND "Gradle Wrapper missing") endif() +message(STATUS " Build Javascript library: ${BUILD_JAVASCRIPT}") +MESSAGE_DEP(WITH_JAVASCRIPT "Disabled by WITH_JAVASCRIPT=OFF") +message(STATUS " Build NodeJS library: ${BUILD_NODEJS}") +MESSAGE_DEP(WITH_NODEJS "Disabled by WITH_NODEJS=OFF") +message(STATUS) message(STATUS " Build Python library: ${BUILD_PYTHON}") MESSAGE_DEP(WITH_PYTHON "Disabled by WITH_PYTHON=OFF") MESSAGE_DEP(PYTHONLIBS_FOUND "Python libraries missing") +message(STATUS) message(STATUS " Build Haskell library: ${BUILD_HASKELL}") MESSAGE_DEP(WITH_HASKELL "Disabled by WITH_HASKELL=OFF") MESSAGE_DEP(GHC_FOUND "GHC missing") MESSAGE_DEP(CABAL_FOUND "Cabal missing") -message(STATUS " Library features:") -message(STATUS " Build shared libraries: ${WITH_SHARED_LIB}") -message(STATUS " Build static libraries: ${WITH_STATIC_LIB}") -message(STATUS " Build with Boost static link library: ${WITH_BOOST_STATIC}") -message(STATUS " Build with Boost thread support: ${WITH_BOOSTTHREADS}") -message(STATUS " Build with boost/tr1/functional (forced) ${WITH_BOOST_FUNCTIONAL}") -message(STATUS " Build with boost/smart_ptr (forced) ${WITH_BOOST_SMART_PTR}") -message(STATUS " Build with C++ std::thread support: ${WITH_STDTHREADS}") -message(STATUS " Build with libevent support: ${WITH_LIBEVENT}") -message(STATUS " Build with OpenSSL support: ${WITH_OPENSSL}") -message(STATUS " Build with Qt4 support: ${WITH_QT4}") -message(STATUS " Build with Qt5 support: ${WITH_QT5}") -message(STATUS " Build with ZLIB support: ${WITH_ZLIB}") +message(STATUS) message(STATUS "----------------------------------------------------------") endmacro(PRINT_CONFIG_SUMMARY) diff --git a/build/cmake/DefinePlatformSpecifc.cmake b/build/cmake/DefinePlatformSpecifc.cmake index a809c0723ff..84409e6ff51 100644 --- a/build/cmake/DefinePlatformSpecifc.cmake +++ b/build/cmake/DefinePlatformSpecifc.cmake @@ -20,8 +20,41 @@ # Uncomment this to show some basic cmake variables about platforms # include (NewPlatformDebug) +# For Debug build types, default to "d"-suffix in library names. +set(CMAKE_DEBUG_POSTFIX "d" CACHE STRING "Set debug library postfix") + +# basic options +foreach(lang IN ITEMS C CXX) + if("CMAKE_${lang}_COMPILER_ID" STREQUAL "MSVC" OR "${CMAKE_${lang}_SIMULATE_ID}" STREQUAL "MSVC") + set(CMAKE_${lang}_FLAGS "${CMAKE_${lang}_FLAGS} /MP") # parallel build + set(CMAKE_${lang}_FLAGS "${CMAKE_${lang}_FLAGS} /W3") # warning level 3 + include(CheckCXXCompilerFlag) + set(CMAKE_REQUIRED_QUIET ON) + check_cxx_compiler_flag("/source-charset:utf-8" res_var) + if (res_var) + set(CMAKE_${lang}_FLAGS "${CMAKE_${lang}_FLAGS} /source-charset:utf-8") + endif() + check_cxx_compiler_flag("/execution-charset:utf-8" res_var) + if (res_var) + set(CMAKE_${lang}_FLAGS "${CMAKE_${lang}_FLAGS} /execution-charset:utf-8") + endif() + add_definitions("-DUNICODE -D_UNICODE") + elseif("CMAKE_${lang}_COMPILER_ID" STREQUAL "Clang") + set(CMAKE_${lang}_FLAGS "${CMAKE_${lang}_FLAGS} -Wall") + set(CMAKE_${lang}_FLAGS "${CMAKE_${lang}_FLAGS} -ferror-limit=1") + elseif("CMAKE_${lang}_COMPILER_ID" STREQUAL "GNU") + set(CMAKE_${lang}_FLAGS "${CMAKE_${lang}_FLAGS} -Wall -Wextra") + set(CMAKE_${lang}_FLAGS "${CMAKE_${lang}_FLAGS} -fmax-errors=1") + endif() +endforeach() + # Visual Studio specific options if(MSVC) + # Allow for shared library builds + if(BUILD_SHARED_LIBS) + set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS ON CACHE TYPE BOOL FORCE) + endif() + #For visual studio the library naming is as following: # Dynamic libraries: # - thrift.dll for release library @@ -36,11 +69,6 @@ if(MSVC) # # the same holds for other libraries like libthriftz etc. - # For Debug build types, append a "d" to the library names. - set(CMAKE_DEBUG_POSTFIX "d" CACHE STRING "Set debug library postfix" FORCE) - set(CMAKE_RELEASE_POSTFIX "" CACHE STRING "Set release library postfix" FORCE) - set(CMAKE_RELWITHDEBINFO_POSTFIX "" CACHE STRING "Set release library postfix" FORCE) - # Build using /MT option instead of /MD if the WITH_MT options is set if(WITH_MT) set(CompilerFlags @@ -56,30 +84,13 @@ if(MSVC) foreach(CompilerFlag ${CompilerFlags}) string(REPLACE "/MD" "/MT" ${CompilerFlag} "${${CompilerFlag}}") endforeach() - set(STATIC_POSTFIX "mt" CACHE STRING "Set static library postfix" FORCE) + set(THRIFT_RUNTIME_POSTFIX "mt" CACHE STRING "Set runtime library postfix" FORCE) else(WITH_MT) - set(STATIC_POSTFIX "md" CACHE STRING "Set static library postfix" FORCE) + set(THRIFT_RUNTIME_POSTFIX "md" CACHE STRING "Set runtime library postfix" FORCE) endif(WITH_MT) # Disable boost auto linking pragmas - cmake includes the right files add_definitions("-DBOOST_ALL_NO_LIB") - - # Windows build does not know how to make a shared library yet - # as there are no __declspec(dllexport) or exports files in the project. - if (WITH_SHARED_LIB) - message (FATAL_ERROR "Windows build does not support shared library output yet, please set -DWITH_SHARED_LIB=off") - endif() - - add_definitions("/MP") # parallel build - add_definitions("/W3") # warning level 3 - - # VS2010 does not provide inttypes which we need for "PRId64" used in many places - find_package(Inttypes) - if (Inttypes_FOUND) - include_directories(${INTTYPES_INCLUDE_DIRS}) - # OpenSSL conflicts with the definition of PRId64 unless it is defined first - add_definitions("/FIinttypes.h") - endif () elseif(UNIX) find_program( MEMORYCHECK_COMMAND valgrind ) set( MEMORYCHECK_COMMAND_OPTIONS "--gen-suppressions=all --leak-check=full" ) @@ -89,13 +100,6 @@ endif() add_definitions("-D__STDC_FORMAT_MACROS") add_definitions("-D__STDC_LIMIT_MACROS") -# WITH_*THREADS selects which threading library to use -if(WITH_BOOSTTHREADS) - add_definitions("-DUSE_BOOST_THREAD=1") -elseif(WITH_STDTHREADS) - add_definitions("-DUSE_STD_THREAD=1") -endif() - # C++ Language Level set(CXX_LANGUAGE_LEVEL "C++${CMAKE_CXX_STANDARD}") if (CMAKE_CXX_STANDARD_REQUIRED) @@ -105,25 +109,8 @@ else() endif() if (CMAKE_CXX_EXTENSIONS) string(CONCAT CXX_LANGUAGE_LEVEL "${CXX_LANGUAGE_LEVEL} [with compiler-specific extensions]") -else() - if ((CMAKE_CXX_COMPILER_ID MATCHES "GNU" OR CMAKE_CXX_COMPILER_ID MATCHES "Clang") AND NOT MINGW) - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-variadic-macros -Wno-long-long") - endif() - if ((CMAKE_CXX_COMPILER_ID MATCHES "Clang") AND NOT MINGW) - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-c++11-long-long") - endif() endif() if (CMAKE_CXX_COMPILER_ID MATCHES "Clang") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-deprecated-register") endif() - -# Building WITH_PLUGIN requires boost memory operations, for now, and gcc >= 4.8 -if (WITH_PLUGIN) - if (CMAKE_CXX_COMPILER_ID MATCHES "GNU" AND CMAKE_CXX_COMPILER_VERSION VERSION_LESS "4.8") - message(SEND_ERROR "Thrift compiler plug-in support is not possible with older gcc ( < 4.8 ) compiler") - endif() - message(STATUS "Forcing use of boost::smart_ptr to build WITH_PLUGIN") - add_definitions("-DFORCE_BOOST_SMART_PTR=1") -endif() - diff --git a/build/cmake/FindClangTools.cmake b/build/cmake/FindClangTools.cmake new file mode 100644 index 00000000000..b72bea765d5 --- /dev/null +++ b/build/cmake/FindClangTools.cmake @@ -0,0 +1,28 @@ +# - Try to find Clang tools +# +# The following are set after configuration is done: +# clang-tidy_FOUND +# ClangTools::clang-tidy +# clang-apply-replacements_FOUND +# ClangTools::clang-apply-replacements +# run-clang-tidy_FOUND +# ClangTools::run-clang-tidy + +include_guard() +include(FindPackageHandleStandardArgs) + +foreach(program_name IN ITEMS clang-tidy clang-apply-replacements) + find_program(${program_name}_BINARY NAMES ${program_name}-devel ${program_name}-8 ${program_name} PATH_SUFFIXES "LLVM/bin") + find_package_handle_standard_args(${program_name} DEFAULT_MSG ${program_name}_BINARY) + if(${program_name}_FOUND AND NOT TARGET ClangTools::${program_name}) + add_executable(ClangTools::${program_name} IMPORTED) + set_property(TARGET ClangTools::${program_name} PROPERTY IMPORTED_LOCATION "${${program_name}_BINARY}") + endif() +endforeach() + +find_program(run-clang-tidy_BINARY NAMES run-clang-tidy run-clang-tidy.py PATH_SUFFIXES "LLVM/bin" "llvm-devel/share/clang") +find_package_handle_standard_args(run-clang-tidy DEFAULT_MSG run-clang-tidy_BINARY) +if(run-clang-tidy_FOUND AND NOT TARGET ClangTools::run-clang-tidy) + add_executable(ClangTools::run-clang-tidy IMPORTED) + set_property(TARGET ClangTools::run-clang-tidy PROPERTY IMPORTED_LOCATION "${run-clang-tidy_BINARY}") +endif() diff --git a/build/cmake/GenerateConfigModule.cmake b/build/cmake/GenerateConfigModule.cmake new file mode 100644 index 00000000000..9533c823593 --- /dev/null +++ b/build/cmake/GenerateConfigModule.cmake @@ -0,0 +1,44 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +include(CMakePackageConfigHelpers) +set(PACKAGE_INCLUDE_INSTALL_DIR "${includedir}/thrift") +set(PACKAGE_CMAKE_INSTALL_DIR "${cmakedir}/thrift") +set(PACKAGE_BIN_INSTALL_DIR "${exec_prefix}") + +# In CYGWIN enviroment below commands does not work properly +if (NOT CYGWIN) + configure_package_config_file("${CMAKE_CURRENT_SOURCE_DIR}/build/cmake/ThriftConfig.cmake.in" + "${CMAKE_CURRENT_BINARY_DIR}/ThriftConfig.cmake" + INSTALL_DESTINATION "${CMAKE_INSTALL_DIR}/thrift" + PATH_VARS + PACKAGE_INCLUDE_INSTALL_DIR + PACKAGE_CMAKE_INSTALL_DIR + PACKAGE_BIN_INSTALL_DIR + ) + + write_basic_package_version_file("${CMAKE_CURRENT_BINARY_DIR}/ThriftConfigVersion.cmake" + VERSION ${thrift_VERSION_MAJOR}.${thrift_VERSION_MINOR}.${thrift_VERSION_PATCH} + COMPATIBILITY SameMajorVersion + ) + + install(FILES "${CMAKE_CURRENT_BINARY_DIR}/ThriftConfig.cmake" + "${CMAKE_CURRENT_BINARY_DIR}/ThriftConfigVersion.cmake" + DESTINATION "${CMAKE_INSTALL_DIR}/thrift") +endif() diff --git a/build/cmake/README-MSYS2.md b/build/cmake/README-MSYS2.md index 02679e615a7..07cad92baa3 100644 --- a/build/cmake/README-MSYS2.md +++ b/build/cmake/README-MSYS2.md @@ -43,7 +43,7 @@ Use cmake to create a MinGW makefile, out of tree (assumes you are in the top le cmake -G"MinGW Makefiles" -DCMAKE_MAKE_PROGRAM=/mingw64/bin/mingw32-make \ -DCMAKE_C_COMPILER=x86_64-w64-mingw32-gcc.exe \ -DCMAKE_CXX_COMPILER=x86_64-w64-mingw32-g++.exe \ - -DWITH_BOOSTTHREADS=ON -DWITH_LIBEVENT=OFF \ + -DWITH_LIBEVENT=OFF \ -DWITH_SHARED_LIB=OFF -DWITH_STATIC_LIB=ON \ -DWITH_JAVA=OFF -DWITH_PYTHON=OFF -DWITH_PERL=OFF \ ../thrift diff --git a/build/cmake/README.md b/build/cmake/README.md index ebc4f7da133..a0eb859bd24 100644 --- a/build/cmake/README.md +++ b/build/cmake/README.md @@ -1,6 +1,7 @@ -# Apache Thrift - CMake build +# Apache Thrift - CMake Build ## Goal + Extend Apache Thrift's *make cross* approach to the build system. Due to growing the field of operating system support, a proper executable @@ -11,12 +12,26 @@ package generation process. As nice side benefit of CMake is the generation of development environment specific soultion files. => No solution files within source tree. +## Prerequisites + +These are language-specific, however for C++ you must provide: + +- Boost +- OpenSSL + +You may optionally provide: + +- libevent +- zlib ## Usage -just do this: - mkdir cmake-build && cd cmake-build - cmake .. +To use CMake you first create an out-of-tree build directory, then use +CMake to generate a build framework, then build: + + mkdir /tmp/build + cd /tmp/build + cmake /location/to/thrift if you use a specific toolchain pass it to cmake, the same for options: @@ -25,13 +40,6 @@ if you use a specific toolchain pass it to cmake, the same for options: cmake -DTHRIFT_COMPILER_HS=OFF .. cmake -DWITH_ZLIB=ON .. -or on Windows - - cmake -G "Visual Studio 12 2013 Win64" \ - -DBOOST_ROOT=C:/3rdparty/boost_1_58_0 \ - -DZLIB_ROOT=C:/3rdparty/zlib128-dll \ - -DWITH_SHARED_LIB=off -DWITH_BOOSTTHREADS=ON .. - and open the development environment you like with the solution or do this: make @@ -39,15 +47,25 @@ and open the development environment you like with the solution or do this: make cross make dist -to generate an installer and distribution package do this: +or on Windows, the following will produce a solution file you can use +inside Visual Studio: + + cmake -G "Visual Studio 15 2017 Win64" \ + -DBOOST_ROOT=C:/3rdparty/boost_1_69_0 \ + -DBOOST_LIBRARYDIR=C:/3rdparty/boost_1_69_0/lib64-msvc-14.1^ + -DZLIB_ROOT=C:/3rdparty/zlib-1.2.11 + + ## TODO + * git hash or tag based versioning depending on source state * build tutorial * build test -* with/without language lib// * enable/disable * make cross * make dist (create an alias to make package_source) @@ -57,4 +75,4 @@ to generate an installer and distribution package do this: * libthrift * tutorial * test -* merge into /README.md +* merge into /README.md \ No newline at end of file diff --git a/build/cmake/StaticCodeAnalysis.cmake b/build/cmake/StaticCodeAnalysis.cmake new file mode 100644 index 00000000000..3356c761dc6 --- /dev/null +++ b/build/cmake/StaticCodeAnalysis.cmake @@ -0,0 +1,9 @@ +find_package(ClangTools QUIET) +if(clang-tidy_FOUND AND run-clang-tidy_FOUND AND NOT TARGET do_run_clang_tidy) + add_custom_target( + do_run_clang_tidy + COMMAND ClangTools::run-clang-tidy -clang-tidy-binary "$" -p ${CMAKE_BINARY_DIR} "-quiet" > ./run-clang-tidy.txt + DEPENDS ${CMAKE_BINARY_DIR}/compile_commands.json + WORKING_DIRECTORY ${CMAKE_BINARY_DIR} + ) +endif() diff --git a/build/travis/installCXXDependencies.sh b/build/cmake/ThriftConfig.cmake.in old mode 100755 new mode 100644 similarity index 53% rename from build/travis/installCXXDependencies.sh rename to build/cmake/ThriftConfig.cmake.in index ac3edf381aa..6bfb12c70d3 --- a/build/travis/installCXXDependencies.sh +++ b/build/cmake/ThriftConfig.cmake.in @@ -1,5 +1,4 @@ -#!/bin/sh - +# # Licensed to the Apache Software Foundation (ASF) under one # or more contributor license agreements. See the NOTICE file # distributed with this work for additional information @@ -16,14 +15,29 @@ # KIND, either express or implied. See the License for the # specific language governing permissions and limitations # under the License. +# + +set(THRIFT_VERSION @thrift_VERSION@) + +@PACKAGE_INIT@ + +set_and_check(THRIFT_INCLUDE_DIR "@PACKAGE_INCLUDE_INSTALL_DIR@") +set_and_check(THRIFT_CMAKE_DIR "@PACKAGE_CMAKE_INSTALL_DIR@") +set_and_check(THRIFT_BIN_DIR "@PACKAGE_BIN_INSTALL_DIR@") +set(THRIFT_COMPILER "${THRIFT_BIN_DIR}/thrift@CMAKE_EXECUTABLE_SUFFIX@") + +if (NOT TARGET thrift::thrift) + include("${THRIFT_CMAKE_DIR}/thriftTargets.cmake") +endif() +set(THRIFT_LIBRARIES thrift::thrift) -# Mainly aiming Travis CI's Ubuntu machines for now -# see what we need: http://thrift.apache.org/docs/install/ubuntu +if ("${THRIFT_LIBRARIES}" STREQUAL "") + message(FATAL_ERROR "thrift libraries were not found") +endif() -# General dependencies -sudo apt-add-repository "deb http://archive.ubuntu.com/ubuntu/ trusty main restricted" -y -sudo apt-get update -qq +if (NOT Thrift_FIND_QUIETLY) + message(STATUS "Found thrift: ${PACKAGE_PREFIX_DIR}") +endif() -sudo apt-get install -qq libpango-1.0-0 libqt4-dev qtbase5-dev qtbase5-dev-tools qt5-default libboost-dev libboost-test-dev libboost-program-options-dev libboost-system-dev libboost-filesystem-dev libboost-thread-dev libevent-dev automake libtool flex bison pkg-config g++ libssl-dev make cmake git debhelper bc nsis ninja-build -dpkg -S /usr/include/boost/version.hpp +check_required_components(Thrift) diff --git a/build/cmake/ThriftMacros.cmake b/build/cmake/ThriftMacros.cmake index f837f9482f4..038651eea11 100644 --- a/build/cmake/ThriftMacros.cmake +++ b/build/cmake/ThriftMacros.cmake @@ -17,89 +17,47 @@ # under the License. # - -set(CMAKE_DEBUG_POSTFIX "d" CACHE STRING "Set debug library postfix" FORCE) - +macro(ADD_PKGCONFIG_THRIFT name) + configure_file("${name}.pc.in" "${CMAKE_CURRENT_BINARY_DIR}/${name}.pc" @ONLY) + install(FILES "${CMAKE_CURRENT_BINARY_DIR}/${name}.pc" + DESTINATION "${PKGCONFIG_INSTALL_DIR}") +endmacro(ADD_PKGCONFIG_THRIFT) macro(ADD_LIBRARY_THRIFT name) - -if(WITH_SHARED_LIB) - add_library(${name} SHARED ${ARGN}) + add_library(${name} ${ARGN}) + target_include_directories(${name} INTERFACE $) set_target_properties(${name} PROPERTIES - OUTPUT_NAME ${name} - VERSION ${thrift_VERSION} - SOVERSION ${thrift_VERSION} ) - #set_target_properties(${name} PROPERTIES PUBLIC_HEADER "${thriftcpp_HEADERS}") - install(TARGETS ${name} + OUTPUT_NAME ${name}${THRIFT_RUNTIME_POSTFIX} # windows link variants (/MT, /MD, /MTd, /MDd) get different names + VERSION ${thrift_VERSION} ) + # set_target_properties(${name} PROPERTIES PUBLIC_HEADER "${thriftcpp_HEADERS}") + install(TARGETS ${name} EXPORT "${name}Targets" RUNTIME DESTINATION "${BIN_INSTALL_DIR}" LIBRARY DESTINATION "${LIB_INSTALL_DIR}" ARCHIVE DESTINATION "${LIB_INSTALL_DIR}" PUBLIC_HEADER DESTINATION "${INCLUDE_INSTALL_DIR}") -endif() -if(WITH_STATIC_LIB) - add_library(${name}_static STATIC ${ARGN}) - set_target_properties(${name}_static PROPERTIES - OUTPUT_NAME ${name}${STATIC_POSTFIX} - VERSION ${thrift_VERSION} - SOVERSION ${thrift_VERSION} ) - install(TARGETS ${name}_static - RUNTIME DESTINATION "${BIN_INSTALL_DIR}" - LIBRARY DESTINATION "${LIB_INSTALL_DIR}" - ARCHIVE DESTINATION "${LIB_INSTALL_DIR}" - PUBLIC_HEADER DESTINATION "${INCLUDE_INSTALL_DIR}") -endif() - -endmacro(ADD_LIBRARY_THRIFT) + export(EXPORT "${name}Targets" + FILE "${CMAKE_CURRENT_BINARY_DIR}/${name}/${name}Targets.cmake" + NAMESPACE "${name}::") + install(EXPORT "${name}Targets" + FILE "${name}Targets.cmake" + NAMESPACE "${name}::" + DESTINATION "${CMAKE_INSTALL_DIR}/thrift") +endmacro() macro(TARGET_INCLUDE_DIRECTORIES_THRIFT name) - -if(WITH_SHARED_LIB) target_include_directories(${name} ${ARGN}) -endif() - -if(WITH_STATIC_LIB) - target_include_directories(${name}_static ${ARGN}) -endif() - -endmacro(TARGET_INCLUDE_DIRECTORIES_THRIFT) - +endmacro() macro(TARGET_LINK_LIBRARIES_THRIFT name) - -if(WITH_SHARED_LIB) target_link_libraries(${name} ${ARGN}) -endif() - -if(WITH_STATIC_LIB) - target_link_libraries(${name}_static ${ARGN}) -endif() - -endmacro(TARGET_LINK_LIBRARIES_THRIFT) - +endmacro() macro(LINK_AGAINST_THRIFT_LIBRARY target libname) - -if (WITH_SHARED_LIB) target_link_libraries(${target} ${libname}) -elseif (WITH_STATIC_LIB) - target_link_libraries(${target} ${libname}_static) -else() - message(FATAL "Not linking with shared or static libraries?") -endif() - -endmacro(LINK_AGAINST_THRIFT_LIBRARY) - +endmacro() macro(TARGET_LINK_LIBRARIES_THRIFT_AGAINST_THRIFT_LIBRARY target libname) - -if(WITH_SHARED_LIB) target_link_libraries(${target} ${ARGN} ${libname}) -endif() - -if(WITH_STATIC_LIB) - target_link_libraries(${target}_static ${ARGN} ${libname}_static) -endif() - -endmacro(TARGET_LINK_LIBRARIES_THRIFT_AGAINST_THRIFT_LIBRARY) +endmacro() diff --git a/build/cmake/config.h.in b/build/cmake/config.h.in index 39d8270aa3c..a3d66428cb9 100644 --- a/build/cmake/config.h.in +++ b/build/cmake/config.h.in @@ -64,12 +64,6 @@ see: aclocal/ac_prog_bison.m4 */ #cmakedefine BISON_USE_PARSER_H_EXTENSION 1 -/* replaces POSIX pthread by boost::thread */ -#cmakedefine USE_BOOST_THREAD 1 - -/* replaces POSIX pthread by std::thread */ -#cmakedefine USE_STD_THREAD 1 - /* Define to 1 if strerror_r returns char *. */ #cmakedefine STRERROR_R_CHAR_P 1 diff --git a/build/cmake/uninstall.cmake b/build/cmake/uninstall.cmake new file mode 100644 index 00000000000..3842a83df55 --- /dev/null +++ b/build/cmake/uninstall.cmake @@ -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. +# + +cmake_minimum_required(VERSION 3.4) + +set(MANIFEST "${CMAKE_CURRENT_BINARY_DIR}/install_manifest.txt") + +if(NOT EXISTS ${MANIFEST}) + message(FATAL_ERROR "Cannot find install mainfest: ${MANIFEST}") +endif() + +file(STRINGS ${MANIFEST} files) +foreach(file ${files}) + if(EXISTS ${file} OR IS_SYMLINK ${file}) + message(STATUS "Removing: ${file}") + + execute_process( + COMMAND ${CMAKE_COMMAND} -E remove ${file} + RESULT_VARIABLE result + OUTPUT_QUIET + ERROR_VARIABLE stderr + ERROR_STRIP_TRAILING_WHITESPACE + ) + + if(NOT ${result} EQUAL 0) + message(FATAL_ERROR "${stderr}") + endif() + else() + message(STATUS "Does-not-exist: ${file}") + endif() +endforeach(file) diff --git a/build/docker/README.md b/build/docker/README.md index d0a83886f88..08023a7b267 100644 --- a/build/docker/README.md +++ b/build/docker/README.md @@ -109,12 +109,12 @@ new one for you: To run all unit tests (just like Travis CI does): - thrift$ dockerrun ubuntu-bionic + thrift$ dockerrun thrift/thrift-build:ubuntu-bionic root@8caf56b0ce7b:/thrift/src# build/docker/scripts/autotools.sh To run the cross tests (just like Travis CI does): - thrift$ dockerrun ubuntu-bionic + thrift$ dockerrun thrift/thrift-build:ubuntu-bionic root@8caf56b0ce7b:/thrift/src# build/docker/scripts/cross-test.sh When you are done, you want to clean up occasionally so that docker isn't using lots of extra disk space: @@ -167,32 +167,31 @@ Last updated: October 1, 2017 | Language | ubuntu-xenial | ubuntu-bionic | Notes | | :-------- | :------------ | :------------ | :---- | -| as of | Mar 06, 2018 | Jul 6, 2018 | | -| as3 | | | Not in CI | -| C++ gcc | 5.4.0 | 7.3.0 | | +| as of | Mar 06, 2018 | Jul 1, 2019 | | +| as3 | | 4.6.0 | | +| C++ gcc | 5.4.0 | 7.4.0 | | | C++ clang | 3.8 | 6.0 | | | C# (mono) | 4.2.1.0 | 4.6.2.7 | | -| c_glib | 2.48.2 | 2.56.0 | | -| cl (sbcl) | | 1.4.9 | | -| cocoa | | | Not in CI | -| d | 2.075.1 | 2.081.0 | | -| dart | 1.22.1 | 1.24.3 | | +| c_glib | 2.48.2 | 2.56.4 | | +| cl (sbcl) | | 1.5.3 | | +| d | 2.075.1 | 2.087.0 | | +| dart | 2.0.0 | 2.4.0 | | | delphi | | | Not in CI | -| dotnet | 2.1.4 | 2.1.301 | | -| erlang | 18.3 | 20.2.2 | | -| go | 1.7.6 | 1.10.3 | | +| erlang | 18.3 | 22.0 | | +| go | 1.14.14 | 1.15.7 | | | haskell | 7.10.3 | 8.0.2 | | | haxe | 3.2.1 | 3.4.4 | THRIFT-4352: avoid 3.4.2 | -| java | 1.8.0_151 | 1.8.0_171 | | -| js | | | Unsure how to look for version info? | -| lua | 5.2.4 | 5.2.4 | Lua 5.3: see THRIFT-4386 | -| nodejs | 6.13.0 | 8.11.3 | | +| java | 1.8.0_191 | 11.0.3 | | +| js | Node.js 6.17.1, V8 5.1.281.111, npm 3.10.10 | Node.js 10.18.0, V8 6.8.275.32, npm 6.13.4 | | +| lua | | 5.2.4 | Lua 5.3: see THRIFT-4386 | +| netstd | 3.1 | 3.1 | LTS version | +| nodejs | 6.16.0 | 10.16.0 | | | ocaml | | 4.05.0 | THRIFT-4517: ocaml 4.02.3 on xenial appears broken | | perl | 5.22.1 | 5.26.1 | | -| php | 7.0.22 | 7.2.5 | | -| python | 2.7.12 | 2.7.15rc1 | | -| python3 | 3.5.2 | 3.6.5 | | +| php | 7.0.32 | 7.2.19 | | +| python | 2.7.12 | 2.7.15 | | +| python3 | 3.5.2 | 3.6.8 | | | ruby | 2.3.1p112 | 2.5.1p57 | | -| rust | 1.17.0 | 1.24.1 | | +| rust | 1.40.0 | 1.40.0 | | | smalltalk | | | Not in CI | -| swift | | | Not in CI | +| swift | | 5.1.4 | | diff --git a/build/docker/msvc2017/Dockerfile b/build/docker/msvc2017/Dockerfile new file mode 100644 index 00000000000..a2b3cd7e221 --- /dev/null +++ b/build/docker/msvc2017/Dockerfile @@ -0,0 +1,103 @@ +# escape=` +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +FROM microsoft/dotnet-framework:4.7.1 + +# Restore the default Windows shell for correct batch processing below. +SHELL ["cmd", "/S", "/C"] + +# Install Build Tools excluding workloads and components with known issues. +ADD https://aka.ms/vs/15/release/vs_buildtools.exe C:\TEMP\vs_buildtools.exe +RUN C:\TEMP\vs_buildtools.exe --quiet --wait --norestart --nocache ` + --installPath C:\BuildTools ` + --all ` + --remove Microsoft.VisualStudio.Component.Windows10SDK.10240 ` + --remove Microsoft.VisualStudio.Component.Windows10SDK.10586 ` + --remove Microsoft.VisualStudio.Component.Windows10SDK.14393 ` + --remove Microsoft.VisualStudio.Component.Windows81SDK ` + || IF "%ERRORLEVEL%"=="3010" EXIT 0 +RUN DEL C:\TEMP\vs_buildtools.exe + +# Install CMake +ADD https://github.com/Kitware/CMake/releases/download/v3.13.4/cmake-3.13.4-win64-x64.msi C:\TEMP\cmake.msi +RUN msiexec.exe /i C:\TEMP\cmake.msi /qn && ` + SETX PATH "%PATH%;C:\Program Files\CMake\bin" && ` + DEL C:\TEMP\cmake.msi + +# Install boost (for the thrift runtime library build) +ADD https://boost.teeks99.com/bin/1.69.0/boost_1_69_0-msvc-14.1-64.exe C:\TEMP\boost.exe +RUN C:\TEMP\boost.exe /DIR="C:\Libraries\boost_1_69_0" /SILENT && ` + DEL C:\TEMP\boost.exe + +# Install chocolatey +RUN @"%SystemRoot%\System32\WindowsPowerShell\v1.0\powershell.exe" ` + -NoProfile -InputFormat None -ExecutionPolicy Bypass -Command ` + "iex ((New-Object System.Net.WebClient).DownloadString('https://chocolatey.org/install.ps1'))" ` + && SETX PATH "%PATH%;%ALLUSERSPROFILE%\chocolatey\bin" + +# Install winflexbison (for the thrift compiler build) +RUN choco install winflexbison3 -y + +# Install 7zip and curl (used by the libevent and zlib build scripts) +RUN choco install 7zip curl -y + +# Install libevent +COPY appveyor\build-libevent.bat C:\TEMP\build-libevent.bat +ENV LIBEVENT_VERSION=2.1.8 +ENV WIN3P=C:\TEMP\WIN3P +RUN C:\BuildTools\Common7\Tools\VsDevCmd.bat -arch=amd64 && ` + MKDIR C:\TEMP\WIN3P && ` + C:\TEMP\build-libevent.bat && ` + MKDIR C:\Libraries\libevent-%LIBEVENT_VERSION% && ` + MOVE C:\TEMP\WIN3P\libevent-%LIBEVENT_VERSION%-stable\include C:\Libraries\libevent-%LIBEVENT_VERSION% && ` + MOVE C:\TEMP\WIN3P\libevent-%LIBEVENT_VERSION%-stable\lib C:\Libraries\libevent-%LIBEVENT_VERSION% && ` + RMDIR /S /Q C:\TEMP\WIN3P + +# Install zlib +COPY appveyor\build-zlib.bat C:\TEMP\build-zlib.bat +ENV ZLIB_VERSION=1.2.11 +ENV WIN3P=C:\TEMP\WIN3P +RUN C:\BuildTools\Common7\Tools\VsDevCmd.bat -arch=amd64 && ` + MKDIR C:\TEMP\WIN3P && ` + C:\TEMP\build-zlib.bat && ` + MOVE C:\TEMP\WIN3P\zlib-inst C:\Libraries\zlib-%ZLIB_VERSION% && ` + RMDIR /S /Q C:\TEMP\WIN3P + +# Install OpenSSL 1.1.0 +ADD http://slproweb.com/download/Win64OpenSSL-1_1_0j.exe C:\TEMP\openssl.exe +RUN C:\TEMP\openssl.exe /silent && ` + DEL C:\TEMP\openssl.exe + +# Install java +RUN choco install jdk8 -y + +# Install haskell +RUN choco install ghc -y + +# Install python3 +RUN choco install python3 -y + +# Install Adobe Flex 4.6 SDK and set FLEX_HOME so it can be found +ADD http://download.macromedia.com/pub/flex/sdk/flex_sdk_4.6.zip C:\Adobe\Flex\SDK\4.6\SDK.zip +RUN CD C:\Adobe\Flex\SDK\4.6 && ` + 7z x SDK.zip && ` + DEL SDK.zip && ` + SETX FLEX_HOME "C:\Adobe\Flex\SDK\4.6" + +# Start developer command prompt with any other commands specified. +ENTRYPOINT C:\BuildTools\Common7\Tools\VsDevCmd.bat -arch=amd64 && + +# Default to PowerShell if no other command specified. +CMD ["powershell.exe", "-NoLogo", "-ExecutionPolicy", "Bypass"] diff --git a/build/docker/msvc2017/README.md b/build/docker/msvc2017/README.md new file mode 100644 index 00000000000..0c882d74758 --- /dev/null +++ b/build/docker/msvc2017/README.md @@ -0,0 +1,50 @@ +# Building Thrift using Docker for Windows + +The build image is very large (just under 30GB) so plan accordingly. +Once Microsoft supports build tools in nano, it should get better. + +Install Docker for Windows and switch to Windows container mode. + +Pull from docker hub: + + PS C:\> docker pull thrift/thrift-build:msvc2017 + +or build in a docker for windows environment: + + PS C:\Thrift> docker build -t thrift/thrift-build:msvc2017 -f build\docker\msvc2017\Dockerfile build\ + +The following directories are used inside the container: + + C:\Build the out-of-tree build directory + C:\Install the install target directory + C:\Thrift the source tree + +You can override these as docker volumes if desired. + +### Compiler + +To build a portable windows thrift compiler (with a statically linked +runtime) and get it placed into C:\install: + + docker run -v C:\thrift:C:\thrift^ + -v C:\install:C:\install^ + --rm -t thrift/thrift-build:msvc2017^ + C:\thrift\build\docker\msvc2017\build-compiler.bat + +The end result is a portable windows thrift compiler located at + + C:\Install\bin\thrift.exe + +If you run it through the [Dependency Walker](http://www.dependencywalker.com/) +you will see it only depends on KERNEL32.DLL which means the runtime is statically +linked, so the executable is portable and self-contained. This is how the +windows thrift compiler is built for each Apache Thrift release. + +### Libraries + +To build, test everything and get the C++ SDK placed into C:\install: + + docker run -v C:\thrift:C:\thrift^ + -v C:\install:C:\install^ + -m 4096 --rm -t thrift/thrift-build:msvc2017^ + C:\thrift\build\docker\msvc2017\build.bat \ No newline at end of file diff --git a/build/docker/msvc2017/build-compiler.bat b/build/docker/msvc2017/build-compiler.bat new file mode 100644 index 00000000000..5534428b446 --- /dev/null +++ b/build/docker/msvc2017/build-compiler.bat @@ -0,0 +1,44 @@ +:: +:: Licensed under the Apache License, Version 2.0 (the "License"); +:: you may not use this file except in compliance with the License. +:: You may obtain a copy of the License at +:: +:: http://www.apache.org/licenses/LICENSE-2.0 +:: +:: Unless required by applicable law or agreed to in writing, software +:: distributed under the License is distributed on an "AS IS" BASIS, +:: WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +:: See the License for the specific language governing permissions and +:: limitations under the License. +:: + +:: +:: Build script example for inside the windows docker container +:: +:: C:\build is the out-of-tree build directory +:: C:\install is the location where artifacts are placed +:: C:\thrift is where the sources are +:: + +:: Make and go into the out-of-tree directory +IF NOT EXIST C:\build (MKDIR C:\build) +cd c:\build + +:: Generate the out-of-tree build files +cmake^ + -DBOOST_ROOT=C:\Libraries\boost_1_69_0^ + -DBOOST_LIBRARYDIR=C:\Libraries\boost_1_69_0\lib64-msvc-14.1^ + -DBUILD_LIBRARIES=OFF^ + -DCMAKE_BUILD_TYPE=Release^ + -DCMAKE_INSTALL_PREFIX=C:\install^ + -DWITH_MT=ON^ + c:\thrift || EXIT /B + +:: Build +cmake --build . --target thrift-compiler --config Release || EXIT /B + +:: Test +cmake --build . --target check || EXIT /B + +:: Install +cmake --build . --target install \ No newline at end of file diff --git a/build/docker/msvc2017/build.bat b/build/docker/msvc2017/build.bat new file mode 100644 index 00000000000..018805bca69 --- /dev/null +++ b/build/docker/msvc2017/build.bat @@ -0,0 +1,45 @@ +:: +:: Licensed under the Apache License, Version 2.0 (the "License"); +:: you may not use this file except in compliance with the License. +:: You may obtain a copy of the License at +:: +:: http://www.apache.org/licenses/LICENSE-2.0 +:: +:: Unless required by applicable law or agreed to in writing, software +:: distributed under the License is distributed on an "AS IS" BASIS, +:: WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +:: See the License for the specific language governing permissions and +:: limitations under the License. +:: + +:: +:: Build script example for inside the windows docker container +:: +:: C:\build is the out-of-tree build directory +:: C:\install is the location where artifacts are placed +:: C:\thrift is where the sources are +:: + +:: Make and go into the out-of-tree directory +IF NOT EXIST C:\build (MKDIR C:\build) +cd c:\build + +:: Generate the out-of-tree build files +cmake^ + -DBOOST_ROOT=C:\Libraries\boost_1_69_0^ + -DBOOST_LIBRARYDIR=C:\Libraries\boost_1_69_0\lib64-msvc-14.1^ + -DFLEX_HOME=C:\Adobe\Flex\SDK\4.6^ + -DLIBEVENT_ROOT=C:\Libraries\libevent-2.1.8^ + -DZLIB_ROOT=C:\Libraries\zlib-1.2.11^ + -DCMAKE_BUILD_TYPE=Release^ + -DCMAKE_INSTALL_PREFIX=C:\install^ + c:\thrift || EXIT /B + +:: Build +cmake --build . --config Release || EXIT /B + +:: Test +cmake --build . --target check || EXIT /B + +:: Install +cmake --build . --target install diff --git a/build/docker/old/debian-jessie/Dockerfile b/build/docker/old/debian-jessie/Dockerfile index 7bc74fc24eb..a49b2078c5d 100644 --- a/build/docker/old/debian-jessie/Dockerfile +++ b/build/docker/old/debian-jessie/Dockerfile @@ -79,6 +79,7 @@ RUN apt-get install -y --no-install-recommends \ RUN apt-get install -y --no-install-recommends \ `# Ruby dependencies` \ ruby \ + ruby-bundler \ ruby-dev \ `# Perl dependencies` \ libbit-vector-perl \ @@ -123,11 +124,6 @@ RUN apt-get update && apt-get install -y --no-install-recommends \ nodejs-legacy \ npm -RUN apt-get update && apt-get install -y --no-install-recommends \ -`# CSharp dependencies` \ - libmono-system-web2.0-cil \ - mono-devel - RUN apt-get update && apt-get install -y --no-install-recommends \ `# D dependencies` \ xdg-utils \ diff --git a/build/docker/old/debian-stretch/Dockerfile b/build/docker/old/debian-stretch/Dockerfile index 503eecd4240..48db7e19f2b 100644 --- a/build/docker/old/debian-stretch/Dockerfile +++ b/build/docker/old/debian-stretch/Dockerfile @@ -76,10 +76,6 @@ RUN apt-get install -y --no-install-recommends \ qtbase5-dev \ qtbase5-dev-tools -RUN apt-get install -y --no-install-recommends \ -`# csharp (mono) dependencies` \ - mono-devel - RUN apt-get install -y --no-install-recommends \ `# D dependencies` \ dmd-bin \ diff --git a/build/docker/ubuntu-artful/Dockerfile b/build/docker/old/ubuntu-artful/Dockerfile similarity index 95% rename from build/docker/ubuntu-artful/Dockerfile rename to build/docker/old/ubuntu-artful/Dockerfile index abe84d11c61..813ef06d71f 100644 --- a/build/docker/ubuntu-artful/Dockerfile +++ b/build/docker/old/ubuntu-artful/Dockerfile @@ -39,10 +39,6 @@ RUN apt-get update && \ software-properties-common \ wget -# csharp (mono) - if we ever want a later version -# RUN echo "deb http://download.mono-project.com/repo/debian xenial main" | tee /etc/apt/sources.list.d/mono.list && \ -# apt-key adv --keyserver keyserver.ubuntu.com --recv-keys A6A19B38D3D831EF - # Dart RUN curl https://dl-ssl.google.com/linux/linux_signing_key.pub | apt-key add - && \ curl https://storage.googleapis.com/download.dartlang.org/linux/debian/dart_stable.list > \ @@ -89,10 +85,6 @@ RUN apt-get install -y --no-install-recommends \ qtbase5-dev \ qtbase5-dev-tools -RUN apt-get install -y --no-install-recommends \ -`# csharp (mono) dependencies` \ - mono-devel - ENV SBCL_VERSION 1.4.5 RUN \ `# Common Lisp (sbcl) dependencies` \ diff --git a/build/docker/old/ubuntu-trusty/Dockerfile b/build/docker/old/ubuntu-trusty/Dockerfile index a8e4d3baa25..96c1540197e 100644 --- a/build/docker/old/ubuntu-trusty/Dockerfile +++ b/build/docker/old/ubuntu-trusty/Dockerfile @@ -74,10 +74,6 @@ RUN apt-get install -y --no-install-recommends \ qtbase5-dev \ qtbase5-dev-tools -RUN apt-get install -y --no-install-recommends \ -`# csharp (mono) dependencies` \ - mono-devel - RUN apt-get install -y --no-install-recommends \ `# D dependencies` \ dmd-bin=2.070.2-0 \ diff --git a/build/docker/scripts/autotools.sh b/build/docker/scripts/autotools.sh index 8388f728ce2..52b17eee890 100755 --- a/build/docker/scripts/autotools.sh +++ b/build/docker/scripts/autotools.sh @@ -1,6 +1,19 @@ #!/bin/sh set -ev +mkdir ~/.m2 +tee >~/.m2/settings.xml < + + + secure-central + https://repo.maven.apache.org/maven2 + central + + + +EOF + ./bootstrap.sh ./configure $* make check -j3 diff --git a/build/docker/scripts/cmake.sh b/build/docker/scripts/cmake.sh index ccc311e44c0..eb384d55224 100755 --- a/build/docker/scripts/cmake.sh +++ b/build/docker/scripts/cmake.sh @@ -19,5 +19,4 @@ for LIB in $BUILD_LIBS; do done $MAKEPROG -j3 cpack -ctest -VV -E "(python_test)" -# disabled cmake python_test for now since it fails in travis under centos +ctest -VV diff --git a/build/docker/scripts/coverity.sh b/build/docker/scripts/coverity.sh new file mode 100755 index 00000000000..ecc7a282e75 --- /dev/null +++ b/build/docker/scripts/coverity.sh @@ -0,0 +1,40 @@ +#! /bin/bash +# +# This script allows you to run coverity on the project and submit the +# results. Do this inside the docker build container. Only works if +# you are a coverity scan thrift project admin with access to the +# necessary security token. +# +# Environment Variables +# +# COVERITY_SCAN_NOTIFICATION_EMAIL - email address to notify +# COVERITY_SCAN_TOKEN - the Coverity Scan token (should be secure) +# VERSION - the version to report we scanned + +set -ex + +wget -nv https://entrust.com/root-certificates/entrust_l1k.cer -O /tmp/scanca.cer + +pushd /tmp +if [[ "$1" != "--skipdownload" ]]; then + rm -rf coverity_tool.tgz cov-analysis* + wget -nv -O coverity_tool.tgz https://scan.coverity.com/download/cxx/linux64 --post-data "project=thrift&token=$COVERITY_SCAN_TOKEN" + tar xzf coverity_tool.tgz +fi +COVBIN=$(echo $(pwd)/cov-analysis*/bin) +export PATH=$COVBIN:$PATH +popd + +./bootstrap.sh +./configure $* +rm -rf cov-int/ +cov-build --dir cov-int make check -j3 +tail -50 cov-int/build-log.txt +tar cJf cov-int.tar.xz cov-int/ +curl --cacert /tmp/scanca.cer \ + --form token="$COVERITY_SCAN_TOKEN" \ + --form email="$COVERITY_SCAN_NOTIFICATION_EMAIL" \ + --form file=@cov-int.tar.xz \ + --form version="$VERSION" \ + --form description="thrift master" \ + https://scan.coverity.com/builds?project=thrift diff --git a/build/docker/scripts/cross-test.sh b/build/docker/scripts/cross-test.sh index 43581a5f3b5..0b6748ab254 100755 --- a/build/docker/scripts/cross-test.sh +++ b/build/docker/scripts/cross-test.sh @@ -10,7 +10,12 @@ make cross$1 RET=$? if [ $RET -ne 0 ]; then - cat test/log/unexpected_failures.log + if [ -f "test/features/log/unexpected_failures.log" ]; then + cat "test/features/log/unexpected_failures.log" + fi + if [ -f "test/log/unexpected_failures.log" ]; then + cat "test/log/unexpected_failures.log" + fi fi exit $RET diff --git a/build/docker/scripts/sca.sh b/build/docker/scripts/sca.sh index 16d5826198d..42128fc671e 100755 --- a/build/docker/scripts/sca.sh +++ b/build/docker/scripts/sca.sh @@ -39,15 +39,7 @@ cppcheck --force --quiet --inline-suppr --error-exitcode=1 -j2 lib/cpp/src lib/c cppcheck --force --quiet --inline-suppr --error-exitcode=1 -j2 lib/c_glib/src lib/c_glib/test test/c_glib/src tutorial/c_glib # Python code style -flake8 --ignore=W504,E501 lib/py -flake8 --exclude=tutorial/py/build tutorial/py -# THRIFT-4371 : generated files are excluded because they haven't been scrubbed yet -flake8 --ignore=E501 --exclude="*/gen-py*/*",test/py/build test/py -flake8 test/py.twisted -flake8 test/py.tornado -flake8 --ignore=E501 test/test.py -flake8 --ignore=E501,E722 test/crossrunner -flake8 test/features +flake8 # PHP code style composer install --quiet diff --git a/build/docker/ubuntu-bionic/Dockerfile b/build/docker/ubuntu-bionic/Dockerfile index a8c1417ac9f..c8ecd8ea9d3 100644 --- a/build/docker/ubuntu-bionic/Dockerfile +++ b/build/docker/ubuntu-bionic/Dockerfile @@ -12,13 +12,7 @@ # # Apache Thrift Docker build environment for Ubuntu Bionic -# Using all stock Ubuntu Bionic packaging except for: -# - cl: want latest -# - d: dmd does not come with Ubuntu -# - dart: does not come with Ubuntu. Pinned to last 1.x release -# - dotnet: does not come with Ubuntu -# - go: want latest -# - nodejs: want v8, bionic comes with v6 +# with some updated packages. # FROM buildpack-deps:bionic-scm @@ -29,7 +23,7 @@ ENV DEBIAN_FRONTEND noninteractive RUN apt-get update && \ apt-get dist-upgrade -y && \ - apt-get install -y --no-install-recommends \ + apt-get install -y --no-install-recommends --fix-missing \ apt \ apt-transport-https \ apt-utils \ @@ -38,15 +32,10 @@ RUN apt-get update && \ software-properties-common \ wget -# csharp (mono) - if we ever want a later version -# RUN echo "deb http://download.mono-project.com/repo/debian xenial main" | tee /etc/apt/sources.list.d/mono.list && \ -# apt-key adv --keyserver keyserver.ubuntu.com --recv-keys A6A19B38D3D831EF - # Dart RUN curl https://dl-ssl.google.com/linux/linux_signing_key.pub | apt-key add - && \ curl https://storage.googleapis.com/download.dartlang.org/linux/debian/dart_stable.list > \ /etc/apt/sources.list.d/dart_stable.list -ENV DART_VERSION 1.24.3-1 # dotnet (netcore) RUN curl https://packages.microsoft.com/keys/microsoft.asc | gpg --dearmor > /etc/apt/trusted.gpg.d/microsoft.gpg && \ @@ -54,9 +43,13 @@ RUN curl https://packages.microsoft.com/keys/microsoft.asc | gpg --dearmor > /et chown root:root /etc/apt/trusted.gpg.d/microsoft.gpg && \ chown root:root /etc/apt/sources.list.d/microsoft-prod.list +# erlang +RUN wget -O- https://packages.erlang-solutions.com/ubuntu/erlang_solutions.asc | apt-key add - && \ + echo "deb https://packages.erlang-solutions.com/ubuntu bionic contrib" | tee /etc/apt/sources.list.d/erlang.list + # node.js RUN curl -sL https://deb.nodesource.com/gpgkey/nodesource.gpg.key | apt-key add - && \ - echo "deb https://deb.nodesource.com/node_8.x bionic main" | tee /etc/apt/sources.list.d/nodesource.list + echo "deb https://deb.nodesource.com/node_10.x bionic main" | tee /etc/apt/sources.list.d/nodesource.list ### install general dependencies RUN apt-get update && apt-get install -y --no-install-recommends \ @@ -69,13 +62,24 @@ RUN apt-get update && apt-get install -y --no-install-recommends \ debhelper \ flex \ gdb \ + libasound2 \ + libatk-bridge2.0-0 \ + libgtk-3-0 \ llvm \ ninja-build \ pkg-config \ + unzip \ valgrind \ vim ENV PATH /usr/lib/llvm-6.0/bin:$PATH +# lib/as3 (ActionScript) +RUN mkdir -p /usr/local/adobe/flex/4.6 && \ + cd /usr/local/adobe/flex/4.6 && \ + wget -q "http://download.macromedia.com/pub/flex/sdk/flex_sdk_4.6.zip" && \ + unzip flex_sdk_4.6.zip +ENV FLEX_HOME /usr/local/adobe/flex/4.6 + RUN apt-get install -y --no-install-recommends \ `# C++ dependencies` \ libboost-all-dev \ @@ -85,15 +89,11 @@ RUN apt-get install -y --no-install-recommends \ qtbase5-dev \ qtbase5-dev-tools -RUN apt-get install -y --no-install-recommends \ -`# csharp (mono) dependencies` \ - mono-devel - -ENV SBCL_VERSION 1.4.12 +ENV SBCL_VERSION 1.5.3 RUN \ `# Common Lisp (sbcl) dependencies` \ curl --version && \ - curl -O -J -L https://kent.dl.sourceforge.net/project/sbcl/sbcl/${SBCL_VERSION}/sbcl-${SBCL_VERSION}-x86-64-linux-binary.tar.bz2 && \ + curl -o sbcl-${SBCL_VERSION}-x86-64-linux-binary.tar.bz2 -J -L https://sourceforge.net/projects/sbcl/files/sbcl/${SBCL_VERSION}/sbcl-${SBCL_VERSION}-x86-64-linux-binary.tar.bz2/download?use_mirror=managedway# && \ tar xjf sbcl-${SBCL_VERSION}-x86-64-linux-binary.tar.bz2 && \ cd sbcl-${SBCL_VERSION}-x86-64-linux && \ ./install.sh && \ @@ -101,23 +101,24 @@ RUN \ cd .. && \ rm -rf sbcl* -ENV D_VERSION 2.082.1 -ENV DMD_DEB dmd_2.082.1-0_amd64.deb +ENV D_VERSION 2.087.0 +ENV DMD_DEB dmd_2.087.0-0_amd64.deb RUN \ `# D dependencies` \ wget -q http://downloads.dlang.org/releases/2.x/${D_VERSION}/${DMD_DEB} && \ dpkg --install ${DMD_DEB} && \ rm -f ${DMD_DEB} && \ mkdir -p /usr/include/dmd/druntime/import/deimos /usr/include/dmd/druntime/import/C && \ - curl -sSL https://github.com/D-Programming-Deimos/libevent/archive/master.tar.gz| tar xz && \ - mv libevent-master/deimos/* /usr/include/dmd/druntime/import/deimos/ && \ - mv libevent-master/C/* /usr/include/dmd/druntime/import/C/ && \ - rm -rf libevent-master && \ - curl -sSL https://github.com/jeking3/openssl/archive/tls_method.tar.gz| tar xz && \ - mv openssl-tls_method/deimos/* /usr/include/dmd/druntime/import/deimos/ && \ - mv openssl-tls_method/C/* /usr/include/dmd/druntime/import/C/ && \ - rm -rf openssl-tls_method - + git clone -b 'v2.0.2+2.0.16' https://github.com/D-Programming-Deimos/libevent.git deimos-libevent-2.0 && \ + mv deimos-libevent-2.0/deimos/* /usr/include/dmd/druntime/import/deimos/ && \ + mv deimos-libevent-2.0/C/* /usr/include/dmd/druntime/import/C/ && \ + rm -rf deimos-libevent-2.0 && \ + git clone -b 'v2.0.0+1.1.0h' https://github.com/D-Programming-Deimos/openssl.git deimos-openssl-1.1.0h && \ + mv deimos-openssl-1.1.0h/deimos/* /usr/include/dmd/druntime/import/deimos/ && \ + mv deimos-openssl-1.1.0h/C/* /usr/include/dmd/druntime/import/C/ && \ + rm -rf deimos-openssl-1.1.0h + +ENV DART_VERSION 2.4.0-1 RUN apt-get install -y --no-install-recommends \ `# Dart dependencies` \ dart=$DART_VERSION @@ -125,24 +126,23 @@ ENV PATH /usr/lib/dart/bin:$PATH RUN apt-get install -y --no-install-recommends \ `# dotnet core dependencies` \ - dotnet-sdk-2.1 + dotnet-sdk-3.1 RUN apt-get install -y --no-install-recommends \ `# Erlang dependencies` \ - erlang-base \ - erlang-eunit \ - erlang-dev \ - erlang-tools \ - rebar + erlang && \ + wget https://s3.amazonaws.com/rebar3/rebar3 -O /usr/bin/rebar3 && \ + chmod 755 /usr/bin/rebar3 && \ + rebar3 --version RUN apt-get install -y --no-install-recommends \ `# GlibC dependencies` \ libglib2.0-dev # golang -ENV GOLANG_VERSION 1.11.1 +ENV GOLANG_VERSION 1.15.7 ENV GOLANG_DOWNLOAD_URL https://golang.org/dl/go$GOLANG_VERSION.linux-amd64.tar.gz -ENV GOLANG_DOWNLOAD_SHA256 2871270d8ff0c8c69f161aaae42f9f28739855ff5c5204752a8d92a1c9f63993 +ENV GOLANG_DOWNLOAD_SHA256 0d142143794721bb63ce6c8a6180c4062bcf8ef4715e7d6d6609f3a8282629b3 RUN curl -fsSL "$GOLANG_DOWNLOAD_URL" -o golang.tar.gz && \ echo "$GOLANG_DOWNLOAD_SHA256 golang.tar.gz" | sha256sum -c - && \ tar -C /usr/local -xzf golang.tar.gz && \ @@ -166,9 +166,8 @@ RUN apt-get install -y --no-install-recommends \ `# Java dependencies` \ ant \ ant-optional \ - openjdk-8-jdk \ - maven && \ - update-alternatives --set java /usr/lib/jvm/java-8-openjdk-amd64/jre/bin/java + maven \ + openjdk-11-jdk-headless RUN apt-get install -y --no-install-recommends \ `# Lua dependencies` \ @@ -200,13 +199,15 @@ RUN apt-get install -y --no-install-recommends \ libclass-accessor-class-perl \ libcrypt-ssleay-perl \ libio-socket-ssl-perl \ - libnet-ssleay-perl + libnet-ssleay-perl \ + libtest-exception-perl RUN apt-get install -y --no-install-recommends \ `# Php dependencies` \ php \ php-cli \ php-dev \ + php-json \ php-pear \ re2c \ composer @@ -245,10 +246,16 @@ RUN apt-get install -y --no-install-recommends \ ruby-dev \ ruby-bundler -RUN apt-get install -y --no-install-recommends \ -`# Rust dependencies` \ - cargo \ - rustc +# Rust dependencies +RUN curl https://sh.rustup.rs -sSf | sh -s -- --default-toolchain 1.40.0 -y +ENV PATH /root/.cargo/bin:$PATH + +# Swift on Linux for cross tests +RUN cd / && \ + wget --quiet https://swift.org/builds/swift-5.1.4-release/ubuntu1804/swift-5.1.4-RELEASE/swift-5.1.4-RELEASE-ubuntu18.04.tar.gz && \ + tar xf swift-5.1.4-RELEASE-ubuntu18.04.tar.gz --strip-components=1 && \ + rm swift-5.1.4-RELEASE-ubuntu18.04.tar.gz && \ + swift --version # cppcheck-1.82 has a nasty cpp parser bug, so we're using something newer RUN apt-get install -y --no-install-recommends \ diff --git a/build/docker/ubuntu-disco/Dockerfile b/build/docker/ubuntu-disco/Dockerfile new file mode 100644 index 00000000000..531718c2a90 --- /dev/null +++ b/build/docker/ubuntu-disco/Dockerfile @@ -0,0 +1,282 @@ +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# +# Apache Thrift Docker build environment for Ubuntu Disco +# with some updated packages. +# + +FROM buildpack-deps:disco-scm +MAINTAINER Apache Thrift +ENV DEBIAN_FRONTEND noninteractive + +### Add apt repos + +RUN apt-get update && \ + apt-get dist-upgrade -y && \ + apt-get install -y --no-install-recommends \ + apt \ + apt-transport-https \ + apt-utils \ + curl \ + dirmngr \ + software-properties-common \ + wget + +# Dart +RUN curl https://dl-ssl.google.com/linux/linux_signing_key.pub | apt-key add - && \ + curl https://storage.googleapis.com/download.dartlang.org/linux/debian/dart_stable.list > \ + /etc/apt/sources.list.d/dart_stable.list + +# dotnet (netcore) +RUN curl https://packages.microsoft.com/keys/microsoft.asc | gpg --dearmor > /etc/apt/trusted.gpg.d/microsoft.gpg && \ + wget -q -O /etc/apt/sources.list.d/microsoft-prod.list https://packages.microsoft.com/config/ubuntu/18.04/prod.list && \ + chown root:root /etc/apt/trusted.gpg.d/microsoft.gpg && \ + chown root:root /etc/apt/sources.list.d/microsoft-prod.list + +# erlang +RUN wget -O- https://packages.erlang-solutions.com/ubuntu/erlang_solutions.asc | apt-key add - && \ + echo "deb https://packages.erlang-solutions.com/ubuntu disco contrib" | tee /etc/apt/sources.list.d/erlang.list + +# node.js +RUN curl -sL https://deb.nodesource.com/gpgkey/nodesource.gpg.key | apt-key add - && \ + echo "deb https://deb.nodesource.com/node_10.x disco main" | tee /etc/apt/sources.list.d/nodesource.list + +### install general dependencies +RUN apt-get update && apt-get install -y --no-install-recommends \ +`# General dependencies` \ + bash-completion \ + bison \ + build-essential \ + clang \ + cmake \ + debhelper \ + flex \ + gdb \ + libasound2 \ + libatk-bridge2.0-0 \ + libgtk-3-0 \ + llvm \ + ninja-build \ + pkg-config \ + unzip \ + valgrind \ + vim +ENV PATH /usr/lib/llvm-6.0/bin:$PATH + +# lib/as3 (ActionScript) +RUN mkdir -p /usr/local/adobe/flex/4.6 && \ + cd /usr/local/adobe/flex/4.6 && \ + wget -q "http://download.macromedia.com/pub/flex/sdk/flex_sdk_4.6.zip" && \ + unzip flex_sdk_4.6.zip +ENV FLEX_HOME /usr/local/adobe/flex/4.6 + +RUN apt-get install -y --no-install-recommends \ +`# C++ dependencies` \ + libboost-all-dev \ + libevent-dev \ + libssl-dev \ + qt5-default \ + qtbase5-dev \ + qtbase5-dev-tools + +ENV SBCL_VERSION 1.5.3 +RUN \ +`# Common Lisp (sbcl) dependencies` \ + curl --version && \ + curl -o sbcl-${SBCL_VERSION}-x86-64-linux-binary.tar.bz2 -J -L https://sourceforge.net/projects/sbcl/files/sbcl/${SBCL_VERSION}/sbcl-${SBCL_VERSION}-x86-64-linux-binary.tar.bz2/download?use_mirror=managedway# && \ + tar xjf sbcl-${SBCL_VERSION}-x86-64-linux-binary.tar.bz2 && \ + cd sbcl-${SBCL_VERSION}-x86-64-linux && \ + ./install.sh && \ + sbcl --version && \ + cd .. && \ + rm -rf sbcl* + +ENV D_VERSION 2.087.0 +ENV DMD_DEB dmd_2.087.0-0_amd64.deb +RUN \ +`# D dependencies` \ + wget -q http://downloads.dlang.org/releases/2.x/${D_VERSION}/${DMD_DEB} && \ + dpkg --install ${DMD_DEB} && \ + rm -f ${DMD_DEB} && \ + mkdir -p /usr/include/dmd/druntime/import/deimos /usr/include/dmd/druntime/import/C && \ + git clone -b 'v2.0.2+2.0.16' https://github.com/D-Programming-Deimos/libevent.git deimos-libevent-2.0 && \ + mv deimos-libevent-2.0/deimos/* /usr/include/dmd/druntime/import/deimos/ && \ + mv deimos-libevent-2.0/C/* /usr/include/dmd/druntime/import/C/ && \ + rm -rf deimos-libevent-2.0 && \ + git clone -b 'v2.0.0+1.1.0h' https://github.com/D-Programming-Deimos/openssl.git deimos-openssl-1.1.0h && \ + mv deimos-openssl-1.1.0h/deimos/* /usr/include/dmd/druntime/import/deimos/ && \ + mv deimos-openssl-1.1.0h/C/* /usr/include/dmd/druntime/import/C/ && \ + rm -rf deimos-openssl-1.1.0h + +ENV DART_VERSION 2.4.0-1 +RUN apt-get install -y --no-install-recommends \ + `# Dart dependencies` \ + dart=$DART_VERSION +ENV PATH /usr/lib/dart/bin:$PATH + +RUN apt-get install -y --no-install-recommends \ +`# dotnet core dependencies` \ + dotnet-sdk-3.1 + +RUN apt-get install -y --no-install-recommends \ +`# Erlang dependencies` \ + erlang && \ + wget https://s3.amazonaws.com/rebar3/rebar3 -O /usr/bin/rebar3 && \ + chmod 755 /usr/bin/rebar3 && \ + rebar3 --version + +RUN apt-get install -y --no-install-recommends \ +`# GlibC dependencies` \ + libglib2.0-dev + +# golang +ENV GOLANG_VERSION 1.15.7 +ENV GOLANG_DOWNLOAD_URL https://golang.org/dl/go$GOLANG_VERSION.linux-amd64.tar.gz +ENV GOLANG_DOWNLOAD_SHA256 0d142143794721bb63ce6c8a6180c4062bcf8ef4715e7d6d6609f3a8282629b3 +RUN curl -fsSL "$GOLANG_DOWNLOAD_URL" -o golang.tar.gz && \ + echo "$GOLANG_DOWNLOAD_SHA256 golang.tar.gz" | sha256sum -c - && \ + tar -C /usr/local -xzf golang.tar.gz && \ + ln -s /usr/local/go/bin/go /usr/local/bin && \ + rm golang.tar.gz + +RUN apt-get install -y --no-install-recommends \ +`# Haskell dependencies` \ + ghc \ + cabal-install + +RUN apt-get install -y --no-install-recommends \ +`# Haxe dependencies` \ + haxe \ + neko \ + neko-dev && \ + haxelib setup --always /usr/share/haxe/lib && \ + haxelib install --always hxcpp 2>&1 > /dev/null + +RUN apt-get install -y --no-install-recommends \ +`# Java dependencies` \ + ant \ + ant-optional \ + maven \ + openjdk-11-jdk-headless + +RUN apt-get install -y --no-install-recommends \ +`# Lua dependencies` \ + lua5.2 \ + lua5.2-dev +# https://bugs.launchpad.net/ubuntu/+source/lua5.3/+bug/1707212 +# lua5.3 does not install alternatives! +# need to update our luasocket code, lua doesn't have luaL_openlib any more + +RUN apt-get install -y --no-install-recommends \ +`# Node.js dependencies` \ + nodejs + +# Test dependencies for running puppeteer +RUN apt-get install -y --no-install-recommends \ +`# JS dependencies` \ + libxss1 + +# does not work on disco? +# RUN apt-get install -y --no-install-recommends \ +# `# OCaml dependencies` \ +# ocaml \ +# opam && \ +# opam init --yes && \ +# opam install --yes oasis + +RUN apt-get install -y --no-install-recommends \ +`# Perl dependencies` \ + libbit-vector-perl \ + libclass-accessor-class-perl \ + libcrypt-ssleay-perl \ + libio-socket-ssl-perl \ + libnet-ssleay-perl \ + libtest-exception-perl + +RUN apt-get install -y --no-install-recommends \ +`# Php dependencies` \ + php \ + php-cli \ + php-dev \ + php-json \ + php-pear \ + re2c \ + composer + +RUN apt-get install -y --no-install-recommends \ +`# Python dependencies` \ + python-all \ + python-all-dbg \ + python-all-dev \ + python-ipaddress \ + python-pip \ + python-setuptools \ + python-six \ + python-tornado \ + python-twisted \ + python-wheel \ + python-zope.interface && \ + pip install --upgrade backports.ssl_match_hostname + +RUN apt-get install -y --no-install-recommends \ +`# Python3 dependencies` \ + python3-all \ + python3-all-dbg \ + python3-all-dev \ + python3-pip \ + python3-setuptools \ + python3-six \ + python3-tornado \ + python3-twisted \ + python3-wheel \ + python3-zope.interface + +RUN apt-get install -y --no-install-recommends \ +`# Ruby dependencies` \ + ruby \ + ruby-dev \ + ruby-bundler + +# Rust dependencies +RUN curl https://sh.rustup.rs -sSf | sh -s -- --default-toolchain 1.40.0 -y +ENV PATH /root/.cargo/bin:$PATH + +# Swift on Linux for cross tests +# does not work on disco +# RUN cd / && \ +# wget --quiet https://swift.org/builds/swift-4.2.1-release/ubuntu1804/swift-4.2.1-RELEASE/swift-4.2.1-RELEASE-ubuntu18.04.tar.gz && \ +# tar xf swift-4.2.1-RELEASE-ubuntu18.04.tar.gz --strip-components=1 && \ +# rm swift-4.2.1-RELEASE-ubuntu18.04.tar.gz && \ +# swift --version + +# cppcheck-1.82 has a nasty cpp parser bug, so we're using something newer +# don't need this on disco, nobody uses it +# RUN apt-get install -y --no-install-recommends \ +# `# Static Code Analysis dependencies` \ +# cppcheck \ +# sloccount && \ +# pip install flake8 && \ +# wget -q "https://launchpad.net/ubuntu/+source/cppcheck/1.83-2/+build/14874703/+files/cppcheck_1.83-2_amd64.deb" && \ +# dpkg -i cppcheck_1.83-2_amd64.deb && \ +# rm cppcheck_1.83-2_amd64.deb + +# Clean up +RUN rm -rf /var/cache/apt/* && \ + rm -rf /var/lib/apt/lists/* && \ + rm -rf /tmp/* && \ + rm -rf /var/tmp/* + +ENV THRIFT_ROOT /thrift +RUN mkdir -p $THRIFT_ROOT/src +COPY Dockerfile $THRIFT_ROOT/ +WORKDIR $THRIFT_ROOT/src diff --git a/build/docker/ubuntu-xenial/Dockerfile b/build/docker/ubuntu-xenial/Dockerfile index 3372b4dbdbb..e554c53179c 100644 --- a/build/docker/ubuntu-xenial/Dockerfile +++ b/build/docker/ubuntu-xenial/Dockerfile @@ -14,10 +14,10 @@ # Apache Thrift Docker build environment for Ubuntu Xenial # Using all stock Ubuntu Xenial packaging except for: # - d: does not come with Ubuntu so we're installing 2.075.1 for coverage -# - dart: does not come with Ubuntu so we're installing 1.22.1 for coverage +# - dart: does not come with Ubuntu so we're installing 2.0.0-1 for coverage # - dotnet: does not come with Ubuntu -# - go: Xenial comes with 1.6, but we need 1.7 or later -# - nodejs: Xenial comes with 4.2.6 which exits LTS April 2018, so we're installing 6.x +# - go: Xenial comes with 1.6, but we need 1.10 or later +# - nodejs: Xenial comes with 4.2.6 which exits LTS April 2018, so we're installing 10.x # - ocaml: causes stack overflow error, just started March 2018 not sure why # @@ -35,31 +35,29 @@ RUN apt-get update && \ apt-utils \ curl \ software-properties-common \ - wget - -# csharp (mono) -# RUN echo "deb http://download.mono-project.com/repo/debian xenial main" | tee /etc/apt/sources.list.d/mono.list && \ -# apt-key adv --keyserver keyserver.ubuntu.com --recv-keys A6A19B38D3D831EF + wget && \ # D -RUN apt-key adv --keyserver keyserver.ubuntu.com --recv-keys EBCF975E5BA24D5E && \ + apt-key adv --keyserver keyserver.ubuntu.com --recv-keys EBCF975E5BA24D5E && \ wget http://master.dl.sourceforge.net/project/d-apt/files/d-apt.list -O /etc/apt/sources.list.d/d-apt.list && \ - wget -qO - https://dlang.org/d-keyring.gpg | apt-key add - + wget -qO - https://dlang.org/d-keyring.gpg | apt-key add - && \ # Dart -RUN curl https://dl-ssl.google.com/linux/linux_signing_key.pub | apt-key add - && \ + curl https://dl-ssl.google.com/linux/linux_signing_key.pub | apt-key add - && \ curl https://storage.googleapis.com/download.dartlang.org/linux/debian/dart_stable.list > \ - /etc/apt/sources.list.d/dart_stable.list -ENV DART_VERSION 1.22.1-1 + /etc/apt/sources.list.d/dart_stable.list && \ # dotnet (core) -RUN curl https://packages.microsoft.com/keys/microsoft.asc | gpg --dearmor > /etc/apt/trusted.gpg.d/microsoft.gpg && \ + curl https://packages.microsoft.com/keys/microsoft.asc | gpg --dearmor > /etc/apt/trusted.gpg.d/microsoft.gpg && \ echo "deb [arch=amd64] https://packages.microsoft.com/repos/microsoft-ubuntu-xenial-prod xenial main" > \ - /etc/apt/sources.list.d/dotnetdev.list + /etc/apt/sources.list.d/dotnetdev.list && \ # node.js -RUN curl -sL https://deb.nodesource.com/gpgkey/nodesource.gpg.key | apt-key add - && \ - echo "deb https://deb.nodesource.com/node_6.x xenial main" | tee /etc/apt/sources.list.d/nodesource.list + curl -sL https://deb.nodesource.com/gpgkey/nodesource.gpg.key | apt-key add - && \ + echo "deb https://deb.nodesource.com/node_10.x xenial main" | tee /etc/apt/sources.list.d/nodesource.list && \ + +# ruby 2.4 + apt-add-repository ppa:brightbox/ruby-ng ### install general dependencies RUN apt-get update && apt-get install -y --no-install-recommends \ @@ -95,10 +93,6 @@ RUN apt-get install -y --no-install-recommends \ qtbase5-dev \ qtbase5-dev-tools -RUN apt-get install -y --no-install-recommends \ -`# csharp (mono) dependencies` \ - mono-devel - ENV D_VERSION 2.075.1-0 RUN apt-get install -y --allow-unauthenticated --no-install-recommends \ `# D dependencies` \ @@ -120,6 +114,7 @@ RUN curl -sSL https://github.com/D-Programming-Deimos/openssl/archive/v1.1.6+1.0 mv openssl-1.1.6-1.0.1g/C/* /usr/include/dmd/druntime/import/C/ && \ rm -rf openssl-1.1.6-1.0.1g +ENV DART_VERSION 2.0.0-1 RUN apt-get install -y --no-install-recommends \ `# Dart dependencies` \ dart=$DART_VERSION @@ -127,7 +122,7 @@ ENV PATH /usr/lib/dart/bin:$PATH RUN apt-get install -y --no-install-recommends \ `# dotnet core dependencies` \ - dotnet-sdk-2.1.4 + dotnet-sdk-3.1 RUN apt-get install -y --no-install-recommends \ `# Erlang dependencies` \ @@ -142,16 +137,16 @@ RUN apt-get install -y --no-install-recommends \ libglib2.0-dev # golang -ENV GOLANG_VERSION 1.7.6 +ENV GOLANG_VERSION 1.14.14 ENV GOLANG_DOWNLOAD_URL https://golang.org/dl/go$GOLANG_VERSION.linux-amd64.tar.gz -ENV GOLANG_DOWNLOAD_SHA256 ad5808bf42b014c22dd7646458f631385003049ded0bb6af2efc7f1f79fa29ea +ENV GOLANG_DOWNLOAD_SHA256 6f1354c9040d65d1622b451f43c324c1e5197aa9242d00c5a117d0e2625f3e0d RUN curl -fsSL "$GOLANG_DOWNLOAD_URL" -o golang.tar.gz && \ echo "$GOLANG_DOWNLOAD_SHA256 golang.tar.gz" | sha256sum -c - && \ tar -C /usr/local -xzf golang.tar.gz && \ ln -s /usr/local/go/bin/go /usr/local/bin && \ rm golang.tar.gz -# due to a bug in cabal in xenial (cabal-install package) we pull in another: +# cabal 1.22 in xenial is too old so we grab a pre-built 1.24 binary RUN apt-get install -y --no-install-recommends \ `# Haskell dependencies` \ ghc && \ @@ -160,7 +155,8 @@ RUN apt-get install -y --no-install-recommends \ tar xzf cabal-install-1.24.0.2-x86_64-unknown-linux.tar.gz && \ find dist-newstyle/ -type f -name cabal -exec mv {} /usr/bin \; && \ rm -rf /tmp/cabal* && \ - cabal --version + cabal --version && \ + cabal update RUN apt-get install -y --no-install-recommends \ `# Haxe dependencies` \ @@ -179,10 +175,11 @@ RUN apt-get install -y --no-install-recommends \ openjdk-8-jdk \ maven -RUN apt-get install -y --no-install-recommends \ -`# Lua dependencies` \ - lua5.2 \ - lua5.2-dev +# disabled: same as ubuntu-bionic jobs +# RUN apt-get install -y --no-install-recommends \ +# `# Lua dependencies` \ +# lua5.2 \ +# lua5.2-dev # https://bugs.launchpad.net/ubuntu/+source/lua5.3/+bug/1707212 # lua5.3 does not install alternatives so stick with 5.2 here @@ -211,13 +208,15 @@ RUN apt-get install -y --no-install-recommends \ libclass-accessor-class-perl \ libcrypt-ssleay-perl \ libio-socket-ssl-perl \ - libnet-ssleay-perl + libnet-ssleay-perl \ + libtest-exception-perl RUN apt-get install -y --no-install-recommends \ `# Php dependencies` \ php7.0 \ php7.0-cli \ php7.0-dev \ + php-json \ php-pear \ re2c \ composer @@ -249,14 +248,12 @@ RUN apt-get install -y --no-install-recommends \ RUN apt-get install -y --no-install-recommends \ `# Ruby dependencies` \ - ruby \ - ruby-dev \ + ruby2.4 \ + ruby2.4-dev \ ruby-bundler -RUN apt-get install -y --no-install-recommends \ -`# Rust dependencies` \ - cargo \ - rustc +# Rust dependencies +RUN curl https://sh.rustup.rs -sSf | sh -s -- --default-toolchain 1.40.0 -y # Clean up RUN rm -rf /var/cache/apt/* && \ diff --git a/build/fixchanges.sh b/build/fixchanges.sh new file mode 100755 index 00000000000..6c57b5497ba --- /dev/null +++ b/build/fixchanges.sh @@ -0,0 +1,39 @@ +#!/usr/bin/env bash +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +# +# fixchanges will take a file as input and look for text matching +# the pattern [THRIFT-nnnn] (the number of digits is not important) +# which is not a markdown link, and change it to be a markdown link. +# The tool writes to stdout so you can redirect it to a temporary +# file and then compare against the original file before replacing +# it. +# +# This tool was developed after the 0.12.0 release to assist with +# generation of CHANGES.md content. +# + +while IFS='' read -r line || [[ -n "$line" ]]; do + if [[ "$line" =~ ^(.*)\[(THRIFT-[[:digit:]]+)\][^\(](.*)$ ]]; then + echo "${BASH_REMATCH[1]}[${BASH_REMATCH[2]}](https://issues.apache.org/jira/browse/${BASH_REMATCH[2]}) ${BASH_REMATCH[3]}" + else + echo "$line" + fi +done < "$1" diff --git a/build/travis/installDependencies.sh b/build/travis/installDependencies.sh deleted file mode 100755 index eab8c6b6be8..00000000000 --- a/build/travis/installDependencies.sh +++ /dev/null @@ -1,66 +0,0 @@ -#!/bin/sh - -# Licensed to the Apache Software Foundation (ASF) under one -# or more contributor license agreements. See the NOTICE file -# distributed with this work for additional information -# regarding copyright ownership. The ASF licenses this file -# to you under the Apache License, Version 2.0 (the -# "License"); you may not use this file except in compliance -# with the License. You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, -# software distributed under the License is distributed on an -# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -# KIND, either express or implied. See the License for the -# specific language governing permissions and limitations -# under the License. - -SCRIPTPATH=$( cd $(dirname $0) ; pwd -P ) - -# Mainly aiming Travis CI's Ubuntu machines for now -# see what we need: http://thrift.apache.org/docs/install/ubuntu - -# Java dependencies -sudo apt-get install -qq ant openjdk-7-jdk -sudo update-java-alternatives -s java-1.7.0-openjdk-amd64 - -# Python dependencies -sudo apt-get install -qq python-all python-all-dev python-all-dbg python-setuptools python-support python-twisted python-six python3-six - -# Ruby dependencies -sudo apt-get install -qq ruby ruby-dev -sudo gem install bundler rake - -# Perl dependencies -sudo apt-get install -qq libbit-vector-perl libclass-accessor-class-perl libio-socket-ssl-perl libnet-ssleay-perl libcrypt-ssleay-perl - -# Php dependencies -sudo apt-get install -qq php5 php5-dev php5-cli php-pear re2c - -# GlibC dependencies -sudo apt-get install -qq libglib2.0-dev - -# Erlang dependencies -sudo apt-get install -qq erlang-base erlang-eunit erlang-dev erlang-tools rebar - -# GO dependencies -echo "golang-go golang-go/dashboard boolean false" | debconf-set-selections -sudo apt-get -y install -qq golang golang-go - -# Haskell dependencies -sudo add-apt-repository -y ppa:hvr/ghc -sudo apt-get update -sudo apt-get install cabal-install-1.20 ghc-$GHCVER - -# Lua dependencies -sudo apt-get install -qq lua5.2 lua5.2-dev - -# Node.js dependencies -sudo apt-get install -qq nodejs nodejs-dev npm -sudo update-alternatives --install /usr/bin/node node /usr/bin/nodejs 10 - -# CSharp -sudo apt-get install -qq mono-gmcs mono-devel libmono-system-web2.0-cil -sudo apt-get install -qq mingw32 mingw32-binutils mingw32-runtime nsis diff --git a/build/veralign.sh b/build/veralign.sh new file mode 100755 index 00000000000..422da85072b --- /dev/null +++ b/build/veralign.sh @@ -0,0 +1,318 @@ +#!/usr/bin/env bash +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +# +# The veralign script sets the appropriate versions in all of +# the package configuration files for all of the supported +# languages. It is used to prepare a release or move master +# forward to the next anticipated version. +# +# USAGE +# ----------------------------------------------------------- +# usage: veralign.sh +# +# EXAMPLE +# ----------------------------------------------------------- +# $ ./veralign.sh 0.12.0 1.0.0 +# $ ./veralign.sh 1.0.0 1.1.0 +# +# IMPORTANT USAGE NOTE +# ----------------------------------------------------------- +# Define the environment variable DRYRUN to have the script +# print out all matches to the oldVersion hilighted so that +# you can verify it will change the right things. +# + +declare -A FILES + +# These files require a manual touch: +FILES[CHANGES.md]=manual +FILES[debian/changelog]=manual +FILES[doap.rdf]=manual + +# These files can be updated automatically: +FILES[ApacheThrift.nuspec]=simpleReplace +FILES[appveyor.yml]=simpleReplace +FILES[bower.json]=jsonReplace +FILES[CMakeLists.txt]=simpleReplace +FILES[compiler/cpp/src/thrift/version.h]=simpleReplace +FILES[configure.ac]=configureReplace +FILES[contrib/Rebus/Properties/AssemblyInfo.cs]=simpleReplace +FILES[contrib/thrift.spec]=simpleReplace +FILES[contrib/zeromq/csharp/AssemblyInfo.cs]=simpleReplace +FILES[doc/specs/idl.md]=simpleReplace +FILES[lib/as3/gradle.properties]=simpleReplace +FILES[lib/d/src/thrift/base.d]=simpleReplace +FILES[lib/dart/pubspec.yaml]=pubspecReplace +FILES[lib/delphi/src/Thrift.pas]=simpleReplace +FILES[lib/erl/src/thrift.app.src]=simpleReplace +FILES[lib/haxe/haxelib.json]=simpleReplace +FILES[lib/hs/thrift.cabal]=simpleReplace +FILES[lib/java/gradle.properties]=simpleReplace +FILES[lib/js/package-lock.json]=jsonReplace +FILES[lib/js/package.json]=jsonReplace +FILES[lib/js/src/thrift.js]=simpleReplace +FILES[lib/lua/Thrift.lua]=simpleReplace +FILES[lib/netstd/Tests/Thrift.PublicInterfaces.Compile.Tests/Properties/AssemblyInfo.cs]=simpleReplace +FILES[lib/netstd/Thrift/Properties/AssemblyInfo.cs]=simpleReplace +FILES[lib/netstd/Thrift/Properties/AssemblyInfo.cs]=simpleReplace +FILES[lib/netstd/Thrift/Thrift.csproj]=simpleReplace +FILES[lib/ocaml/_oasis]=simpleReplace +FILES[lib/perl/lib/Thrift.pm]=simpleReplace +FILES[lib/py/setup.py]=simpleReplace +FILES[lib/rb/thrift.gemspec]=simpleReplace +FILES[lib/rs/Cargo.toml]=simpleReplace +FILES[lib/st/package.xml]=simpleReplace +FILES[lib/swift/Sources/Thrift.swift]=simpleReplace +FILES[lib/swift/Tests/ThriftTests/ThriftTests.swift]=simpleReplace +FILES[lib/ts/package-lock.json]=jsonReplace +FILES[lib/ts/package.json]=jsonReplace +FILES[package-lock.json]=jsonReplace +FILES[package.json]=jsonReplace +FILES[sonar-project.properties]=simpleReplace +FILES[test/dart/test_client/pubspec.yaml]=pubspecReplace +FILES[test/erl/src/thrift_test.app.src]=simpleReplace +FILES[test/netstd/Client/Properties/AssemblyInfo.cs]=simpleReplace +FILES[test/netstd/Server/Properties/AssemblyInfo.cs]=simpleReplace +FILES[Thrift.podspec]=simpleReplace +FILES[tutorial/dart/client/pubspec.yaml]=pubspecReplace +FILES[tutorial/dart/console_client/pubspec.yaml]=pubspecReplace +FILES[tutorial/dart/server/pubspec.yaml]=pubspecReplace +FILES[tutorial/delphi/DelphiClient/DelphiClient.dproj]=simpleReplace +FILES[tutorial/delphi/DelphiServer/DelphiServer.dproj]=simpleReplace +FILES[tutorial/hs/ThriftTutorial.cabal]=simpleReplace +FILES[tutorial/netstd/Client/Properties/AssemblyInfo.cs]=simpleReplace +FILES[tutorial/netstd/Interfaces/Properties/AssemblyInfo.cs]=simpleReplace +FILES[tutorial/netstd/Server/Properties/AssemblyInfo.cs]=simpleReplace +FILES[tutorial/ocaml/_oasis]=simpleReplace + + + +if [ ! -f "CHANGES.md" ]; then + >&2 echo "error: run veralign.sh while in the thrift root directory" + exit 1 +fi + +if [ $# -ne 2 ]; then + >&2 echo "usage: veralign.sh " + exit 1 +fi + +jq --version 1>/dev/null 2>/dev/null +if [ $? -ne 0 ]; then + >&2 echo "error: the 'jq' package is not installed" + exit 1 +fi + +# +# validateVersion: check that a version matches the major.minor.patch +# format which is the lowest common denominator supported by all +# project systems. +# \param $1 the version +# \returns 0 if the version is compliant +# +function validateVersion +{ + local result + local valid + valid=$(echo "$1" | sed '/^[[:digit:]]\+\.[[:digit:]]\+\.[[:digit:]]\+$/!{q22}') + result=$? + if [ $result -eq 22 ]; then + >&2 echo "error: version '$1' does not conform to the required major.minor.patch format" + return ${result} + fi +} + +OLDVERSION=$1 +NEWVERSION=$2 +validateVersion "${OLDVERSION}" || exit $? +validateVersion "${NEWVERSION}" || exit $? + +# +# escapeVersion: escape the version for use as a sed search +# \param $1 the version to escape +# \output the escaped string +# \returns 0 +# \example VERSEARCH=$(escapeVersion "[1.0.0]"); echo $VERSEARCH; => "\[1\.0\.0\]" +# +function escapeVersion +{ + echo "$(echo $1 | sed 's/\./\\./g' | sed 's/\[/\\\[/g' | sed 's/\]/\\\]/g')" +} + +# Set up verbose hilighting if running interactive +if [ "$(tput colors)" -ne 0 ]; then + reverse=$(tput rev) + red=$(tput setaf 1) + green=$(tput setaf 2) + yellow=$(tput setaf 3) + normal=$(tput sgr0) +fi + +declare -A MANUAL + +# +# manual: note that update of said file is manual +# \param $1 filename to do replacements on +# \returns 0 +# +function manual +{ + MANUAL["$1"]="" + return 0 +} + +# +# configureReplace: replace the AC_INIT field in configure.ac +# \param $1 filename to do replacements on +# \returns 0 on success +# + +function configureReplace +{ + replace "$1" "[thrift], [${OLDVERSION}]" "[thrift], [${NEWVERSION}]" +} + +# +# jsonReplace: replace a specific version field in a JSON file +# must be a top level "version" field in the json structure +# \param $1 filename to do replacements on +# \returns 0 on success +# + +function jsonReplace +{ + local result + local output + if [ ! -z "$DRYRUN" ]; then + output=$(jq -e ".version" "$1") + else + output=$(jq -e ".version = \"${NEWVERSION}\"" "$1" > tmp.$$.json && mv tmp.$$.json "$1") + fi + result=$? + if [ $? -ne 0 ]; then + printf "%-60s | %5d | ${red}ERROR${normal}: version tag not found" "$1" "$count" + echo + return 1 + elif [ ! -z "$DRYRUN" ]; then + output=${output%\"} + output=${output#\"} + printf "%-60s | %5d | MATCHES: version: \"${reverse}${green}${output}${normal}\"" "$1" 1 + echo + return 0 + fi + printf "%-60s | %5d | ${green}OK${normal}" "$1" 1 + echo + return 0 +} + +# +# pubspecReplace: replace a specific version field in a YAML file +# must be a top level "version" field in the yaml structure +# did not find a package that preserves comments so this is +# somewhat brain-dead, but it gets the job done +# \param $1 filename to do replacements on +# \returns 0 on success +# + +function pubspecReplace +{ + replace "$1" "version: ${OLDVERSION}" "version: ${NEWVERSION}" +} + +# +# replace: replace occurrences of one string with another +# the file specified must contain the old string at least once +# in order to be successful. +# \param $1 filename to do replacements on +# \param $2 the "old" string to be replaced +# \param $3 the "new" striing to replace it with +# \returns 0 on success +# +function replace +{ + local result + local output + local oldString="$2" + local newString="$3" + local oldRegex=$(escapeVersion "${oldString}") + local count=$(grep -Ec "${oldRegex}" "$1") + local verbose + if [ $count -eq 0 ]; then + printf "%-60s | %5d | ${red}NOT FOUND${normal}: ${oldString}" "$1" 0 + echo + return 1 + elif [ ! -z "$DRYRUN" ]; then + printf "%-60s | %5d | MATCHES:" "$1" "$count" + echo + while read -r line; do + echo " > $(echo "$line" | sed "s/${oldRegex}/${reverse}${green}${oldString}${normal}/g")" + done < <(grep -E "${oldRegex}" "$1") + return 0 + fi + output=$(sed -i "s/${oldRegex}/${newString}/g" "$1") + result=$? + if [ $result -ne 0 ]; then + printf "%-60s | %5d | ${red}ERROR${normal}: %s" "$1" "$count" "$output" + echo + return 1 + fi + printf "%-60s | %5d | ${green}OK${normal}" "$1" "$count" + echo + return 0 +} + +# +# simpleReplace: replace occurrences of ${OLDVERSION} with ${NEWVERSION} +# the file specified must contain OLDVERSION at least once +# in order to be successful. +# \param $1 filename to do replacements on +# \param $2 the "old" string to be replaced +# \param $3 the "new" striing to replace it with +# \returns 0 on success +# +function simpleReplace +{ + replace "$1" "${OLDVERSION}" "${NEWVERSION}" +} + +echo "" +echo "Apache Thrift Version Alignment Tool" +echo "------------------------------------" +echo "" +echo "Previous Version: ${OLDVERSION}" +echo " New Version: ${NEWVERSION}" +echo "" +echo "-------------------------------------------------------------+-------+----------------------" +echo "Filename | Count | Status " +echo "-------------------------------------------------------------+-------+----------------------" + +for file in $(echo "${!FILES[@]}" | sort); do + ${FILES[$file]} $file || exit $? +done + +echo +echo "Files that must be modified manually:" +echo +for manu in $(echo "${!MANUAL[@]}" | sort); do + echo " > ${yellow}${manu}${normal}" +done + +exit 0 diff --git a/build/wincpp/README.md b/build/wincpp/README.md deleted file mode 100644 index a231780407d..00000000000 --- a/build/wincpp/README.md +++ /dev/null @@ -1,219 +0,0 @@ - - -# Building thrift on Windows (Native) - -Thrift uses cmake to make it easier to build the project on multiple platforms, however to build a fully functional and production ready thrift on Windows requires a number of third party libraries to be obtained or built. Once third party libraries are ready, the right combination of options must be passed to cmake in order to generate the correct environment. - -## Summary - -These instructions will help you build thrift for windows using Visual -Studio 2010 or later. The contributed batch files will help you build -the third party libraries needed for complete thrift functionality as -well as thrift itself. - -These instructions follow a directory layout that looks like the following: - - workspace\ - build\ - this is where the out-of-tree thrift cmake builds are generated - dist\ - this is where the thrift build results end up - thirdparty\ - this is where all third party binaries and libraries live - build\ - this is where all third party out-of-tree builds are generated - (except for openssl, which only builds in-tree) - dist\ - this is where all third party distributions end up - src\ - this is where all third party source projects live - scripts\ - batch files used to set environment variables for builds - thrift\ - this is where the thrift source project lives - -Create a "workspace" directory somewhere on your system and then copy the contents of this -directory to there, then clone or unpack thrift into `workspace\thrift`. - -## Third Party Libraries - -Batch scripts are provided to build some third party libraries. You must download them and place them into the directory noted for each. You can use different versions if you prefer; these instructions were made with the versions listed. - -> TIP: To modify the versions used in the batch scripts, look in scripts\tpversions.bat. - -Build them in the order listed to satisfy their dependencies. - -### winflexbison - - source: web site - location: https://sourceforge.net/projects/winflexbison/files/win_flex_bison-latest.zip/download - version: "latest" - directory: workspace\thirdparty\dist\winflexbison - -This package is required to build the compiler. This third party package does not need to be built as it is a binary distribution of the "bison" and "flex" tools normally found on Unix boxes. - -> TIP: If you are only interested in building the compiler, you can skip the remaining third party libraries. - -### zlib - - source: web site - location: http://zlib.net/ - version: 1.2.9 - directory: workspace\thirdparty\src\zlib-1.2.9 - -To build, open the appropriate Visual Studio command prompt and then run -the build-zlib.bat script in thirdparty\src. - -### openssl - - source: web site - location: https://www.openssl.org/ - version: 1.1.0c - directory: workspace\thirdparty\src\openssl-1.1.0c - depends-on: zlib - -If you are using openssl-1.1.0 or later, they changed static builds to use Microsoft Static RTL for release builds. zlib by default uses a dynamic runtime, as does libevent. Edit the file Configurations/10-main.conf and replace the section contents for "VC-noCE-common" with what appears below to make openssl build with dynamic runtime instead: - - "VC-noCE-common" => { - inherit_from => [ "VC-common" ], - template => 1, - cflags => add(picker(default => "-DUNICODE -D_UNICODE", - debug => "/MDd /Od -DDEBUG -D_DEBUG", - release => "/MD /O2" - )), - bin_cflags => add(picker(debug => "/MDd", - release => "/MD", - )), - bin_lflags => add("/subsystem:console /opt:ref"), - ex_libs => add(sub { - my @ex_libs = (); - push @ex_libs, 'ws2_32.lib' unless $disabled{sock}; - push @ex_libs, 'gdi32.lib advapi32.lib crypt32.lib user32.lib'; - return join(" ", @ex_libs); - }), - }, - -To build, open the appropriate Visual Studio command prompt and then run -the build-openssl.bat script in thirdparty\src. - -### libevent - - source: git - location: https://github.com/nmathewson/Libevent.git - use: commit 3821cca1a637f4da4099c9343e7326da00f6981c or later - date: Fri Dec 23 16:19:35 2016 +0800 or later - version: corresponds to 2.1.7rc + patches - directory: workspace\thirdparty\src\libevent-2.1.7rc2 - depends-on: openssl, zlib - -To build, open the appropriate Visual Studio command prompt and then run -the build-libevent.bat script in thirdparty\src. - -### msinttypes - - source: web site - location: https://code.google.com/archive/p/msinttypes/downloads - version: 26 - directory: workspace\thirdparty\dist\msinttypes - -> TIP: This is only necessary for Visual Studio 2010, which did not include an header. - -This third party package does not need to be built as it is a distribution of header files. - -### boost - - source: web site - location: http://boost.teeks99.com/ - version: 1_62_0 - directory: workspace\thirdparty\dist\boost_1_62_0 - -The pre-built binary versions of boost come in self-unpacking executables. Run each of the ones you are interested in and point them at the same thirdparty dist directory. - -## Building a Production thrift Compiler - -### Prerequisites - -* CMake-2.8.12.2 or later -* Visual Studio 2010 or later -* thrift source placed into workspace\thrift -* winflexbison placed into workspace\thirdparty\dist - -### Instructions - -By following these instructions you will end up with a release mode thrift compiler that is suitable for distribution as it has no external dependencies. - -1. Open the appropriate Visual Studio Command Prompt. -2. `cd workspace` -3. `build-thrift-compiler.bat` - -The batch file uses CMake to generate an out-of-tree build directory in `workspace\build` and then builds the compiler. The resulting `thrift.exe` program is placed into `workspace\dist` in a path that depends on your compiler version and platform. For example, if you use a Visual Studio 2010 x64 Command Prompt, the compiler will be placed into `workspace\dist\thrift-compiler-dev\vc100\x64\Release\thrift.exe` - -#### Details - -This section is for those who are curious about the CMake options used in the build process. - -CMake takes the source tree as the first argument and uses the remaining arguments for configuration. The batch file `build-thrift-compiler` essentially performs the following commands: - - C:\> CD workspace\build - C:\workspace\build> "C:\Program Files\CMake\bin\cmake.exe" ..\thrift - -DBISON_EXECUTABLE=..\thirdparty\dist\winflexbison\win_bison.exe - -DCMAKE_BUILD_TYPE=Release - -DFLEX_EXECUTABLE=..\thirdparty\dist\winflexbison\win_flex.exe - -DWITH_MT=ON - -DWITH_SHARED_LIB=OFF - -G"NMake Makefiles" - C:\workspace\build> NMAKE /FMakefile thrift-compiler - -WITH_MT controls the dynamic or static runtime library selection. To build a production compiler, the thrift project recommends using the static runtime library to make the executable portable. The batch file sets this. - -You can build a Visual Studio project file by following the example but substituting a different generator for the "-G" option. Run `cmake.exe --help` for a list of generators. Typically, this is one of the following on Windows (omit "Win64" to build 32-bit instead): - -* "Visual Studio 10 2010 Win64" -* "Visual Studio 11 2012 Win64" -* "Visual Studio 12 2013 Win64" -* "Visual Studio 14 2015 Win64" -* "Visual Studio 15 2017 Win64" - -For example you can build using a Visual Studio solution file on the command line by doing: - - C:\> CD workspace\build - C:\workspace\build> "C:\Program Files\CMake\bin\cmake.exe" ..\thrift - -DBISON_EXECUTABLE=..\thirdparty\dist\winflexbison\win_bison.exe - -DCMAKE_BUILD_TYPE=Release - -DFLEX_EXECUTABLE=..\thirdparty\dist\winflexbison\win_flex.exe - -DWITH_MT=ON - -DWITH_SHARED_LIB=OFF - -G"Visual Studio 14 2015 Win64" - C:\workspace\build> MSBUILD "Apache Thrift.sln" /p:Configuration=Release /p:Platform=x64 /t:thrift-compiler - -You can also double-click on the solution file to bring it up in Visual Studio and build or debug interactively from there. - -## Building the thrift C++ Run-Time Library - -These instructions are similar to the compiler build however there are additional dependencies on third party libraries to build a feature-complete runtime. The resulting static link library for thrift uses a dynamic Microsoft runtime. - -1. Open the desired Visual Studio Command Prompt. -2. `cd workspace` -3. `build-thrift.bat` - -Thrift depends on boost, libevent, openssl, and zlib in order to build with all server and transport types. To use later versions of boost like 1.62 you will need a recent version of cmake (at least 3.7). - -The build-thrift script has options to build debug or release and to optionally disable any of the generation (cmake), build, or test phases. By default, the batch file will generate an out-of-tree build directory inside `workspace\build`, then perform a release build, then run the unit tests. The batch file accepts some option flags to control its behavior: - - :: Flags you can use to change this behavior: - :: - :: /DEBUG - if building, perform a debug build instead - :: /NOGENERATE - skip cmake generation - useful if you - :: have already generated a solution and just - :: want to build - :: /NOBUILD - skip cmake build - useful if you just - :: want to generate a solution - :: /NOTEST - skip ctest execution - -For example if you want to generate the cmake environment without building or running tests: - - C:\workspace> build-thrift.bat /NOBUILD /NOTEST diff --git a/build/wincpp/build-thrift-compiler.bat b/build/wincpp/build-thrift-compiler.bat deleted file mode 100644 index b6b42a8d76f..00000000000 --- a/build/wincpp/build-thrift-compiler.bat +++ /dev/null @@ -1,79 +0,0 @@ -:: -:: Licensed under the Apache License, Version 2.0 (the "License"); -:: you may not use this file except in compliance with the License. -:: You may obtain a copy of the License at -:: -:: http://www.apache.org/licenses/LICENSE-2.0 -:: -:: Unless required by applicable law or agreed to in writing, software -:: distributed under the License is distributed on an "AS IS" BASIS, -:: WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -:: See the License for the specific language governing permissions and -:: limitations under the License. -:: - -:: -:: Produces a production thrift compiler suitable for redistribution. -:: The compiler is linked to runtime statically for maximum portability. -:: Assumes the thirdparty files for "winflexbison" have been placed -:: according to the README.md instructions. -:: -:: Open a Visual Studio Command Prompt of your choosing and then -:: run this script. - -@ECHO OFF -SETLOCAL EnableDelayedExpansion - -IF NOT DEFINED PACKAGE_NAME SET PACKAGE_NAME=thrift -IF NOT DEFINED PACKAGE_VERSION SET PACKAGE_VERSION=dev -IF NOT DEFINED SOURCE_DIR SET SOURCEDIR=%~dp0%PACKAGE_NAME% -IF NOT DEFINED WIN3P_ROOT SET WIN3P_ROOT=%~dp0thirdparty - -:: Set COMPILER to (vc100 - vc140) depending on the current environment -CALL scripts\cl_setcompiler.bat || EXIT /B - -:: Set ARCH to either win32 or x64 depending on the current environment -CALL scripts\cl_setarch.bat || EXIT /B - -:: Set GENERATOR for CMake depending on the current environment -CALL scripts\cl_setgenerator.bat || EXIT /B - -IF NOT DEFINED BUILDTYPE ( - SET BUILDTYPE=Release -) - - SET BUILDDIR=%~dp0build\%PACKAGE_NAME%-compiler\%PACKAGE_VERSION%\%COMPILER%\ - SET OUTDIR=%~dp0dist\%PACKAGE_NAME%-compiler-%PACKAGE_VERSION%\%COMPILER%\%ARCH%\%BUILDTYPE%\ - SET BOOST_LIBDIR=lib%ARCH:~-2,2%-msvc-%COMPILER:~-3,2%.0 - IF "%BUILDTYPE%" == "Debug" (SET ZLIB_STATIC_SUFFIX=d) - - ECHO/ - ECHO ========================================================================= - ECHO Configuration: %PACKAGE_NAME% %PACKAGE_VERSION% %COMPILER%:%ARCH%:%BUILDTYPE% "%GENERATOR%" -IF DEFINED COMPILERONLY ( - ECHO COMPILER ONLY -) - ECHO Build Directory: %BUILDDIR% - ECHO Install Directory: %OUTDIR% - ECHO Source Directory: %SOURCEDIR% - ECHO ========================================================================= - ECHO/ - - MKDIR "%BUILDDIR%" - CD "%BUILDDIR%" || EXIT /B - - CMAKE.EXE %~dp0thrift ^ - -G"%GENERATOR%" ^ - -DBISON_EXECUTABLE=%WIN3P_ROOT%\dist\winflexbison\win_bison.exe ^ - -DCMAKE_BUILD_TYPE=%BUILDTYPE% ^ - -DFLEX_EXECUTABLE=%WIN3P_ROOT%\dist\winflexbison\win_flex.exe ^ - -DWITH_MT=ON ^ - -DWITH_SHARED_LIB=OFF || EXIT /B - - CD %BUILDDIR% - - CMAKE.EXE --build . --config %BUILDTYPE% --target thrift-compiler || EXIT /B - XCOPY /F /Y %BUILDDIR%\bin\%BUILDTYPE%\thrift.exe %OUTDIR% - -ENDLOCAL -EXIT /B diff --git a/build/wincpp/build-thrift.bat b/build/wincpp/build-thrift.bat deleted file mode 100644 index ba3e47675e2..00000000000 --- a/build/wincpp/build-thrift.bat +++ /dev/null @@ -1,164 +0,0 @@ -:: -:: Licensed under the Apache License, Version 2.0 (the "License"); -:: you may not use this file except in compliance with the License. -:: You may obtain a copy of the License at -:: -:: http://www.apache.org/licenses/LICENSE-2.0 -:: -:: Unless required by applicable law or agreed to in writing, software -:: distributed under the License is distributed on an "AS IS" BASIS, -:: WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -:: See the License for the specific language governing permissions and -:: limitations under the License. -:: - -:: -:: Generates a Visual Studio solution for thrift and then builds it. -:: Assumes third party libraries have been built or placed already. -:: -:: Open a Visual Studio Command Prompt of your choosing and then -:: run this script. -:: -:: Normally the script will run cmake to generate a solution, then -:: perform a build, then run tests on the complete thrift library -:: in release mode. -:: -:: Flags you can use to change this behavior: -:: -:: /DEBUG - debug instead of release -:: /IDE - launch Visual Studio with a path set -:: up correctly to run tests instead of -:: performing any other actions, i.e. -:: implies setting the next three flags -:: /NOGENERATE - skip cmake generation - useful if you -:: have already generated a solution and just -:: want to build -:: /NOBUILD - skip cmake build - useful if you just -:: want to generate a solution -:: /NOTEST - skip ctest execution -:: - -@ECHO OFF -SETLOCAL EnableDelayedExpansion - -:: Sets variables for third party versions used in build -CALL scripts\tpversions.bat || EXIT /B - -IF NOT DEFINED PACKAGE_NAME SET PACKAGE_NAME=thrift -IF NOT DEFINED PACKAGE_VERSION SET PACKAGE_VERSION=dev -IF NOT DEFINED SOURCE_DIR SET SOURCEDIR=%~dp0%PACKAGE_NAME% -IF NOT DEFINED WIN3P_ROOT SET WIN3P_ROOT=%~dp0thirdparty - -:: Set COMPILER to (vc100 - vc140) depending on the current environment -CALL scripts\cl_setcompiler.bat || EXIT /B - -:: Set ARCH to either win32 or x64 depending on the current environment -CALL scripts\cl_setarch.bat || EXIT /B - -:: Set GENERATOR for CMake depending on the current environment -CALL scripts\cl_setgenerator.bat || EXIT /B - -:: Defaults - -IF NOT DEFINED BUILDTYPE SET BUILDTYPE=Release -SET OPT_IDE=0 -SET OPT_BUILD=1 -SET OPT_GENERATE=1 -SET OPT_TEST=1 - -:: Apply Flags - -IF /I "%1" == "/DEBUG" SET BUILDTYPE=Debug -IF /I "%2" == "/DEBUG" SET BUILDTYPE=Debug -IF /I "%3" == "/DEBUG" SET BUILDTYPE=Debug -IF /I "%1" == "/IDE" SET OPT_IDE=1 -IF /I "%2" == "/IDE" SET OPT_IDE=1 -IF /I "%3" == "/IDE" SET OPT_IDE=1 -IF /I "%1" == "/NOBUILD" SET OPT_BUILD=0 -IF /I "%2" == "/NOBUILD" SET OPT_BUILD=0 -IF /I "%3" == "/NOBUILD" SET OPT_BUILD=0 -IF /I "%1" == "/NOGENERATE" SET OPT_GENERATE=0 -IF /I "%2" == "/NOGENERATE" SET OPT_GENERATE=0 -IF /I "%3" == "/NOGENERATE" SET OPT_GENERATE=0 -IF /I "%1" == "/NOTEST" SET OPT_TEST=0 -IF /I "%2" == "/NOTEST" SET OPT_TEST=0 -IF /I "%3" == "/NOTEST" SET OPT_TEST=0 - -IF %OPT_IDE% == 1 ( - SET OPT_GENERATE=0 - SET OPT_BUILD=0 - SET OPT_TEST=0 -) - - SET BUILDDIR=%~dp0build\%PACKAGE_NAME%\%PACKAGE_VERSION%\%COMPILER%\%ARCH%\ - SET OUTDIR=%~dp0dist\%PACKAGE_NAME%-%PACKAGE_VERSION%\%COMPILER%\%ARCH%\%BUILDTYPE%\ - SET BOOST_LIBDIR=lib%ARCH:~-2,2%-msvc-%COMPILER:~-3,2%.0 - IF "%BUILDTYPE%" == "Debug" (SET ZLIB_STATIC_SUFFIX=d) - - ECHO/ - ECHO ========================================================================= - ECHO Configuration: %PACKAGE_NAME% %PACKAGE_VERSION% %COMPILER%:%ARCH%:%BUILDTYPE% "%GENERATOR%" -IF DEFINED COMPILERONLY ( - ECHO COMPILER ONLY -) - ECHO Build Directory: %BUILDDIR% - ECHO Install Directory: %OUTDIR% - ECHO Source Directory: %SOURCEDIR% - ECHO ========================================================================= - ECHO/ - -IF %OPT_IDE% == 1 ( - - CALL :SETRUNPATH || EXIT /B - CALL DEVENV "!BUILDDIR!Apache Thrift.sln" || EXIT /B - EXIT /B - -) - - MKDIR "%BUILDDIR%" - CD "%BUILDDIR%" || EXIT /B - -IF %OPT_GENERATE% == 1 ( - - CMAKE.EXE %~dp0thrift ^ - -G"%GENERATOR%" ^ - -DBISON_EXECUTABLE=%WIN3P_ROOT%\dist\winflexbison\win_bison.exe ^ - -DBOOST_ROOT=%WIN3P_ROOT%\dist\boost_%TP_BOOST_VERSION% ^ - -DBOOST_LIBRARYDIR=%WIN3P_ROOT%\dist\boost_%TP_BOOST_VERSION%\%BOOST_LIBDIR% ^ - -DCMAKE_INSTALL_PREFIX=%OUTDIR% ^ - -DCMAKE_BUILD_TYPE=%BUILDTYPE% ^ - -DFLEX_EXECUTABLE=%WIN3P_ROOT%\dist\winflexbison\win_flex.exe ^ - -DINTTYPES_ROOT=%WIN3P_ROOT%\dist\msinttypes ^ - -DLIBEVENT_ROOT=%WIN3P_ROOT%\dist\libevent-%TP_LIBEVENT_VERSION%\%COMPILER%\%ARCH%\%BUILDTYPE% ^ - -DOPENSSL_ROOT_DIR=%WIN3P_ROOT%\dist\openssl-%TP_OPENSSL_VERSION%\%COMPILER%\%ARCH%\%BUILDTYPE%\dynamic ^ - -DOPENSSL_USE_STATIC_LIBS=OFF ^ - -DZLIB_LIBRARY=%WIN3P_ROOT%\dist\zlib-%TP_ZLIB_VERSION%\%COMPILER%\%ARCH%\lib\zlib%ZLIB_LIB_SUFFIX%.lib ^ - -DZLIB_ROOT=%WIN3P_ROOT%\dist\zlib-%TP_ZLIB_VERSION%\%COMPILER%\%ARCH% ^ - -DWITH_BOOSTTHREADS=ON ^ - -DWITH_SHARED_LIB=OFF ^ - -DWITH_STATIC_LIB=ON || EXIT /B - -) - -IF %OPT_BUILD% == 1 ( - - CD %BUILDDIR% - CMAKE.EXE --build . --config %BUILDTYPE% --target INSTALL || EXIT /B - -) - -IF %OPT_TEST% == 1 ( - - CALL :SETRUNPATH || EXIT /B - CMAKE.EXE --build . --config %BUILDTYPE% --target RUN_TESTS || EXIT /B - -) - -:SETRUNPATH - SET PATH=!PATH!;%WIN3P_ROOT%\dist\boost_%TP_BOOST_VERSION%\%BOOST_LIBDIR% - SET PATH=!PATH!;%WIN3P_ROOT%\dist\openssl-%TP_OPENSSL_VERSION%\%COMPILER%\%ARCH%\%BUILDTYPE%\dynamic\bin - SET PATH=!PATH!;%WIN3P_ROOT%\dist\zlib-%TP_ZLIB_VERSION%\%COMPILER%\%ARCH%\bin - EXIT /B - -ENDLOCAL -EXIT /B diff --git a/build/wincpp/scripts/cl_setarch.bat b/build/wincpp/scripts/cl_setarch.bat deleted file mode 100644 index 9570a1e856c..00000000000 --- a/build/wincpp/scripts/cl_setarch.bat +++ /dev/null @@ -1,47 +0,0 @@ -:: -:: Licensed under the Apache License, Version 2.0 (the "License"); -:: you may not use this file except in compliance with the License. -:: You may obtain a copy of the License at -:: -:: http://www.apache.org/licenses/LICENSE-2.0 -:: -:: Unless required by applicable law or agreed to in writing, software -:: distributed under the License is distributed on an "AS IS" BASIS, -:: WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -:: See the License for the specific language governing permissions and -:: limitations under the License. -:: - -:: -:: Detect the architecture we're building for. -:: Set the ARCH environment variable to one of: -:: win32 -:: x64 -:: -:: Honors any existing ARCH environment variable -:: setting instead of overwriting it, to allow it -:: to be forced if needed. -:: -:: Sets ERRORLEVEL to 0 if ARCH can be determined, -:: to 1 if it cannot. -:: - -IF DEFINED ARCH ( - ECHO [warn ] using existing environment variable ARCH - EXIT /B 0 -) - -CALL :CHECK x64 -IF %ERRORLEVEL% == 0 (SET ARCH=x64) ELSE (SET ARCH=win32) - -IF NOT DEFINED ARCH ( - ECHO [error] unable to determine the target architecture - EXIT /B 1 -) - -ECHO [info ] detected target architecture %ARCH% -EXIT /B 0 - -:CHECK -cl /? 2>&1 | findstr /C:" for %1%" > nul -EXIT /B %ERRORLEVEL% diff --git a/build/wincpp/scripts/cl_setcompiler.bat b/build/wincpp/scripts/cl_setcompiler.bat deleted file mode 100644 index 8405d761689..00000000000 --- a/build/wincpp/scripts/cl_setcompiler.bat +++ /dev/null @@ -1,58 +0,0 @@ -:: -:: Licensed under the Apache License, Version 2.0 (the "License"); -:: you may not use this file except in compliance with the License. -:: You may obtain a copy of the License at -:: -:: http://www.apache.org/licenses/LICENSE-2.0 -:: -:: Unless required by applicable law or agreed to in writing, software -:: distributed under the License is distributed on an "AS IS" BASIS, -:: WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -:: See the License for the specific language governing permissions and -:: limitations under the License. -:: - -:: -:: Detect the compiler edition we're building in. -:: Set the COMPILER environment variable to one of: -:: vc100 = Visual Studio 2010 -:: vc110 = Visual Studio 2012 -:: vc120 = Visual Studio 2013 -:: vc140 = Visual Studio 2015 -:: vc150 = Visual Studio 2017 -:: -:: Honors any existing COMPILER environment variable -:: setting instead of overwriting it, to allow it -:: to be forced if needed. -:: -:: Sets ERRORLEVEL to 0 if COMPILER can be determined, -:: to 1 if it cannot. -:: - -IF DEFINED COMPILER ( - ECHO [warn ] using existing environment variable COMPILER - EXIT /B 0 -) - -CALL :CHECK 16 -IF %ERRORLEVEL% == 0 (IF NOT DEFINED COMPILER SET COMPILER=vc100) -CALL :CHECK 17 -IF %ERRORLEVEL% == 0 (IF NOT DEFINED COMPILER SET COMPILER=vc110) -CALL :CHECK 18 -IF %ERRORLEVEL% == 0 (IF NOT DEFINED COMPILER SET COMPILER=vc120) -CALL :CHECK 19.00 -IF %ERRORLEVEL% == 0 (IF NOT DEFINED COMPILER SET COMPILER=vc140) -CALL :CHECK 19.10 -IF %ERRORLEVEL% == 0 (IF NOT DEFINED COMPILER SET COMPILER=vc150) - -IF NOT DEFINED COMPILER ( - ECHO [error] unable to determine the compiler edition - EXIT /B 1 -) - -ECHO [info ] detected compiler edition %COMPILER% -EXIT /B 0 - -:CHECK -cl /? 2>&1 | findstr /C:"Version %1%." > nul -EXIT /B %ERRORLEVEL% diff --git a/build/wincpp/scripts/cl_setgenerator.bat b/build/wincpp/scripts/cl_setgenerator.bat deleted file mode 100644 index bae2742f7ba..00000000000 --- a/build/wincpp/scripts/cl_setgenerator.bat +++ /dev/null @@ -1,69 +0,0 @@ -:: -:: Licensed under the Apache License, Version 2.0 (the "License"); -:: you may not use this file except in compliance with the License. -:: You may obtain a copy of the License at -:: -:: http://www.apache.org/licenses/LICENSE-2.0 -:: -:: Unless required by applicable law or agreed to in writing, software -:: distributed under the License is distributed on an "AS IS" BASIS, -:: WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -:: See the License for the specific language governing permissions and -:: limitations under the License. -:: - -:: -:: Detect the compiler edition we're building in and then -:: set the GENERATOR environment variable to one of: -:: -:: Visual Studio 15 2017 [arch] = Generates Visual Studio 2017 project files. -:: Optional [arch] can be "Win64" or "ARM". -:: Visual Studio 14 2015 [arch] = Generates Visual Studio 2015 project files. -:: Optional [arch] can be "Win64" or "ARM". -:: Visual Studio 12 2013 [arch] = Generates Visual Studio 2013 project files. -:: Optional [arch] can be "Win64" or "ARM". -:: Visual Studio 11 2012 [arch] = Generates Visual Studio 2012 project files. -:: Optional [arch] can be "Win64" or "ARM". -:: Visual Studio 10 2010 [arch] = Generates Visual Studio 2010 project files. -:: Optional [arch] can be "Win64" or "IA64". -:: -:: Honors any existing GENERATOR environment variable -:: setting instead of overwriting it, to allow it -:: to be forced if needed. -:: -:: Sets ERRORLEVEL to 0 if GENERATOR can be determined, -:: to 1 if it cannot. -:: -:: Requires cl_setarch.bat to have been executed or the ARCH environment -:: variable to be set. -:: - -IF "%ARCH%" == "x64" (SET GENARCH= Win64) - -IF DEFINED GENERATOR ( - ECHO [warn ] using existing environment variable GENERATOR - EXIT /B 0 -) - -CALL :CHECK 16 -IF %ERRORLEVEL% == 0 (IF NOT DEFINED GENERATOR SET GENERATOR=Visual Studio 10 2010%GENARCH%) -CALL :CHECK 17 -IF %ERRORLEVEL% == 0 (IF NOT DEFINED GENERATOR SET GENERATOR=Visual Studio 11 2012%GENARCH%) -CALL :CHECK 18 -IF %ERRORLEVEL% == 0 (IF NOT DEFINED GENERATOR SET GENERATOR=Visual Studio 12 2013%GENARCH%) -CALL :CHECK 19.00 -IF %ERRORLEVEL% == 0 (IF NOT DEFINED GENERATOR SET GENERATOR=Visual Studio 14 2015%GENARCH%) -CALL :CHECK 19.10 -IF %ERRORLEVEL% == 0 (IF NOT DEFINED GENERATOR SET GENERATOR=Visual Studio 15 2017%GENARCH%) - -IF NOT DEFINED GENERATOR ( - ECHO [error] unable to determine the CMake generator to use - EXIT /B 1 -) - -ECHO [info ] using CMake generator %GENERATOR% -EXIT /B 0 - -:CHECK -cl /? 2>&1 | findstr /C:"Version %1%." > nul -EXIT /B %ERRORLEVEL% diff --git a/build/wincpp/scripts/tpversions.bat b/build/wincpp/scripts/tpversions.bat deleted file mode 100644 index d80c868789c..00000000000 --- a/build/wincpp/scripts/tpversions.bat +++ /dev/null @@ -1,24 +0,0 @@ -:: -:: Licensed under the Apache License, Version 2.0 (the "License"); -:: you may not use this file except in compliance with the License. -:: You may obtain a copy of the License at -:: -:: http://www.apache.org/licenses/LICENSE-2.0 -:: -:: Unless required by applicable law or agreed to in writing, software -:: distributed under the License is distributed on an "AS IS" BASIS, -:: WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -:: See the License for the specific language governing permissions and -:: limitations under the License. -:: - -:: -:: Set the versions of third party libraries to use. -:: - -IF NOT DEFINED TP_BOOST_VERSION SET TP_BOOST_VERSION=1_62_0 -IF NOT DEFINED TP_LIBEVENT_VERSION SET TP_LIBEVENT_VERSION=2.1.7rc2 -IF NOT DEFINED TP_OPENSSL_VERSION SET TP_OPENSSL_VERSION=1.1.0c -IF NOT DEFINED TP_ZLIB_VERSION SET TP_ZLIB_VERSION=1.2.9 - -EXIT /B 0 diff --git a/build/wincpp/thirdparty/src/build-libevent.bat b/build/wincpp/thirdparty/src/build-libevent.bat deleted file mode 100644 index 4af505c61fb..00000000000 --- a/build/wincpp/thirdparty/src/build-libevent.bat +++ /dev/null @@ -1,86 +0,0 @@ -:: -:: Licensed under the Apache License, Version 2.0 (the "License"); -:: you may not use this file except in compliance with the License. -:: You may obtain a copy of the License at -:: -:: http://www.apache.org/licenses/LICENSE-2.0 -:: -:: Unless required by applicable law or agreed to in writing, software -:: distributed under the License is distributed on an "AS IS" BASIS, -:: WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -:: See the License for the specific language governing permissions and -:: limitations under the License. -:: - -:: -:: Build script for libevent on windows -:: Use libevent master from github which has cmake integration -:: Uses the environment set up by a Visual Studio Command Prompt shortcut -:: to target a specific architecture and compiler -:: -:: Creates a static link library. -:: Links against OpenSSL and zlib statically. -:: - -@ECHO OFF -SETLOCAL EnableDelayedExpansion - -:: Sets variables for third party versions used in build -CALL ..\..\scripts\tpversions.bat || EXIT /B - -:: use "build-libevent.bat /yes" to skip the question part -IF /I "%1" == "/YES" SET NOASK=1 - -:: Set COMPILER to (vc100 - vc140) depending on the current environment -CALL ..\..\scripts\cl_setcompiler.bat || EXIT /B - -:: Set ARCH to either win32 or x64 depending on the current environment -CALL ..\..\scripts\cl_setarch.bat || EXIT /B - -IF NOT DEFINED GENERATOR SET GENERATOR=NMake Makefiles -IF NOT DEFINED PACKAGE_NAME SET PACKAGE_NAME=libevent -IF NOT DEFINED PACKAGE_VERSION SET PACKAGE_VERSION=%TP_LIBEVENT_VERSION% -IF NOT DEFINED SOURCEDIR SET SOURCEDIR=%~dp0%PACKAGE_NAME%-%PACKAGE_VERSION% -IF NOT DEFINED WIN3P_ROOT SET WIN3P_ROOT=%~dp0.. - -FOR %%X IN ( - Debug - Release -) DO ( - SET BUILDTYPE=%%X - SET BUILDDIR=%WIN3P_ROOT%\build\%PACKAGE_NAME%\%PACKAGE_VERSION%\%COMPILER%\%ARCH%\!BUILDTYPE! - SET OUTDIR=%WIN3P_ROOT%\dist\%PACKAGE_NAME%-%PACKAGE_VERSION%\%COMPILER%\%ARCH%\!BUILDTYPE! - - IF "!BUILDTYPE!" == "Debug" (SET ZLIB_LIB_SUFFIX=d) - - SET CMAKE_DEFS=^ - -DEVENT__DISABLE_SAMPLES=ON ^ - -DEVENT__DISABLE_TESTS=ON ^ - -DOPENSSL_USE_STATIC_LIBS=OFF ^ - -DOPENSSL_ROOT_DIR=%WIN3P_ROOT%\dist\openssl-%TP_OPENSSL_VERSION%\%COMPILER%\%ARCH%\!BUILDTYPE!\dynamic ^ - -DZLIB_LIBRARY=%WIN3P_ROOT%\dist\zlib-%TP_ZLIB_VERSION%\%COMPILER%\%ARCH%\lib\zlib!ZLIB_LIB_SUFFIX!.lib ^ - -DZLIB_ROOT=%WIN3P_ROOT%\dist\zlib-%TP_ZLIB_VERSION%\%COMPILER%\%ARCH% - - ECHO/ - ECHO ========================================================================= - ECHO Building: %PACKAGE_NAME% v%PACKAGE_VERSION% %COMPILER%:%ARCH%:!BUILDTYPE! "%GENERATOR%" - ECHO CMake Definitions: !CMAKE_DEFS! - ECHO Build Directory: !BUILDDIR! - ECHO Install Directory: !OUTDIR! - ECHO Source Directory: %SOURCEDIR% - ECHO ========================================================================= - ECHO/ - - IF NOT DEFINED NOASK ( - CHOICE /M "Do you want to build this configuration? " /c YN - IF !ERRORLEVEL! NEQ 1 (EXIT /B !ERRORLEVEL!) - ) - - MKDIR "!BUILDDIR!" - CD "!BUILDDIR!" || EXIT /B - - CMAKE.EXE -G"%GENERATOR%" -DCMAKE_INSTALL_PREFIX=!OUTDIR! -DCMAKE_BUILD_TYPE=!BUILDTYPE! !CMAKE_DEFS! "%SOURCEDIR%" || EXIT /B - NMAKE /fMakefile install || EXIT /B -) - -ENDLOCAL diff --git a/build/wincpp/thirdparty/src/build-openssl.bat b/build/wincpp/thirdparty/src/build-openssl.bat deleted file mode 100644 index cf270f05b39..00000000000 --- a/build/wincpp/thirdparty/src/build-openssl.bat +++ /dev/null @@ -1,106 +0,0 @@ -:: -:: Licensed under the Apache License, Version 2.0 (the "License"); -:: you may not use this file except in compliance with the License. -:: You may obtain a copy of the License at -:: -:: http://www.apache.org/licenses/LICENSE-2.0 -:: -:: Unless required by applicable law or agreed to in writing, software -:: distributed under the License is distributed on an "AS IS" BASIS, -:: WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -:: See the License for the specific language governing permissions and -:: limitations under the License. -:: - -:: -:: Build script for openssl on windows -:: openssl uses an in-tree build so you have to clean between each one -:: -:: Uses the environment set up by a Visual Studio Command Prompt shortcut -:: to target a specific architecture and compiler -:: -:: If you use Lavasoft Ad-Aware, disable it for this build. It blocks the creation -:: of any file named "clienthellotest.exe" for whatever reason, which breaks the build. -:: - -@ECHO OFF -SETLOCAL EnableDelayedExpansion - -:: Sets variables for third party versions used in build -CALL ..\..\scripts\tpversions.bat || EXIT /B - -:: use "build-openssl.bat /yes" to skip the question part -IF /I "%1" == "/YES" SET NOASK=1 - -IF NOT DEFINED PACKAGE_NAME SET PACKAGE_NAME=openssl -IF NOT DEFINED PACKAGE_VERSION SET PACKAGE_VERSION=%TP_OPENSSL_VERSION% -IF NOT DEFINED SOURCEDIR SET SOURCEDIR=%~dp0%PACKAGE_NAME%-%PACKAGE_VERSION% -IF NOT DEFINED WIN3P_ROOT SET WIN3P_ROOT=%~dp0.. - -:: Set COMPILER to (vc100 - vc140) depending on the current environment -CALL ..\..\scripts\cl_setcompiler.bat || EXIT /B - -:: Set ARCH to either win32 or x64 depending on the current environment -CALL ..\..\scripts\cl_setarch.bat || EXIT /B - -IF "%ARCH%" == "x64" ( - SET TODO=debug-VC-WIN64A VC-WIN64A -) ELSE ( - SET TODO=debug-VC-WIN32 VC-WIN32 -) - -FOR %%X IN ( !TODO! ) DO ( - SET BUILDTYPE=%%X - FOR %%Y IN ( - nt - ntdll - ) DO ( - SET LIBTYPE=%%Y - - IF "!BUILDTYPE:~0,6!" == "debug-" ( - SET OUTBUILDTYPE=debug - SET ZLIBLIBSUFFIX=d - ) ELSE ( - SET OUTBUILDTYPE=release - SET ZLIBLIBSUFFIX= - ) - - IF "!LIBTYPE!" == "ntdll" ( - SET BUILD_OPTIONS=shared - SET OUTLIBTYPE=dynamic - SET ZLIBLIB=zlib!ZLIBLIBSUFFIX! - SET ZLIBOPT=zlib-dynamic - ) ELSE ( - SET BUILD_OPTIONS=no-shared - SET OUTLIBTYPE=static - SET ZLIBLIB=zlibstatic!ZLIBLIBSUFFIX!.lib - SET ZLIBOPT=zlib - ) - - SET LIB=%~dp0..\dist\zlib-%TP_ZLIB_VERSION%\!COMPILER!\!ARCH!\lib;!LIB! - SET BUILD_OPTIONS=!BUILD_OPTIONS! no-asm no-unit-test !ZLIBOPT! --openssldir=ssl --with-zlib-include=%~dp0..\dist\zlib-%TP_ZLIB_VERSION%\!COMPILER!\!ARCH!\include --with-zlib-lib=!ZLIBLIB! - SET OUTDIR=%WIN3P_ROOT%\dist\%PACKAGE_NAME%-%PACKAGE_VERSION%\%COMPILER%\%ARCH%\!OUTBUILDTYPE!\!OUTLIBTYPE! - - ECHO/ - ECHO ========================================================================= - ECHO Building: %PACKAGE_NAME% %PACKAGE_VERSION% %COMPILER%:%ARCH%:!OUTBUILDTYPE!:!OUTLIBTYPE! [!BUILDTYPE!] - ECHO Configure Options: !BUILD_OPTIONS! - ECHO Install Directory: !OUTDIR! - ECHO Source Directory: %SOURCEDIR% - ECHO ========================================================================= - ECHO/ - - IF NOT DEFINED NOASK ( - CHOICE /M "Do you want to build this configuration? " /c YN - IF !ERRORLEVEL! NEQ 1 (EXIT /B !ERRORLEVEL!) - ) - - CD %SOURCEDIR% || EXIT /B - perl Configure !BUILDTYPE! --prefix="!OUTDIR!" !BUILD_OPTIONS! || EXIT /B - NMAKE /FMakefile install_sw || EXIT /B - NMAKE /FMakefile clean || EXIT /B - ) -) - -ENDLOCAL -EXIT /B diff --git a/build/wincpp/thirdparty/src/build-zlib.bat b/build/wincpp/thirdparty/src/build-zlib.bat deleted file mode 100644 index 2427230d0fe..00000000000 --- a/build/wincpp/thirdparty/src/build-zlib.bat +++ /dev/null @@ -1,75 +0,0 @@ -:: -:: Licensed under the Apache License, Version 2.0 (the "License"); -:: you may not use this file except in compliance with the License. -:: You may obtain a copy of the License at -:: -:: http://www.apache.org/licenses/LICENSE-2.0 -:: -:: Unless required by applicable law or agreed to in writing, software -:: distributed under the License is distributed on an "AS IS" BASIS, -:: WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -:: See the License for the specific language governing permissions and -:: limitations under the License. -:: - -:: -:: Build script for zlib on windows. -:: Uses the environment set up by a Visual Studio Command Prompt shortcut -:: to target a specific architecture and compiler. -:: - -@ECHO OFF -SETLOCAL EnableDelayedExpansion - -:: Sets variables for third party versions used in build -CALL ..\..\scripts\tpversions.bat || EXIT /B - -:: use "build-zlib.bat /yes" to skip the question part -IF /I "%1" == "/YES" SET NOASK=1 - -IF NOT DEFINED GENERATOR SET GENERATOR=NMake Makefiles -IF NOT DEFINED PACKAGE_NAME SET PACKAGE_NAME=zlib -IF NOT DEFINED PACKAGE_VERSION SET PACKAGE_VERSION=%TP_ZLIB_VERSION% -IF NOT DEFINED SOURCE_DIR SET SOURCEDIR=%~dp0%PACKAGE_NAME%-%PACKAGE_VERSION% -IF NOT DEFINED WIN3P_ROOT SET WIN3P_ROOT=%~dp0.. - -:: Set COMPILER to (vc100 - vc140) depending on the current environment -CALL ..\..\scripts\cl_setcompiler.bat || EXIT /B - -:: Set ARCH to either win32 or x64 depending on the current environment -CALL ..\..\scripts\cl_setarch.bat || EXIT /B - -FOR %%X IN ( - Debug - Release -) DO ( - SET BUILDTYPE=%%X - SET BUILDDIR=%WIN3P_ROOT%\build\%PACKAGE_NAME%\%PACKAGE_VERSION%\%COMPILER%\%ARCH%\!BUILDTYPE! - SET OUTDIR=%WIN3P_ROOT%\dist\%PACKAGE_NAME%-%PACKAGE_VERSION%\%COMPILER%\%ARCH% - - ECHO/ - ECHO ========================================================================= - ECHO Building: %PACKAGE_NAME% v%PACKAGE_VERSION% %COMPILER%:%ARCH%:!BUILDTYPE! "%GENERATOR%" - ECHO Build Directory: !BUILDDIR! - ECHO Install Directory: !OUTDIR! - ECHO Source Directory: %SOURCEDIR% - ECHO ========================================================================= - ECHO/ - - IF NOT DEFINED NOASK ( - CHOICE /M "Do you want to build this configuration? " /c YN - IF !ERRORLEVEL! NEQ 1 (EXIT /B !ERRORLEVEL!) - ) - - MKDIR "!BUILDDIR!" - CD "!BUILDDIR!" || EXIT /B - - CMAKE.EXE -G"%GENERATOR%" -DCMAKE_INSTALL_PREFIX=!OUTDIR! -DCMAKE_BUILD_TYPE=!BUILDTYPE! "%SOURCEDIR%" || EXIT /B - NMAKE /fMakefile install || EXIT /B - - IF "!BUILDTYPE!" == "Debug" ( - COPY "!BUILDDIR!\zlibd.pdb" "!OUTDIR!\bin\" || EXIT /B - ) -) - -ENDLOCAL diff --git a/cleanup.sh b/cleanup.sh deleted file mode 100755 index f110721ac9f..00000000000 --- a/cleanup.sh +++ /dev/null @@ -1,89 +0,0 @@ -#!/bin/sh - -# -# Licensed to the Apache Software Foundation (ASF) under one -# or more contributor license agreements. See the NOTICE file -# distributed with this work for additional information -# regarding copyright ownership. The ASF licenses this file -# to you under the Apache License, Version 2.0 (the -# "License"); you may not use this file except in compliance -# with the License. You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, -# software distributed under the License is distributed on an -# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -# KIND, either express or implied. See the License for the -# specific language governing permissions and limitations -# under the License. -# - -topsrcdir="`dirname $0`" -cd "$topsrcdir" - -make -k clean >/dev/null 2>&1 -make -k distclean >/dev/null 2>&1 -find . -name Makefile.in -exec rm -f {} \; -rm -rf \ -AUTHORS \ -ChangeLog \ -INSTALL \ -Makefile \ -Makefile.in \ -Makefile.orig \ -aclocal/libtool.m4 \ -aclocal/ltoptions.m4 \ -aclocal/ltsugar.m4 \ -aclocal/ltversion.m4 \ -aclocal/lt~obsolete.m4 \ -aclocal.m4 \ -autom4te.cache \ -autoscan.log \ -config.guess \ -config.h \ -config.hin \ -config.hin~ \ -config.log \ -config.status \ -config.status.lineno \ -config.sub \ -configure \ -configure.lineno \ -configure.scan \ -depcomp \ -.deps \ -install-sh \ -.libs \ -libtool \ -ltmain.sh \ -missing \ -ylwrap \ -if/gen-* \ -test/gen-* \ -lib/php/src/ext/thrift_protocol/.deps \ -lib/php/src/ext/thrift_protocol/Makefile \ -lib/php/src/ext/thrift_protocol/Makefile.fragments \ -lib/php/src/ext/thrift_protocol/Makefile.global \ -lib/php/src/ext/thrift_protocol/Makefile.objects \ -lib/php/src/ext/thrift_protocol/acinclude.m4 \ -lib/php/src/ext/thrift_protocol/aclocal.m4 \ -lib/php/src/ext/thrift_protocol/autom4te.cache \ -lib/php/src/ext/thrift_protocol/build \ -lib/php/src/ext/thrift_protocol/config.guess \ -lib/php/src/ext/thrift_protocol/config.h \ -lib/php/src/ext/thrift_protocol/config.h.in \ -lib/php/src/ext/thrift_protocol/config.log \ -lib/php/src/ext/thrift_protocol/config.nice \ -lib/php/src/ext/thrift_protocol/config.status \ -lib/php/src/ext/thrift_protocol/config.sub \ -lib/php/src/ext/thrift_protocol/configure \ -lib/php/src/ext/thrift_protocol/configure.in \ -lib/php/src/ext/thrift_protocol/include \ -lib/php/src/ext/thrift_protocol/install-sh \ -lib/php/src/ext/thrift_protocol/libtool \ -lib/php/src/ext/thrift_protocol/ltmain.sh \ -lib/php/src/ext/thrift_protocol/missing \ -lib/php/src/ext/thrift_protocol/mkinstalldirs \ -lib/php/src/ext/thrift_protocol/modules \ -lib/php/src/ext/thrift_protocol/run-tests.php diff --git a/compiler/cpp/CMakeLists.txt b/compiler/cpp/CMakeLists.txt index 0df790ee2d1..0675f0eaa52 100644 --- a/compiler/cpp/CMakeLists.txt +++ b/compiler/cpp/CMakeLists.txt @@ -16,18 +16,12 @@ # specific language governing permissions and limitations # under the License. # -cmake_minimum_required(VERSION 2.8.12) - -configure_file(${CMAKE_CURRENT_SOURCE_DIR}/src/thrift/version.h.in ${CMAKE_CURRENT_BINARY_DIR}/thrift/version.h) -if(MSVC) - # The winflexbison generator outputs some macros that conflict with the Visual Studio 2010 copy of stdint.h - # This might be fixed in later versions of Visual Studio, but an easy solution is to include stdint.h first - if(HAVE_STDINT_H) - add_definitions(-D__STDC_FORMAT_MACROS) - add_definitions(-D__STDC_LIMIT_MACROS) - add_definitions(/FI"stdint.h") - endif(HAVE_STDINT_H) -endif() + +cmake_minimum_required(VERSION 3.3) +project("thrift-compiler" VERSION ${PACKAGE_VERSION}) + +# version.h now handled via veralign.sh +#configure_file(${CMAKE_CURRENT_SOURCE_DIR}/src/thrift/version.h.in ${CMAKE_CURRENT_BINARY_DIR}/thrift/version.h) find_package(FLEX REQUIRED) find_package(BISON REQUIRED) @@ -54,7 +48,7 @@ set(compiler_core src/thrift/generate/t_generator.cc src/thrift/parse/t_typedef.cc src/thrift/parse/parse.cc - ${CMAKE_CURRENT_BINARY_DIR}/thrift/version.h + src/thrift/version.h ) set(thrift-compiler_SOURCES @@ -78,143 +72,58 @@ macro(THRIFT_ADD_COMPILER name description initial) endmacro() # The following compiler can be enabled or disabled +THRIFT_ADD_COMPILER(as3 "Enable compiler for ActionScript 3" ON) THRIFT_ADD_COMPILER(c_glib "Enable compiler for C with Glib" ON) +THRIFT_ADD_COMPILER(cl "Enable compiler for Common LISP" ON) THRIFT_ADD_COMPILER(cpp "Enable compiler for C++" ON) -THRIFT_ADD_COMPILER(java "Enable compiler for Java" ON) -THRIFT_ADD_COMPILER(as3 "Enable compiler for ActionScript 3" ON) +THRIFT_ADD_COMPILER(d "Enable compiler for D" ON) THRIFT_ADD_COMPILER(dart "Enable compiler for Dart" ON) -THRIFT_ADD_COMPILER(haxe "Enable compiler for Haxe" ON) -THRIFT_ADD_COMPILER(csharp "Enable compiler for C#" ON) -THRIFT_ADD_COMPILER(netcore "Enable compiler for .NET Core" ON) -THRIFT_ADD_COMPILER(py "Enable compiler for Python 2.0" ON) -THRIFT_ADD_COMPILER(rb "Enable compiler for Ruby" ON) -THRIFT_ADD_COMPILER(perl "Enable compiler for Perl" ON) -THRIFT_ADD_COMPILER(php "Enable compiler for PHP" ON) +THRIFT_ADD_COMPILER(delphi "Enable compiler for Delphi" ON) THRIFT_ADD_COMPILER(erl "Enable compiler for Erlang" ON) -THRIFT_ADD_COMPILER(cocoa "Enable compiler for Cocoa Objective-C" ON) -THRIFT_ADD_COMPILER(swift "Enable compiler for Cocoa Swift" ON) -THRIFT_ADD_COMPILER(st "Enable compiler for Smalltalk" ON) -THRIFT_ADD_COMPILER(ocaml "Enable compiler for OCaml" ON) +THRIFT_ADD_COMPILER(go "Enable compiler for Go" ON) +THRIFT_ADD_COMPILER(gv "Enable compiler for GraphViz" ON) +THRIFT_ADD_COMPILER(haxe "Enable compiler for Haxe" ON) THRIFT_ADD_COMPILER(hs "Enable compiler for Haskell" ON) -THRIFT_ADD_COMPILER(xsd "Enable compiler for XSD" ON) THRIFT_ADD_COMPILER(html "Enable compiler for HTML Documentation" ON) +THRIFT_ADD_COMPILER(markdown "Enable compiler for Markdown Documentation" ON) +THRIFT_ADD_COMPILER(java "Enable compiler for Java" ON) +THRIFT_ADD_COMPILER(javame "Enable compiler for Java ME" ON) THRIFT_ADD_COMPILER(js "Enable compiler for JavaScript" ON) THRIFT_ADD_COMPILER(json "Enable compiler for JSON" ON) -THRIFT_ADD_COMPILER(javame "Enable compiler for Java ME" ON) -THRIFT_ADD_COMPILER(delphi "Enable compiler for Delphi" ON) -THRIFT_ADD_COMPILER(go "Enable compiler for Go" ON) -THRIFT_ADD_COMPILER(d "Enable compiler for D" ON) THRIFT_ADD_COMPILER(lua "Enable compiler for Lua" ON) -THRIFT_ADD_COMPILER(gv "Enable compiler for GraphViz" ON) +THRIFT_ADD_COMPILER(netstd "Enable compiler for .NET Standard" ON) +THRIFT_ADD_COMPILER(ocaml "Enable compiler for OCaml" ON) +THRIFT_ADD_COMPILER(perl "Enable compiler for Perl" ON) +THRIFT_ADD_COMPILER(php "Enable compiler for PHP" ON) +THRIFT_ADD_COMPILER(py "Enable compiler for Python 2.0" ON) +THRIFT_ADD_COMPILER(rb "Enable compiler for Ruby" ON) THRIFT_ADD_COMPILER(rs "Enable compiler for Rust" ON) +THRIFT_ADD_COMPILER(st "Enable compiler for Smalltalk" ON) +THRIFT_ADD_COMPILER(swift "Enable compiler for Cocoa Swift" ON) THRIFT_ADD_COMPILER(xml "Enable compiler for XML" ON) +THRIFT_ADD_COMPILER(xsd "Enable compiler for XSD" ON) # Thrift is looking for include files in the src directory # we also add the current binary directory for generated files include_directories(${CMAKE_CURRENT_BINARY_DIR} src) -if(NOT DEFINED WITH_PLUGIN OR NOT ${WITH_PLUGIN}) - list(APPEND thrift-compiler_SOURCES ${compiler_core}) -endif() +list(APPEND thrift-compiler_SOURCES ${compiler_core}) add_executable(thrift-compiler ${thrift-compiler_SOURCES}) -if(${WITH_PLUGIN}) - add_executable(thrift-bootstrap ${compiler_core} - src/thrift/main.cc - src/thrift/audit/t_audit.cpp - src/thrift/generate/t_cpp_generator.cc - ) - target_link_libraries(thrift-bootstrap parse) - - set(PLUGIN_GEN_SOURCES - ${CMAKE_CURRENT_BINARY_DIR}/thrift/plugin/plugin_types.h - ${CMAKE_CURRENT_BINARY_DIR}/thrift/plugin/plugin_types.cpp - ${CMAKE_CURRENT_BINARY_DIR}/thrift/plugin/plugin_constants.h - ${CMAKE_CURRENT_BINARY_DIR}/thrift/plugin/plugin_constants.cpp - ) - - file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/thrift/plugin) - add_custom_command(OUTPUT ${PLUGIN_GEN_SOURCES} - DEPENDS thrift-bootstrap src/thrift/plugin/plugin.thrift - COMMAND thrift-bootstrap -gen cpp - -out ${CMAKE_CURRENT_BINARY_DIR}/thrift/plugin - ${CMAKE_CURRENT_SOURCE_DIR}/src/thrift/plugin/plugin.thrift - ) - - include_directories(../../lib/cpp/src) - - include(ThriftMacros) - ADD_LIBRARY_THRIFT(thriftc - ${compiler_core} - ${PLUGIN_GEN_SOURCES} - src/thrift/logging.cc - src/thrift/plugin/plugin_output.cc - src/thrift/plugin/plugin.cc - ) - TARGET_INCLUDE_DIRECTORIES_THRIFT(thriftc PUBLIC ${Boost_INCLUDE_DIRS}) - TARGET_LINK_LIBRARIES_THRIFT_AGAINST_THRIFT_LIBRARY(thriftc thrift PUBLIC) - target_compile_definitions(thrift-compiler PUBLIC THRIFT_ENABLE_PLUGIN) - LINK_AGAINST_THRIFT_LIBRARY(thrift-compiler thriftc) -endif() - set_target_properties(thrift-compiler PROPERTIES RUNTIME_OUTPUT_DIRECTORY bin/) set_target_properties(thrift-compiler PROPERTIES OUTPUT_NAME thrift) target_link_libraries(thrift-compiler parse) -install(TARGETS thrift-compiler DESTINATION bin) +add_custom_command(OUTPUT "${CMAKE_CURRENT_SOURCE_DIR}/thrift${CMAKE_EXECUTABLE_SUFFIX}" + DEPENDS thrift-compiler + COMMAND ${CMAKE_COMMAND} -E copy "$" "${CMAKE_CURRENT_SOURCE_DIR}/" + COMMENT "Copying the thrift compiler to the source tree for use by downstream targets") +add_custom_target(copy-thrift-compiler + DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/thrift${CMAKE_EXECUTABLE_SUFFIX}") -if(${WITH_PLUGIN}) - # Install the headers - install(FILES - "src/thrift/common.h" - "src/thrift/globals.h" - "src/thrift/logging.h" - "src/thrift/main.h" - "src/thrift/platform.h" - "${CMAKE_BINARY_DIR}/compiler/cpp/thrift/version.h" - DESTINATION "${INCLUDE_INSTALL_DIR}/thrift") - install(FILES - "src/thrift/audit/t_audit.h" - DESTINATION "${INCLUDE_INSTALL_DIR}/thrift/audit") - install(FILES - "src/thrift/generate/t_generator.h" - "src/thrift/generate/t_generator_registry.h" - "src/thrift/generate/t_html_generator.h" - "src/thrift/generate/t_oop_generator.h" - DESTINATION "${INCLUDE_INSTALL_DIR}/thrift/generate") - install(FILES - "src/thrift/parse/t_base_type.h" - "src/thrift/parse/t_const.h" - "src/thrift/parse/t_const_value.h" - "src/thrift/parse/t_container.h" - "src/thrift/parse/t_doc.h" - "src/thrift/parse/t_enum.h" - "src/thrift/parse/t_enum_value.h" - "src/thrift/parse/t_field.h" - "src/thrift/parse/t_function.h" - "src/thrift/parse/t_list.h" - "src/thrift/parse/t_map.h" - "src/thrift/parse/t_program.h" - "src/thrift/parse/t_scope.h" - "src/thrift/parse/t_service.h" - "src/thrift/parse/t_set.h" - "src/thrift/parse/t_struct.h" - "src/thrift/parse/t_typedef.h" - "src/thrift/parse/t_type.h" - DESTINATION "${INCLUDE_INSTALL_DIR}/thrift/parse") - install(FILES - "src/thrift/plugin/plugin.h" - "src/thrift/plugin/plugin_output.h" - "src/thrift/plugin/type_util.h" - DESTINATION "${INCLUDE_INSTALL_DIR}/thrift/plugin") -if(MSVC) - install(FILES - "src/thrift/windows/config.h" - DESTINATION "${INCLUDE_INSTALL_DIR}/thrift/windows") -endif() -endif() +install(TARGETS thrift-compiler DESTINATION bin) if(BUILD_TESTING) add_subdirectory(test) diff --git a/compiler/cpp/Makefile.am b/compiler/cpp/Makefile.am index 0b8ef2e7e19..05c9121716b 100644 --- a/compiler/cpp/Makefile.am +++ b/compiler/cpp/Makefile.am @@ -21,14 +21,9 @@ # Please see doc/old-thrift-license.txt in the Thrift distribution for # details. -AUTOMAKE_OPTIONS = subdir-objects +AUTOMAKE_OPTIONS = subdir-objects nostdinc -# Note on why we have src/thrift and src/thrift/plugin directories: -# Since Automake supports only one set of BUILT_SOURCES per file and does not allow -# SUBDIRS built before BUILT_SOURCES, we end up separate Makefile.am for each source -# code generation, i.e. lex-yacc and Thrift, to achieve stable parallel make. - -SUBDIRS = src src/thrift/plugin . +SUBDIRS = src . if WITH_TESTS SUBDIRS += test endif @@ -37,155 +32,79 @@ bin_PROGRAMS = thrift thrift_OBJDIR = obj -plugin_gen = src/thrift/plugin/plugin_types.h \ - src/thrift/plugin/plugin_types.cpp \ - src/thrift/plugin/plugin_constants.h \ - src/thrift/plugin/plugin_constants.cpp - -compiler_core = src/thrift/common.h \ +thrift_SOURCES = src/thrift/audit/t_audit.cpp \ + src/thrift/audit/t_audit.h \ src/thrift/common.cc \ + src/thrift/common.h \ src/thrift/generate/t_generator.cc \ + src/thrift/generate/t_generator.h \ src/thrift/generate/t_generator_registry.h \ + src/thrift/generate/t_html_generator.h \ + src/thrift/generate/t_oop_generator.h \ src/thrift/globals.h \ - src/thrift/platform.h \ src/thrift/logging.h \ - src/thrift/parse/t_doc.h \ - src/thrift/parse/t_type.h \ + src/thrift/main.cc \ + src/thrift/main.h \ + src/thrift/version.h \ + src/thrift/parse/parse.cc \ src/thrift/parse/t_base_type.h \ + src/thrift/parse/t_const.h \ + src/thrift/parse/t_const_value.h \ + src/thrift/parse/t_container.h \ + src/thrift/parse/t_doc.h \ src/thrift/parse/t_enum.h \ src/thrift/parse/t_enum_value.h \ - src/thrift/parse/t_typedef.h \ - src/thrift/parse/t_typedef.cc \ - src/thrift/parse/t_container.h \ - src/thrift/parse/t_list.h \ - src/thrift/parse/t_set.h \ - src/thrift/parse/t_map.h \ - src/thrift/parse/t_struct.h \ src/thrift/parse/t_field.h \ - src/thrift/parse/t_service.h \ src/thrift/parse/t_function.h \ + src/thrift/parse/t_list.h \ + src/thrift/parse/t_map.h \ src/thrift/parse/t_program.h \ src/thrift/parse/t_scope.h \ - src/thrift/parse/t_const.h \ - src/thrift/parse/t_const_value.h \ - src/thrift/parse/parse.cc \ - src/thrift/generate/t_generator.h \ - src/thrift/generate/t_oop_generator.h \ - src/thrift/generate/t_html_generator.h - -thrift_SOURCES = src/thrift/main.h \ - src/thrift/main.cc \ - src/thrift/audit/t_audit.cpp \ - src/thrift/audit/t_audit.h + src/thrift/parse/t_service.h \ + src/thrift/parse/t_set.h \ + src/thrift/parse/t_struct.h \ + src/thrift/parse/t_type.h \ + src/thrift/parse/t_typedef.cc \ + src/thrift/parse/t_typedef.h \ + src/thrift/platform.h # Specific client generator source -thrift_SOURCES += src/thrift/generate/t_c_glib_generator.cc \ +thrift_SOURCES += src/thrift/generate/t_as3_generator.cc \ + src/thrift/generate/t_c_glib_generator.cc \ + src/thrift/generate/t_cl_generator.cc \ src/thrift/generate/t_cpp_generator.cc \ - src/thrift/generate/t_java_generator.cc \ - src/thrift/generate/t_json_generator.cc \ - src/thrift/generate/t_as3_generator.cc \ + src/thrift/generate/t_d_generator.cc \ src/thrift/generate/t_dart_generator.cc \ - src/thrift/generate/t_haxe_generator.cc \ - src/thrift/generate/t_csharp_generator.cc \ - src/thrift/generate/t_netcore_generator.cc \ - src/thrift/generate/t_netcore_generator.h \ - src/thrift/generate/t_py_generator.cc \ - src/thrift/generate/t_rb_generator.cc \ - src/thrift/generate/t_perl_generator.cc \ - src/thrift/generate/t_php_generator.cc \ + src/thrift/generate/t_delphi_generator.cc \ src/thrift/generate/t_erl_generator.cc \ - src/thrift/generate/t_cocoa_generator.cc \ - src/thrift/generate/t_swift_generator.cc \ - src/thrift/generate/t_st_generator.cc \ - src/thrift/generate/t_ocaml_generator.cc \ + src/thrift/generate/t_go_generator.cc \ + src/thrift/generate/t_gv_generator.cc \ + src/thrift/generate/t_haxe_generator.cc \ src/thrift/generate/t_hs_generator.cc \ - src/thrift/generate/t_xsd_generator.cc \ - src/thrift/generate/t_xml_generator.cc \ src/thrift/generate/t_html_generator.cc \ - src/thrift/generate/t_js_generator.cc \ + src/thrift/generate/t_markdown_generator.cc \ + src/thrift/generate/t_java_generator.cc \ src/thrift/generate/t_javame_generator.cc \ - src/thrift/generate/t_delphi_generator.cc \ - src/thrift/generate/t_go_generator.cc \ - src/thrift/generate/t_gv_generator.cc \ - src/thrift/generate/t_d_generator.cc \ + src/thrift/generate/t_js_generator.cc \ + src/thrift/generate/t_json_generator.cc \ src/thrift/generate/t_lua_generator.cc \ + src/thrift/generate/t_netstd_generator.cc \ + src/thrift/generate/t_netstd_generator.h \ + src/thrift/generate/t_ocaml_generator.cc \ + src/thrift/generate/t_perl_generator.cc \ + src/thrift/generate/t_php_generator.cc \ + src/thrift/generate/t_py_generator.cc \ + src/thrift/generate/t_rb_generator.cc \ src/thrift/generate/t_rs_generator.cc \ - src/thrift/generate/t_cl_generator.cc + src/thrift/generate/t_st_generator.cc \ + src/thrift/generate/t_swift_generator.cc \ + src/thrift/generate/t_xml_generator.cc \ + src/thrift/generate/t_xsd_generator.cc thrift_CPPFLAGS = -I$(srcdir)/src thrift_CXXFLAGS = -Wall -Wextra -pedantic -Werror thrift_LDADD = @LEXLIB@ src/thrift/libparse.a -if !WITH_PLUGIN -thrift_SOURCES += $(compiler_core) -else - -lib_LTLIBRARIES = libthriftc.la - -thrift_CPPFLAGS += -DTHRIFT_ENABLE_PLUGIN=1 -thrift_LDADD += libthriftc.la - -nodist_libthriftc_la_SOURCES = $(plugin_gen) -libthriftc_la_SOURCES = $(compiler_core) \ - src/thrift/plugin/type_util.h \ - src/thrift/plugin/plugin.h \ - src/thrift/plugin/plugin.cc \ - src/thrift/plugin/plugin_output.h \ - src/thrift/plugin/plugin_output.cc \ - src/thrift/plugin/plugin.thrift \ - src/thrift/logging.cc - - -libthriftc_la_CPPFLAGS = -I$(srcdir)/src -Isrc -I$(top_builddir)/lib/cpp/src -DTHRIFT_ENABLE_PLUGIN=1 -libthriftc_la_CXXFLAGS = -Wall -Wextra -pedantic -libthriftc_la_LIBADD = $(top_builddir)/lib/cpp/libthrift.la - -include_thriftdir = $(includedir)/thrift -include_thrift_HEADERS = src/thrift/common.h \ - src/thrift/globals.h \ - src/thrift/logging.h \ - src/thrift/main.h \ - src/thrift/platform.h \ - src/thrift/version.h - -include_auditdir = $(include_thriftdir)/windows -include_audit_HEADERS = src/thrift/audit/t_audit.h - -include_generatedir = $(include_thriftdir)/generate -include_generate_HEADERS = src/thrift/generate/t_generator.h \ - src/thrift/generate/t_generator_registry.h \ - src/thrift/generate/t_oop_generator.h \ - src/thrift/generate/t_html_generator.h - -include_parsedir = $(include_thriftdir)/parse -include_parse_HEADERS = src/thrift/parse/t_service.h \ - src/thrift/parse/t_program.h \ - src/thrift/parse/t_field.h \ - src/thrift/parse/t_scope.h \ - src/thrift/parse/t_typedef.h \ - src/thrift/parse/t_set.h \ - src/thrift/parse/t_const_value.h \ - src/thrift/parse/t_enum_value.h \ - src/thrift/parse/t_const.h \ - src/thrift/parse/t_list.h \ - src/thrift/parse/t_map.h \ - src/thrift/parse/t_container.h \ - src/thrift/parse/t_base_type.h \ - src/thrift/parse/t_enum.h \ - src/thrift/parse/t_function.h \ - src/thrift/parse/t_type.h \ - src/thrift/parse/t_doc.h \ - src/thrift/parse/t_struct.h - -include_plugindir = $(include_thriftdir)/plugin -include_plugin_HEADERS = src/thrift/plugin/plugin.h \ - src/thrift/plugin/type_util.h \ - src/thrift/plugin/plugin_output.h - -include_windowsdir = $(include_thriftdir)/windows -include_windows_HEADERS = src/thrift/windows/config.h -endif - WINDOWS_DIST = \ compiler.sln \ compiler.vcxproj \ @@ -196,12 +115,13 @@ EXTRA_DIST = \ README.md \ CMakeLists.txt \ test \ + tests \ $(WINDOWS_DIST) -clean-local: - $(RM) version.h $(plugin_gen) +#clean-local: +# $(RM) version.h -- do not delete, we need it src/thrift/main.cc: src/thrift/version.h style-local: - $(CPPSTYLE_CMD) + $(CPPSTYLE_CMD) diff --git a/compiler/cpp/README.md b/compiler/cpp/README.md index 32eac9fbc7d..8d6edacb5c1 100644 --- a/compiler/cpp/README.md +++ b/compiler/cpp/README.md @@ -67,7 +67,7 @@ brew install bison ``` mkdir cmake-build && cd cmake-build -cmake -G "Xcode" -DWITH_PLUGIN=OFF .. +cmake -G "Xcode" .. cmake --build . ``` @@ -107,7 +107,7 @@ cmake --build . ``` mkdir cmake-vs cd cmake-vs -cmake -G "Visual Studio 15 2017" -DWITH_PLUGIN=OFF .. +cmake -G "Visual Studio 15 2017" .. ``` - Now open the folder cmake-vs using Visual Studio. @@ -115,7 +115,7 @@ cmake -G "Visual Studio 15 2017" -DWITH_PLUGIN=OFF .. ``` mkdir cmake-mingw32 && cd cmake-mingw32 -cmake -DCMAKE_TOOLCHAIN_FILE=../build/cmake/mingw32-toolchain.cmake -DBUILD_COMPILER=ON -DBUILD_LIBRARIES=OFF -DBUILD_TESTING=OFF -DBUILD_EXAMPLES=OFF .. +cmake -DCMAKE_TOOLCHAIN_FILE=../build/cmake/mingw32-toolchain.cmake -DBUILD_COMPILER=ON -DBUILD_LIBRARIES=OFF -DBUILD_TESTING=OFF .. cpack ``` @@ -168,8 +168,8 @@ Build the compiler in Visual Studio. ## Using Catch C++ test library -Added generic way to cover code by tests for many languages (you just need to make a correct header file for generator for your language - example in **netcore** implementation) +Added generic way to cover code by tests for many languages (you just need to make a correct header file for generator for your language - example in **netstd** implementation) - pls check **tests** folder -# Have a Happy free time and holidays \ No newline at end of file +# Have a Happy free time and holidays diff --git a/compiler/cpp/compiler.vcxproj b/compiler/cpp/compiler.vcxproj index 0628b54c85b..dc9793f575c 100644 --- a/compiler/cpp/compiler.vcxproj +++ b/compiler/cpp/compiler.vcxproj @@ -54,11 +54,9 @@ - - - - + + @@ -69,11 +67,13 @@ - + + + @@ -171,6 +171,7 @@ WIN32;MINGW;YY_NO_UNISTD_H;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) thrift\windows\config.h CompileAsCpp + MultiThreadedDebugDLL Console @@ -189,6 +190,7 @@ WIN32;MINGW;YY_NO_UNISTD_H;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) thrift\windows\config.h CompileAsCpp + MultiThreadedDLL Console @@ -209,7 +211,7 @@ WIN32;MINGW;YY_NO_UNISTD_H;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) thrift\windows\config.h CompileAsCpp - MultiThreaded + MultiThreadedDLL Console @@ -232,7 +234,7 @@ WIN32;MINGW;YY_NO_UNISTD_H;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) thrift\windows\config.h CompileAsCpp - MultiThreaded + MultiThreadedDLL Console diff --git a/compiler/cpp/compiler.vcxproj.filters b/compiler/cpp/compiler.vcxproj.filters index b96865b51ce..360c4461ca9 100644 --- a/compiler/cpp/compiler.vcxproj.filters +++ b/compiler/cpp/compiler.vcxproj.filters @@ -101,9 +101,6 @@ generate - - generate - generate @@ -137,6 +134,9 @@ generate + + generate + generate @@ -161,6 +161,12 @@ generate + + generate + + + generate + generate diff --git a/compiler/cpp/src/Makefile.am b/compiler/cpp/src/Makefile.am index bc2c5cbacea..5111fd55038 100644 --- a/compiler/cpp/src/Makefile.am +++ b/compiler/cpp/src/Makefile.am @@ -21,7 +21,7 @@ # Please see doc/old-thrift-license.txt in the Thrift distribution for # details. -AUTOMAKE_OPTIONS = subdir-objects +AUTOMAKE_OPTIONS = subdir-objects nostdinc AM_YFLAGS = -d @@ -38,50 +38,7 @@ thrift_libparse_a_SOURCES = thrift/thrifty.yy \ clean-local: $(RM) thrift/thriftl.cc thrift/thrifty.cc thrift/thrifty.h thrift/thrifty.hh -if WITH_PLUGIN -noinst_PROGRAMS = thrift/thrift-bootstrap -thrift_thrift_bootstrap_SOURCES = \ - thrift/common.h \ - thrift/common.cc \ - thrift/audit/t_audit.h \ - thrift/audit/t_audit.cpp \ - thrift/generate/t_generator.cc \ - thrift/generate/t_generator_registry.h \ - thrift/globals.h \ - thrift/platform.h \ - thrift/logging.h \ - thrift/parse/t_doc.h \ - thrift/parse/t_type.h \ - thrift/parse/t_base_type.h \ - thrift/parse/t_enum.h \ - thrift/parse/t_enum_value.h \ - thrift/parse/t_typedef.h \ - thrift/parse/t_typedef.cc \ - thrift/parse/t_container.h \ - thrift/parse/t_list.h \ - thrift/parse/t_set.h \ - thrift/parse/t_map.h \ - thrift/parse/t_struct.h \ - thrift/parse/t_field.h \ - thrift/parse/t_service.h \ - thrift/parse/t_function.h \ - thrift/parse/t_program.h \ - thrift/parse/t_scope.h \ - thrift/parse/t_const.h \ - thrift/parse/t_const_value.h \ - thrift/parse/parse.cc \ - thrift/generate/t_generator.h \ - thrift/generate/t_oop_generator.h \ - thrift/generate/t_html_generator.h \ - thrift/windows/config.h \ - thrift/version.h \ - thrift/generate/t_cpp_generator.cc \ - thrift/main.h \ - thrift/main.cc - -main.cc: version.h - -thrift_thrift_bootstrap_CXXFLAGS = -Wall -Wextra -pedantic -thrift_thrift_bootstrap_LDADD = @LEXLIB@ thrift/libparse.a -endif +EXTRA_DIST = \ + thrift/logging.cc \ + thrift/windows/config.h diff --git a/compiler/cpp/src/thrift/audit/t_audit.cpp b/compiler/cpp/src/thrift/audit/t_audit.cpp index ef39d60c302..fbce15c7758 100644 --- a/compiler/cpp/src/thrift/audit/t_audit.cpp +++ b/compiler/cpp/src/thrift/audit/t_audit.cpp @@ -54,18 +54,16 @@ void compare_namespace(t_program* newProgram, t_program* oldProgram) const std::map& newNamespaceMap = newProgram->get_all_namespaces(); const std::map& oldNamespaceMap = oldProgram->get_all_namespaces(); - for(std::map::const_iterator oldNamespaceMapIt = oldNamespaceMap.begin(); - oldNamespaceMapIt != oldNamespaceMap.end(); - oldNamespaceMapIt++) + for(const auto & oldNamespaceMapIt : oldNamespaceMap) { - std::map::const_iterator newNamespaceMapIt = newNamespaceMap.find(oldNamespaceMapIt->first); + auto newNamespaceMapIt = newNamespaceMap.find(oldNamespaceMapIt.first); if(newNamespaceMapIt == newNamespaceMap.end()) { - thrift_audit_warning(1, "Language %s not found in new thrift file\n", (oldNamespaceMapIt->first).c_str()); + thrift_audit_warning(1, "Language %s not found in new thrift file\n", (oldNamespaceMapIt.first).c_str()); } - else if((newNamespaceMapIt->second) != oldNamespaceMapIt->second) + else if((newNamespaceMapIt->second) != oldNamespaceMapIt.second) { - thrift_audit_warning(1, "Namespace %s changed in new thrift file\n", (oldNamespaceMapIt->second).c_str()); + thrift_audit_warning(1, "Namespace %s changed in new thrift file\n", (oldNamespaceMapIt.second).c_str()); } } } @@ -73,15 +71,13 @@ void compare_namespace(t_program* newProgram, t_program* oldProgram) void compare_enum_values(t_enum* newEnum,t_enum* oldEnum) { const std::vector& oldEnumValues = oldEnum->get_constants(); - for(std::vector::const_iterator oldEnumValuesIt = oldEnumValues.begin(); - oldEnumValuesIt != oldEnumValues.end(); - oldEnumValuesIt++) + for(auto oldEnumValue : oldEnumValues) { - int enumValue = (*oldEnumValuesIt)->get_value(); + int enumValue = oldEnumValue->get_value(); t_enum_value* newEnumValue = newEnum->get_constant_by_value(enumValue); - if(newEnumValue != NULL) + if(newEnumValue != nullptr) { - std::string enumName = (*oldEnumValuesIt)->get_name(); + std::string enumName = oldEnumValue->get_name(); if(enumName != newEnumValue->get_name()) { thrift_audit_warning(1, "Name of the value %d changed in enum %s\n", enumValue, oldEnum->get_name().c_str()); @@ -175,9 +171,9 @@ bool compare_pair(std::pair newMapPair, std::pai // This function returns 'true' if the default values are same. Returns false if they are different. bool compare_defaults(t_const_value* newStructDefault, t_const_value* oldStructDefault) { - if(newStructDefault == NULL && oldStructDefault == NULL) return true; - else if(newStructDefault == NULL && oldStructDefault != NULL) return false; - else if (newStructDefault != NULL && oldStructDefault == NULL) return false; + if(newStructDefault == nullptr && oldStructDefault == nullptr) return true; + else if(newStructDefault == nullptr && oldStructDefault != nullptr) return false; + else if (newStructDefault != nullptr && oldStructDefault == nullptr) return false; if(newStructDefault->get_type() != oldStructDefault->get_type()) { @@ -255,8 +251,8 @@ void compare_single_struct(t_struct* newStruct, t_struct* oldStruct, const std:: std::string structName = oldStructName.empty() ? oldStruct->get_name() : oldStructName; const std::vector& oldStructMembersInIdOrder = oldStruct->get_sorted_members(); const std::vector& newStructMembersInIdOrder = newStruct->get_sorted_members(); - std::vector::const_iterator oldStructMemberIt = oldStructMembersInIdOrder.begin(); - std::vector::const_iterator newStructMemberIt = newStructMembersInIdOrder.begin(); + auto oldStructMemberIt = oldStructMembersInIdOrder.begin(); + auto newStructMemberIt = newStructMembersInIdOrder.begin(); // Since we have the struct members in their ID order, comparing their IDs can be done by traversing the two member // lists together. @@ -352,27 +348,23 @@ void compare_functions(const std::vector& newFunctionList, const st { std::map newFunctionMap; std::map::iterator newFunctionMapIt; - for(std::vector::const_iterator newFunctionIt = newFunctionList.begin(); - newFunctionIt != newFunctionList.end(); - newFunctionIt++) + for(auto newFunctionIt : newFunctionList) { - newFunctionMap[(*newFunctionIt)->get_name()] = *newFunctionIt; + newFunctionMap[newFunctionIt->get_name()] = newFunctionIt; } - for(std::vector::const_iterator oldFunctionIt = oldFunctionList.begin(); - oldFunctionIt != oldFunctionList.end(); - oldFunctionIt++) + for(auto oldFunctionIt : oldFunctionList) { - newFunctionMapIt = newFunctionMap.find((*oldFunctionIt)->get_name()); + newFunctionMapIt = newFunctionMap.find(oldFunctionIt->get_name()); if(newFunctionMapIt == newFunctionMap.end()) { - thrift_audit_failure("New Thrift File has missing function %s\n",(*oldFunctionIt)->get_name().c_str()); + thrift_audit_failure("New Thrift File has missing function %s\n",oldFunctionIt->get_name().c_str()); continue; } else { //Function is found in both thrift files. Compare return type and argument list - compare_single_function(newFunctionMapIt->second, *oldFunctionIt); + compare_single_function(newFunctionMapIt->second, oldFunctionIt); } } @@ -383,18 +375,16 @@ void compare_services(const std::vector& newServices, const std::vec std::vector::const_iterator oldServiceIt; std::map newServiceMap; - for(std::vector::const_iterator newServiceIt = newServices.begin(); - newServiceIt != newServices.end(); - newServiceIt++) + for(auto newService : newServices) { - newServiceMap[(*newServiceIt)->get_name()] = *newServiceIt; + newServiceMap[newService->get_name()] = newService; } for(oldServiceIt = oldServices.begin(); oldServiceIt != oldServices.end(); oldServiceIt++) { const std::string oldServiceName = (*oldServiceIt)->get_name(); - std::map::iterator newServiceMapIt = newServiceMap.find(oldServiceName); + auto newServiceMapIt = newServiceMap.find(oldServiceName); if(newServiceMapIt == newServiceMap.end()) { @@ -405,12 +395,12 @@ void compare_services(const std::vector& newServices, const std::vec t_service* oldServiceExtends = (*oldServiceIt)->get_extends(); t_service* newServiceExtends = (newServiceMapIt->second)->get_extends(); - if(oldServiceExtends == NULL) + if(oldServiceExtends == nullptr) { // It is fine to add extends. So if service in older thrift did not have any extends, we are fine. // DO Nothing } - else if(oldServiceExtends != NULL && newServiceExtends == NULL) + else if(oldServiceExtends != nullptr && newServiceExtends == nullptr) { thrift_audit_failure("Change in Service inheritance for %s\n", oldServiceName.c_str()); } diff --git a/compiler/cpp/src/thrift/common.cc b/compiler/cpp/src/thrift/common.cc index 3a2b9d359ec..fb1832daf7e 100644 --- a/compiler/cpp/src/thrift/common.cc +++ b/compiler/cpp/src/thrift/common.cc @@ -57,10 +57,6 @@ void clearGlobals() { delete g_type_double; } -/** - * Those are not really needed for plugins but causes link errors without - */ - /** * The location of the last parsed doctext comment. */ diff --git a/compiler/cpp/src/thrift/generate/t_as3_generator.cc b/compiler/cpp/src/thrift/generate/t_as3_generator.cc index 41724fe4999..fa2967be944 100644 --- a/compiler/cpp/src/thrift/generate/t_as3_generator.cc +++ b/compiler/cpp/src/thrift/generate/t_as3_generator.cc @@ -68,20 +68,20 @@ class t_as3_generator : public t_oop_generator { * Init and close methods */ - void init_generator(); - void close_generator(); + void init_generator() override; + void close_generator() override; - void generate_consts(std::vector consts); + void generate_consts(std::vector consts) override; /** * Program-level generation functions */ - void generate_typedef(t_typedef* ttypedef); - void generate_enum(t_enum* tenum); - void generate_struct(t_struct* tstruct); - void generate_xception(t_struct* txception); - void generate_service(t_service* tservice); + void generate_typedef(t_typedef* ttypedef) override; + void generate_enum(t_enum* tenum) override; + void generate_struct(t_struct* tstruct) override; + void generate_xception(t_struct* txception) override; + void generate_service(t_service* tservice) override; void print_const_value(std::ostream& out, std::string name, @@ -191,7 +191,7 @@ class t_as3_generator : public t_oop_generator { std::string function_signature(t_function* tfunction, std::string prefix = ""); std::string argument_list(t_struct* tstruct); std::string type_to_enum(t_type* ttype); - std::string get_enum_class_name(t_type* type); + std::string get_enum_class_name(t_type* type) override; bool type_can_be_null(t_type* ttype) { ttype = get_true_type(ttype); @@ -286,7 +286,7 @@ string t_as3_generator::as3_thrift_gen_imports(t_struct* tstruct, string& import // For each type check if it is from a differnet namespace for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) { t_program* program = (*m_iter)->get_type()->get_program(); - if (program != NULL && program != program_) { + if (program != nullptr && program != program_) { string package = program->get_namespace("as3"); if (!package.empty()) { if (imports.find(package + "." + (*m_iter)->get_type()->get_name()) == string::npos) { @@ -311,7 +311,7 @@ string t_as3_generator::as3_thrift_gen_imports(t_service* tservice) { // For each type check if it is from a differnet namespace for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) { t_program* program = (*f_iter)->get_returntype()->get_program(); - if (program != NULL && program != program_) { + if (program != nullptr && program != program_) { string package = program->get_namespace("as3"); if (!package.empty()) { if (imports.find(package + "." + (*f_iter)->get_returntype()->get_name()) == string::npos) { @@ -482,13 +482,13 @@ void t_as3_generator::print_const_value(std::ostream& out, indent_up(); } for (v_iter = val.begin(); v_iter != val.end(); ++v_iter) { - t_type* field_type = NULL; + t_type* field_type = nullptr; for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { if ((*f_iter)->get_name() == v_iter->first->get_string()) { field_type = (*f_iter)->get_type(); } } - if (field_type == NULL) { + if (field_type == nullptr) { throw "type error: " + type->get_name() + " has no field " + v_iter->first->get_string(); } string val = render_const_value(out, name, field_type, v_iter->second); @@ -753,7 +753,7 @@ void t_as3_generator::generate_as3_struct_definition(ostream& out, indent(out) << "public function " << tstruct->get_name() << "() {" << endl; indent_up(); for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) { - if ((*m_iter)->get_value() != NULL) { + if ((*m_iter)->get_value() != nullptr) { indent(out) << "this._" << (*m_iter)->get_name() << " = " << (*m_iter)->get_value()->get_integer() << ";" << endl; } @@ -1432,7 +1432,7 @@ void t_as3_generator::generate_service(t_service* tservice) { f_service_ << endl << as3_type_imports() << as3_thrift_imports() << as3_thrift_gen_imports(tservice); - if (tservice->get_extends() != NULL) { + if (tservice->get_extends() != nullptr) { t_type* parent = tservice->get_extends(); string parent_namespace = parent->get_program()->get_namespace("as3"); if (!parent_namespace.empty() && parent_namespace != package_name_) { @@ -1458,7 +1458,7 @@ void t_as3_generator::generate_service(t_service* tservice) { f_service_ << endl << as3_type_imports() << as3_thrift_imports() << as3_thrift_gen_imports(tservice); - if (tservice->get_extends() != NULL) { + if (tservice->get_extends() != nullptr) { t_type* parent = tservice->get_extends(); string parent_namespace = parent->get_program()->get_namespace("as3"); if (!parent_namespace.empty() && parent_namespace != package_name_) { @@ -1515,7 +1515,7 @@ void t_as3_generator::generate_service(t_service* tservice) { */ void t_as3_generator::generate_service_interface(t_service* tservice) { string extends_iface = ""; - if (tservice->get_extends() != NULL) { + if (tservice->get_extends() != nullptr) { extends_iface = " extends " + tservice->get_extends()->get_name(); } @@ -1566,7 +1566,7 @@ void t_as3_generator::generate_service_helpers(t_service* tservice) { void t_as3_generator::generate_service_client(t_service* tservice) { string extends = ""; string extends_client = ""; - if (tservice->get_extends() != NULL) { + if (tservice->get_extends() != nullptr) { extends = tservice->get_extends()->get_name(); extends_client = " extends " + extends + "Impl"; } @@ -1729,7 +1729,7 @@ void t_as3_generator::generate_service_server(t_service* tservice) { // Extends stuff string extends = ""; string extends_processor = ""; - if (tservice->get_extends() != NULL) { + if (tservice->get_extends() != nullptr) { extends = type_name(tservice->get_extends()); extends_processor = " extends " + extends + "Processor"; } @@ -1765,7 +1765,7 @@ void t_as3_generator::generate_service_server(t_service* tservice) { // Generate the server implementation string override = ""; - if (tservice->get_extends() != NULL) { + if (tservice->get_extends() != nullptr) { override = "override "; } indent(f_service_) << override @@ -2335,7 +2335,7 @@ string t_as3_generator::type_name(t_type* ttype, bool in_container, bool in_init // Check for namespacing t_program* program = ttype->get_program(); - if (program != NULL && program != program_) { + if (program != nullptr && program != program_) { string package = program->get_namespace("as3"); if (!package.empty()) { return package + "." + ttype->get_name(); @@ -2389,7 +2389,7 @@ string t_as3_generator::declare_field(t_field* tfield, bool init) { string result = "var " + tfield->get_name() + ":" + type_name(tfield->get_type()); if (init) { t_type* ttype = get_true_type(tfield->get_type()); - if (ttype->is_base_type() && tfield->get_value() != NULL) { + if (ttype->is_base_type() && tfield->get_value() != nullptr) { std::ofstream dummy; result += " = " + render_const_value(dummy, tfield->get_name(), ttype, tfield->get_value()); } else if (ttype->is_base_type()) { @@ -2519,9 +2519,7 @@ string t_as3_generator::constant_name(string name) { bool is_first = true; bool was_previous_char_upper = false; - for (string::iterator iter = name.begin(); iter != name.end(); ++iter) { - string::value_type character = (*iter); - + for (char character : name) { bool is_upper = isupper(character); if (is_upper && !is_first && !was_previous_char_upper) { @@ -2582,7 +2580,7 @@ void t_as3_generator::generate_isset_set(ostream& out, t_field* field) { std::string t_as3_generator::get_enum_class_name(t_type* type) { string package = ""; t_program* program = type->get_program(); - if (program != NULL && program != program_) { + if (program != nullptr && program != program_) { package = program->get_namespace("as3") + "."; } return package + type->get_name(); diff --git a/compiler/cpp/src/thrift/generate/t_c_glib_generator.cc b/compiler/cpp/src/thrift/generate/t_c_glib_generator.cc index be3bad16128..8cb82c18089 100644 --- a/compiler/cpp/src/thrift/generate/t_c_glib_generator.cc +++ b/compiler/cpp/src/thrift/generate/t_c_glib_generator.cc @@ -103,16 +103,16 @@ class t_c_glib_generator : public t_oop_generator { } /* initialization and destruction */ - void init_generator(); - void close_generator(); + void init_generator() override; + void close_generator() override; /* generation functions */ - void generate_typedef(t_typedef* ttypedef); - void generate_enum(t_enum* tenum); - void generate_consts(vector consts); - void generate_struct(t_struct* tstruct); - void generate_service(t_service* tservice); - void generate_xception(t_struct* tstruct); + void generate_typedef(t_typedef* ttypedef) override; + void generate_enum(t_enum* tenum) override; + void generate_consts(vector consts) override; + void generate_struct(t_struct* tstruct) override; + void generate_service(t_service* tservice) override; + void generate_xception(t_struct* tstruct) override; private: /* file streams */ @@ -253,15 +253,13 @@ void t_c_glib_generator::init_generator() { if (!includes.empty()) { f_types_ << "/* other thrift includes */" << endl; - for (vector::const_iterator iter = includes.begin(); - iter != includes.end(); - ++iter) { - const std::string& include_nspace = (*iter)->get_namespace("c_glib"); + for (auto include : includes) { + const std::string& include_nspace = include->get_namespace("c_glib"); std::string include_nspace_prefix = include_nspace.empty() ? "" : initial_caps_to_underscores(include_nspace) + "_"; f_types_ << "#include \"" << include_nspace_prefix - << initial_caps_to_underscores((*iter)->get_name()) << "_types.h\"" << endl; + << initial_caps_to_underscores(include->get_name()) << "_types.h\"" << endl; } f_types_ << endl; } @@ -269,11 +267,11 @@ void t_c_glib_generator::init_generator() { /* include custom headers */ const vector& c_includes = program_->get_c_includes(); f_types_ << "/* custom thrift includes */" << endl; - for (size_t i = 0; i < c_includes.size(); ++i) { - if (c_includes[i][0] == '<') { - f_types_ << "#include " << c_includes[i] << endl; + for (const auto & c_include : c_includes) { + if (c_include[0] == '<') { + f_types_ << "#include " << c_include << endl; } else { - f_types_ << "#include \"" << c_includes[i] << "\"" << endl; + f_types_ << "#include \"" << c_include << "\"" << endl; } } f_types_ << endl; @@ -473,7 +471,7 @@ void t_c_glib_generator::generate_service(t_service* tservice) { // if we are inheriting from another service, include its header t_service* extends_service = tservice->get_extends(); - if (extends_service != NULL) { + if (extends_service != nullptr) { f_header_ << "#include \"" << this->nspace_lc << to_lower_case(initial_caps_to_underscores(extends_service->get_name())) << ".h\"" << endl; @@ -1014,7 +1012,7 @@ void t_c_glib_generator::generate_const_initializer(string name, // initialize any constants that may be referenced by this initializer for (v_iter = val.begin(); v_iter != val.end(); ++v_iter) { - t_type* field_type = NULL; + t_type* field_type = nullptr; string field_name = ""; for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { @@ -1024,7 +1022,7 @@ void t_c_glib_generator::generate_const_initializer(string name, break; } } - if (field_type == NULL) { + if (field_type == nullptr) { throw "type error: " + type->get_name() + " has no field " + v_iter->first->get_string(); } @@ -1056,7 +1054,7 @@ void t_c_glib_generator::generate_const_initializer(string name, scope_down(f_types_impl_); for (v_iter = val.begin(); v_iter != val.end(); ++v_iter) { - t_type* field_type = NULL; + t_type* field_type = nullptr; string field_name = ""; for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { @@ -1066,7 +1064,7 @@ void t_c_glib_generator::generate_const_initializer(string name, break; } } - if (field_type == NULL) { + if (field_type == nullptr) { throw "type error: " + type->get_name() + " has no field " + v_iter->first->get_string(); } @@ -1173,7 +1171,7 @@ void t_c_glib_generator::generate_const_initializer(string name, << indent() << "if (constant == NULL)" << endl; scope_up(f_types_impl_); f_types_impl_ << initializers.str() << endl - << indent() << "constant = " << generate_new_hash_from_type(etype, NULL) << endl + << indent() << "constant = " << generate_new_hash_from_type(etype, nullptr) << endl << appenders.str(); scope_down(f_types_impl_); f_types_impl_ << indent() << "return constant;" << endl; @@ -1275,7 +1273,7 @@ void t_c_glib_generator::generate_service_client(t_service* tservice) { string parent_class_name = "GObject"; string parent_type_name = "G_TYPE_OBJECT"; - // The service this service extends, or NULL if it extends no + // The service this service extends, or nullptr if it extends no // service t_service* extends_service = tservice->get_extends(); if (extends_service) { @@ -1853,7 +1851,7 @@ void t_c_glib_generator::generate_service_handler(t_service* tservice) { string args_indent; - // The service this service extends, or NULL if it extends no service + // The service this service extends, or nullptr if it extends no service t_service* extends_service = tservice->get_extends(); // Determine the name of our parent service (if any) and the handler class' @@ -2083,7 +2081,7 @@ void t_c_glib_generator::generate_service_processor(t_service* tservice) { string function_name; string args_indent; - // The service this service extends, or NULL if it extends no service + // The service this service extends, or nullptr if it extends no service t_service* extends_service = tservice->get_extends(); // Determine the name of our parent service (if any) and the @@ -2373,48 +2371,6 @@ void t_c_glib_generator::generate_service_processor(t_service* tservice) { } f_service_ << "return_value, " << "NULL);" << endl; - - // Deallocate (or unref) return_value - return_type = get_true_type(return_type); - if (return_type->is_base_type()) { - t_base_type* base_type = ((t_base_type*)return_type); - - if (base_type->get_base() == t_base_type::TYPE_STRING) { - f_service_ << indent() << "if (return_value != NULL)" << endl; - indent_up(); - if (base_type->is_binary()) { - f_service_ << indent() << "g_byte_array_unref (return_value);" << endl; - } else { - f_service_ << indent() << "g_free (return_value);" << endl; - } - indent_down(); - } - } else if (return_type->is_container()) { - f_service_ << indent() << "if (return_value != NULL)" << endl; - indent_up(); - - if (return_type->is_list()) { - t_type* elem_type = ((t_list*)return_type)->get_elem_type(); - - f_service_ << indent(); - if (is_numeric(elem_type)) { - f_service_ << "g_array_unref"; - } else { - f_service_ << "g_ptr_array_unref"; - } - f_service_ << " (return_value);" << endl; - } else if (return_type->is_map() || return_type->is_set()) { - f_service_ << indent() << "g_hash_table_unref (return_value);" << endl; - } - - indent_down(); - } else if (return_type->is_struct()) { - f_service_ << indent() << "if (return_value != NULL)" << endl; - indent_up(); - f_service_ << indent() << "g_object_unref (return_value);" << endl; - indent_down(); - } - f_service_ << endl; } f_service_ << indent() << "result =" << endl; @@ -2447,6 +2403,7 @@ void t_c_glib_generator::generate_service_processor(t_service* tservice) { f_service_ << args_indent << "\"" << (*xception_iter)->get_name() << "\", " << (*xception_iter)->get_name() << "," << endl << args_indent << "NULL);" << endl << endl; + f_service_ << indent() << "g_object_unref ("<< (*xception_iter)->get_name() <<");"<< endl; f_service_ << indent() << "result =" << endl; indent_up(); f_service_ << indent() << "((thrift_protocol_write_message_begin (output_protocol," << endl; @@ -2552,6 +2509,47 @@ void t_c_glib_generator::generate_service_processor(t_service* tservice) { } if (!(*function_iter)->is_oneway()) { + if (has_return_value) { + // Deallocate (or unref) return_value + return_type = get_true_type(return_type); + if (return_type->is_base_type()) { + t_base_type* base_type = ((t_base_type*)return_type); + if (base_type->get_base() == t_base_type::TYPE_STRING) { + f_service_ << indent() << "if (return_value != NULL)" << endl; + indent_up(); + if (base_type->is_binary()) { + f_service_ << indent() << "g_byte_array_unref (return_value);" << endl; + } else { + f_service_ << indent() << "g_free (return_value);" << endl; + } + indent_down(); + } + } else if (return_type->is_container()) { + f_service_ << indent() << "if (return_value != NULL)" << endl; + indent_up(); + + if (return_type->is_list()) { + t_type* elem_type = ((t_list*)return_type)->get_elem_type(); + + f_service_ << indent(); + if (is_numeric(elem_type)) { + f_service_ << "g_array_unref"; + } else { + f_service_ << "g_ptr_array_unref"; + } + f_service_ << " (return_value);" << endl; + } else if (return_type->is_map() || return_type->is_set()) { + f_service_ << indent() << "g_hash_table_unref (return_value);" << endl; + } + + indent_down(); + } else if (return_type->is_struct()) { + f_service_ << indent() << "if (return_value != NULL)" << endl; + indent_up(); + f_service_ << indent() << "g_object_unref (return_value);" << endl; + indent_down(); + } + } f_service_ << indent() << "g_object_unref (result_struct);" << endl << endl << indent() << "if (result == TRUE)" << endl; indent_up(); @@ -3098,7 +3096,7 @@ void t_c_glib_generator::generate_object(t_struct* tstruct) { t_field* member = *m_iter; t_const_value* member_value = member->get_value(); - if (member_value != NULL) { + if (member_value != nullptr) { string member_name = member->get_name(); t_type* member_type = get_true_type(member->get_type()); @@ -3147,7 +3145,7 @@ void t_c_glib_generator::generate_object(t_struct* tstruct) { dval += "(" + type_name(t) + ")"; } t_const_value* cv = (*m_iter)->get_value(); - if (cv != NULL) { + if (cv != nullptr) { dval += constant_value("", t, cv); } else { dval += t->is_string() ? "NULL" : "0"; @@ -3169,7 +3167,7 @@ void t_c_glib_generator::generate_object(t_struct* tstruct) { } else if (t->is_container()) { string name = (*m_iter)->get_name(); string init_function; - t_type* etype = NULL; + t_type* etype = nullptr; if (t->is_map()) { t_type* key = ((t_map*)t)->get_key_type(); @@ -3177,7 +3175,7 @@ void t_c_glib_generator::generate_object(t_struct* tstruct) { init_function = generate_new_hash_from_type(key, value); } else if (t->is_set()) { etype = ((t_set*)t)->get_elem_type(); - init_function = generate_new_hash_from_type(etype, NULL); + init_function = generate_new_hash_from_type(etype, nullptr); } else if (t->is_list()) { etype = ((t_list*)t)->get_elem_type(); init_function = generate_new_array_from_type(etype); @@ -3414,7 +3412,7 @@ void t_c_glib_generator::generate_object(t_struct* tstruct) { break; } - if (member_value != NULL) { + if (member_value != nullptr) { default_value << (base_type == t_base_type::TYPE_DOUBLE ? member_value->get_double() : member_value->get_integer()); } else { @@ -3433,8 +3431,8 @@ void t_c_glib_generator::generate_object(t_struct* tstruct) { } else if (member_type->is_enum()) { t_enum_value* enum_min_value = ((t_enum*)member_type)->get_min_value(); t_enum_value* enum_max_value = ((t_enum*)member_type)->get_max_value(); - int min_value = (enum_min_value != NULL) ? enum_min_value->get_value() : 0; - int max_value = (enum_max_value != NULL) ? enum_max_value->get_value() : 0; + int min_value = (enum_min_value != nullptr) ? enum_min_value->get_value() : 0; + int max_value = (enum_max_value != nullptr) ? enum_max_value->get_value() : 0; args_indent += string(18, ' '); f_types_impl_ << "g_param_spec_int (\"" << member_name << "\"," << endl << args_indent @@ -4334,13 +4332,14 @@ void t_c_glib_generator::generate_deserialize_list_element(ostream& out, throw std::runtime_error("compiler error: list element type cannot be void"); } else if (is_numeric(ttype)) { indent(out) << "g_array_append_vals (" << prefix << ", " << elem << ", 1);" << endl; + indent(out) << "g_free (" << elem << ");" << endl; } else { indent(out) << "g_ptr_array_add (" << prefix << ", " << elem << ");" << endl; } } string t_c_glib_generator::generate_free_func_from_type(t_type* ttype) { - if (ttype == NULL) + if (ttype == nullptr) return "NULL"; if (ttype->is_base_type()) { @@ -4406,7 +4405,7 @@ string t_c_glib_generator::generate_free_func_from_type(t_type* ttype) { } string t_c_glib_generator::generate_hash_func_from_type(t_type* ttype) { - if (ttype == NULL) + if (ttype == nullptr) return "NULL"; if (ttype->is_base_type()) { @@ -4444,7 +4443,7 @@ string t_c_glib_generator::generate_hash_func_from_type(t_type* ttype) { } string t_c_glib_generator::generate_cmp_func_from_type(t_type* ttype) { - if (ttype == NULL) + if (ttype == nullptr) return "NULL"; if (ttype->is_base_type()) { @@ -4507,23 +4506,21 @@ string t_c_glib_generator::generate_new_array_from_type(t_type* ttype) { ***************************************/ /** - * Upper case a string. Wraps boost's string utility. + * Upper case a string. */ string to_upper_case(string name) { string s(name); std::transform(s.begin(), s.end(), s.begin(), ::toupper); return s; - // return boost::to_upper_copy (name); } /** - * Lower case a string. Wraps boost's string utility. + * Lower case a string. */ string to_lower_case(string name) { string s(name); std::transform(s.begin(), s.end(), s.begin(), ::tolower); return s; - // return boost::to_lower_copy (name); } /** diff --git a/compiler/cpp/src/thrift/generate/t_cl_generator.cc b/compiler/cpp/src/thrift/generate/t_cl_generator.cc index 628d0d8d785..06b6b656356 100644 --- a/compiler/cpp/src/thrift/generate/t_cl_generator.cc +++ b/compiler/cpp/src/thrift/generate/t_cl_generator.cc @@ -26,7 +26,6 @@ #include #include -#include #include #include #include @@ -35,6 +34,7 @@ #include "thrift/platform.h" #include "t_oop_generator.h" + using namespace std; @@ -70,15 +70,15 @@ class t_cl_generator : public t_oop_generator { copy_options_ = option_string; } - void init_generator(); - void close_generator(); + void init_generator() override; + void close_generator() override; - void generate_typedef (t_typedef* ttypedef); - void generate_enum (t_enum* tenum); - void generate_const (t_const* tconst); - void generate_struct (t_struct* tstruct); - void generate_xception (t_struct* txception); - void generate_service (t_service* tservice); + void generate_typedef (t_typedef* ttypedef) override; + void generate_enum (t_enum* tenum) override; + void generate_const (t_const* tconst) override; + void generate_struct (t_struct* tstruct) override; + void generate_xception (t_struct* txception) override; + void generate_service (t_service* tservice) override; void generate_cl_struct (std::ostream& out, t_struct* tstruct, bool is_exception); void generate_cl_struct_internal (std::ostream& out, t_struct* tstruct, bool is_exception); void generate_exception_sig(std::ostream& out, t_function* f); @@ -128,9 +128,9 @@ void t_cl_generator::init_generator() { string f_types_name = program_dir + "/" + program_name_ + "-types.lisp"; string f_vars_name = program_dir + "/" + program_name_ + "-vars.lisp"; - f_types_.open(f_types_name.c_str()); + f_types_.open(f_types_name); f_types_ << cl_autogen_comment() << endl; - f_vars_.open(f_vars_name.c_str()); + f_vars_.open(f_vars_name); f_vars_ << cl_autogen_comment() << endl; package_def(f_types_); @@ -139,7 +139,7 @@ void t_cl_generator::init_generator() { if (!no_asd) { string f_asd_name = program_dir + "/" + system_prefix + program_name_ + ".asd"; - f_asd_.open(f_asd_name.c_str()); + f_asd_.open(f_asd_name); f_asd_ << cl_autogen_comment() << endl; asdf_def(f_asd_); } @@ -152,8 +152,8 @@ string t_cl_generator::render_includes() { const vector& includes = program_->get_includes(); string result = ""; result += ":depends-on (:thrift"; - for (size_t i = 0; i < includes.size(); ++i) { - result += " :" + system_prefix + underscore(includes[i]->get_name()); + for (auto include : includes) { + result += " :" + system_prefix + underscore(include->get_name()); } result += ")\n"; return result; @@ -215,8 +215,8 @@ void t_cl_generator::package_def(std::ostream &out) { out << "(thrift:def-package :" << package(); if ( includes.size() > 0 ) { out << " :use ("; - for (size_t i = 0; i < includes.size(); ++i) { - out << " :" << includes[i]->get_name(); + for (auto include : includes) { + out << " :" << include->get_name(); } out << ")"; } @@ -314,13 +314,13 @@ string t_cl_generator::render_const_value(t_type* type, t_const_value* value) { map::const_iterator v_iter; for (v_iter = val.begin(); v_iter != val.end(); ++v_iter) { - t_type* field_type = NULL; + t_type* field_type = nullptr; for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { if ((*f_iter)->get_name() == v_iter->first->get_string()) { field_type = (*f_iter)->get_type(); } } - if (field_type == NULL) { + if (field_type == nullptr) { throw "type error: " + type->get_name() + " has no field " + v_iter->first->get_string(); } @@ -396,7 +396,7 @@ void t_cl_generator::generate_cl_struct_internal(std::ostream& out, t_struct* ts out << endl << indent() << " "; } out << "(" << prefix((*m_iter)->get_name()) << " " << - ( (NULL != value) ? render_const_value(type, value) : "nil" ) << + ( (nullptr != value) ? render_const_value(type, value) : "nil" ) << " :id " << (*m_iter)->get_key(); if ( type->is_base_type() && "string" == typespec(type) ) if ( ((t_base_type*)type)->is_binary() ) @@ -441,7 +441,7 @@ void t_cl_generator::generate_service(t_service* tservice) { vector functions = tservice->get_functions(); vector::iterator f_iter; - if (tservice->get_extends() != NULL) { + if (tservice->get_extends() != nullptr) { extends_client = type_name(tservice->get_extends()); } @@ -540,7 +540,7 @@ string t_cl_generator::type_name(t_type* ttype) { string prefix = ""; t_program* program = ttype->get_program(); - if (program != NULL && program != program_) + if (program != nullptr && program != program_) prefix = package_of(program) == package() ? "" : package_of(program) + ":"; string name = ttype->get_name(); diff --git a/compiler/cpp/src/thrift/generate/t_cocoa_generator.cc b/compiler/cpp/src/thrift/generate/t_cocoa_generator.cc deleted file mode 100644 index ac644f5c599..00000000000 --- a/compiler/cpp/src/thrift/generate/t_cocoa_generator.cc +++ /dev/null @@ -1,3309 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -#include -#include -#include -#include - -#include -#include -#include -#include "thrift/platform.h" -#include "thrift/generate/t_oop_generator.h" - -using std::map; -using std::ostream; -using std::ofstream; -using std::ostringstream; -using std::string; -using std::stringstream; -using std::vector; - -static const string endl = "\n"; // avoid ostream << std::endl flushes - -/** - * Objective-C code generator. - * - * mostly copy/pasting/tweaking from mcslee's work. - */ -class t_cocoa_generator : public t_oop_generator { -public: - t_cocoa_generator(t_program* program, - const std::map& parsed_options, - const std::string& option_string) - : t_oop_generator(program) { - (void)option_string; - std::map::const_iterator iter; - - log_unexpected_ = false; - validate_required_ = false; - async_clients_ = false; - promise_kit_ = false; - debug_descriptions_ = false; - pods_ = false; - for( iter = parsed_options.begin(); iter != parsed_options.end(); ++iter) { - if( iter->first.compare("log_unexpected") == 0) { - log_unexpected_ = true; - } else if( iter->first.compare("validate_required") == 0) { - validate_required_ = true; - } else if( iter->first.compare("async_clients") == 0) { - async_clients_ = true; - } else if( iter->first.compare("promise_kit") == 0) { - promise_kit_ = true; - } else if( iter->first.compare("debug_descriptions") == 0) { - debug_descriptions_ = true; - } else if( iter->first.compare("pods") == 0) { - pods_ = true; - } else { - throw "unknown option cocoa:" + iter->first; - } - } - - out_dir_base_ = "gen-cocoa"; - } - - /** - * Init and close methods - */ - - void init_generator(); - void close_generator(); - - void generate_consts(std::vector consts); - - /** - * Program-level generation functions - */ - - void generate_typedef(t_typedef* ttypedef); - void generate_enum(t_enum* tenum); - void generate_struct(t_struct* tstruct); - void generate_xception(t_struct* txception); - void generate_service(t_service* tservice); - - void print_const_value(ostream& out, - string name, - t_type* type, - t_const_value* value, - bool defval = false); - std::string render_const_value(ostream& out, - t_type* type, - t_const_value* value, - bool box_it = false); - - void generate_cocoa_struct(t_struct* tstruct, bool is_exception); - void generate_cocoa_struct_interface(std::ostream& out, - t_struct* tstruct, - bool is_xception = false); - void generate_cocoa_struct_implementation(std::ostream& out, - t_struct* tstruct, - bool is_xception = false, - bool is_result = false); - void generate_cocoa_struct_initializer_signature(std::ostream& out, t_struct* tstruct); - void generate_cocoa_struct_init_with_coder_method(ostream& out, - t_struct* tstruct, - bool is_exception); - void generate_cocoa_struct_encode_with_coder_method(ostream& out, - t_struct* tstruct, - bool is_exception); - void generate_cocoa_struct_copy_method(ostream& out, - t_struct* tstruct, - bool is_exception); - void generate_cocoa_struct_hash_method(ostream& out, t_struct* tstruct); - void generate_cocoa_struct_is_equal_method(ostream& out, - t_struct* tstruct, - bool is_exception); - void generate_cocoa_struct_field_accessor_implementations(std::ostream& out, - t_struct* tstruct, - bool is_exception); - void generate_cocoa_struct_reader(std::ostream& out, t_struct* tstruct); - void generate_cocoa_struct_result_writer(std::ostream& out, t_struct* tstruct); - void generate_cocoa_struct_writer(std::ostream& out, t_struct* tstruct); - void generate_cocoa_struct_validator(std::ostream& out, t_struct* tstruct); - void generate_cocoa_struct_description(std::ostream& out, t_struct* tstruct); - - std::string function_result_helper_struct_type(t_service *tservice, t_function* tfunction); - std::string function_args_helper_struct_type(t_service* tservice, t_function* tfunction); - void generate_function_helpers(t_service *tservice, t_function* tfunction); - - /** - * Service-level generation functions - */ - - void generate_cocoa_service_protocol(std::ostream& out, t_service* tservice); - void generate_cocoa_service_async_protocol(std::ostream& out, t_service* tservice); - - void generate_cocoa_service_client_interface(std::ostream& out, t_service* tservice); - void generate_cocoa_service_client_async_interface(std::ostream& out, t_service* tservice); - - void generate_cocoa_service_client_send_function_implementation(ostream& out, - t_service* tservice, - t_function* tfunction, - bool needs_protocol); - void generate_cocoa_service_client_send_function_invocation(ostream& out, t_function* tfunction); - void generate_cocoa_service_client_send_async_function_invocation(ostream& out, - t_function* tfunction, - string failureBlockName); - void generate_cocoa_service_client_recv_function_implementation(ostream& out, - t_service* tservice, - t_function* tfunction, - bool needs_protocol); - void generate_cocoa_service_client_implementation(std::ostream& out, t_service* tservice); - void generate_cocoa_service_client_async_implementation(std::ostream& out, t_service* tservice); - - void generate_cocoa_service_server_interface(std::ostream& out, t_service* tservice); - void generate_cocoa_service_server_implementation(std::ostream& out, t_service* tservice); - void generate_cocoa_service_helpers(t_service* tservice); - void generate_service_client(t_service* tservice); - void generate_service_server(t_service* tservice); - void generate_process_function(t_service* tservice, t_function* tfunction); - - /** - * Serialization constructs - */ - - void generate_deserialize_field(std::ostream& out, t_field* tfield, std::string fieldName); - - void generate_deserialize_struct(std::ostream& out, t_struct* tstruct, std::string prefix = ""); - - void generate_deserialize_container(std::ostream& out, t_type* ttype, std::string prefix = ""); - - void generate_deserialize_set_element(std::ostream& out, t_set* tset, std::string prefix = ""); - - void generate_deserialize_map_element(std::ostream& out, t_map* tmap, std::string prefix = ""); - - void generate_deserialize_list_element(std::ostream& out, - t_list* tlist, - std::string prefix = ""); - - void generate_serialize_field(std::ostream& out, t_field* tfield, std::string prefix = ""); - - void generate_serialize_struct(std::ostream& out, t_struct* tstruct, std::string fieldName = ""); - - void generate_serialize_container(std::ostream& out, t_type* ttype, std::string prefix = ""); - - void generate_serialize_map_element(std::ostream& out, - t_map* tmap, - std::string iter, - std::string map); - - void generate_serialize_set_element(std::ostream& out, t_set* tmap, std::string iter); - - void generate_serialize_list_element(std::ostream& out, - t_list* tlist, - std::string index, - std::string listName); - - /** - * Helper rendering functions - */ - - std::string cocoa_prefix(); - std::string cocoa_imports(); - std::string cocoa_thrift_imports(); - std::string type_name(t_type* ttype, bool class_ref = false, bool needs_mutable = false); - std::string element_type_name(t_type* ttype); - std::string base_type_name(t_base_type* tbase); - std::string declare_property(t_field* tfield); - std::string declare_property_isset(t_field* tfield); - std::string declare_property_unset(t_field* tfield); - std::string invalid_return_statement(t_function* tfunction); - std::string function_signature(t_function* tfunction, bool include_error); - std::string async_function_signature(t_function* tfunction, bool include_error); - std::string promise_function_signature(t_function* tfunction); - std::string argument_list(t_struct* tstruct, string protocol_name, bool include_error); - std::string type_to_enum(t_type* ttype); - std::string format_string_for_type(t_type* type); - std::string format_cast_for_type(t_type* type); - std::string call_field_setter(t_field* tfield, std::string fieldName); - std::string box(t_type *ttype, std::string field_name); - std::string unbox(t_type* ttype, std::string field_name); - std::string getter_name(string field_name); - std::string setter_name(string field_name); - - bool type_can_be_copy(t_type* ttype) { - ttype = get_true_type(ttype); - - return ttype->is_string(); - } - - bool type_can_be_null(t_type* ttype) { - ttype = get_true_type(ttype); - - return ttype->is_container() || ttype->is_struct() || ttype->is_xception() - || ttype->is_string(); - } - -private: - std::string cocoa_prefix_; - std::string constants_declarations_; - int error_constant_; - - /** - * File streams - */ - - ofstream_with_content_based_conditional_update f_header_; - ofstream_with_content_based_conditional_update f_impl_; - - bool log_unexpected_; - bool validate_required_; - bool async_clients_; - bool promise_kit_; - bool debug_descriptions_; - bool pods_; -}; - -/** - * Prepares for file generation by opening up the necessary file output - * streams. - */ -void t_cocoa_generator::init_generator() { - // Make output directory - MKDIR(get_out_dir().c_str()); - cocoa_prefix_ = program_->get_namespace("cocoa"); - - // we have a .h header file... - string f_header_name = cocoa_prefix_ + capitalize(program_name_) + ".h"; - string f_header_fullname = get_out_dir() + f_header_name; - f_header_.open(f_header_fullname.c_str()); - - f_header_ << autogen_comment() << endl; - - f_header_ << cocoa_imports() << cocoa_thrift_imports(); - - // ...and a .m implementation file - string f_impl_name = cocoa_prefix_ + capitalize(program_name_) + ".m"; - string f_impl_fullname = get_out_dir() + f_impl_name; - f_impl_.open(f_impl_fullname.c_str()); - - f_impl_ << autogen_comment() << endl; - - f_impl_ << cocoa_imports() << cocoa_thrift_imports() << "#import \"" << f_header_name << "\"" - << endl << endl; - - error_constant_ = 60000; -} - -/** - * Prints standard Cocoa imports - * - * @return List of imports for Cocoa libraries - */ -string t_cocoa_generator::cocoa_imports() { - return string() + "#import \n" + "\n"; -} - -/** - * Prints thrift runtime imports - * - * @return List of imports necessary for thrift runtime - */ -string t_cocoa_generator::cocoa_thrift_imports() { - - vector includes_list; - includes_list.push_back("TProtocol.h"); - includes_list.push_back("TProtocolFactory.h"); - includes_list.push_back("TApplicationError.h"); - includes_list.push_back("TProtocolError.h"); - includes_list.push_back("TProtocolUtil.h"); - includes_list.push_back("TProcessor.h"); - includes_list.push_back("TBase.h"); - includes_list.push_back("TAsyncTransport.h"); - includes_list.push_back("TBaseClient.h"); - - std::ostringstream includes; - - vector::const_iterator i_iter; - for (i_iter=includes_list.begin(); i_iter!=includes_list.end(); ++i_iter) { - includes << "#import "; - if (pods_) { - includes << ""; - } else { - includes << "\"" << *i_iter << "\""; - } - includes << endl; - } - - includes << endl; - - if (promise_kit_) { - includes << "#import "; - if (pods_) { - includes << ""; - } else { - includes << "\"PromiseKit.h\""; - } - includes << endl; - } - - // Include other Thrift includes - const vector& other_includes = program_->get_includes(); - for (size_t i = 0; i < other_includes.size(); ++i) { - includes << "#import \"" - << other_includes[i]->get_namespace("cocoa") - << capitalize(other_includes[i]->get_name()) - << ".h\"" << endl; - } - - includes << endl; - - return includes.str(); -} - -/** - * Finish up generation. - */ -void t_cocoa_generator::close_generator() { - // stick our constants declarations at the end of the header file - // since they refer to things we are defining. - f_header_ << constants_declarations_ << endl; -} - -/** - * Generates a typedef. This is just a simple 1-liner in objective-c - * - * @param ttypedef The type definition - */ -void t_cocoa_generator::generate_typedef(t_typedef* ttypedef) { - if (ttypedef->get_type()->is_map()) { - t_map *map = (t_map *)ttypedef->get_type(); - if (map->get_key_type()->is_struct()) { - f_header_ << indent() << "@class " << type_name(map->get_key_type(), true) << ";" << endl; - } - if (map->get_val_type()->is_struct()) { - f_header_ << indent() << "@class " << type_name(map->get_val_type(), true) << ";" << endl; - } - } - else if (ttypedef->get_type()->is_set()) { - t_set *set = (t_set *)ttypedef->get_type(); - if (set->get_elem_type()->is_struct()) { - f_header_ << indent() << "@class " << type_name(set->get_elem_type(), true) << ";" << endl; - } - } - else if (ttypedef->get_type()->is_list()) { - t_list *list = (t_list *)ttypedef->get_type(); - if (list->get_elem_type()->is_struct()) { - f_header_ << indent() << "@class " << type_name(list->get_elem_type(), true) << ";" << endl; - } - } - f_header_ << indent() << "typedef " << type_name(ttypedef->get_type()) << " " << cocoa_prefix_ - << ttypedef->get_symbolic() << ";" << endl << endl; - if (ttypedef->get_type()->is_container()) { - f_header_ << indent() << "typedef " << type_name(ttypedef->get_type(), false, true) << " " << cocoa_prefix_ - << "Mutable" << ttypedef->get_symbolic() << ";" << endl << endl; - } -} - -/** - * Generates code for an enumerated type. In Objective-C, this is - * essentially the same as the thrift definition itself, instead using - * NS_ENUM keyword in Objective-C. For namespace purposes, the name of - * the enum is prefixed to each element in keeping with Cocoa & Swift - * standards. - * - * @param tenum The enumeration - */ -void t_cocoa_generator::generate_enum(t_enum* tenum) { - f_header_ << indent() << "typedef NS_ENUM(SInt32, " << cocoa_prefix_ << tenum->get_name() << ") {" << endl; - indent_up(); - - vector constants = tenum->get_constants(); - vector::iterator c_iter; - bool first = true; - for (c_iter = constants.begin(); c_iter != constants.end(); ++c_iter) { - if (first) { - first = false; - } else { - f_header_ << "," << endl; - } - f_header_ << indent() << cocoa_prefix_ << tenum->get_name() << (*c_iter)->get_name(); - f_header_ << " = " << (*c_iter)->get_value(); - } - - indent_down(); - f_header_ << endl << "};" << endl << endl; -} - -/** - * Generates a class that holds all the constants. - */ -void t_cocoa_generator::generate_consts(std::vector consts) { - std::ostringstream const_interface; - - const_interface << "FOUNDATION_EXPORT NSString *" << cocoa_prefix_ << capitalize(program_name_) << "ErrorDomain;" << endl - << endl; - - - bool needs_class = false; - - // Public constants for base types & strings - vector::iterator c_iter; - for (c_iter = consts.begin(); c_iter != consts.end(); ++c_iter) { - t_type* type = (*c_iter)->get_type()->get_true_type(); - if (!type->is_container() && !type->is_struct()) { - const_interface << "FOUNDATION_EXPORT " << type_name(type) << " " - << cocoa_prefix_ << capitalize((*c_iter)->get_name()) << ";" << endl; - } - else { - needs_class = true; - } - } - - - string constants_class_name = cocoa_prefix_ + capitalize(program_name_) + "Constants"; - - if (needs_class) { - - const_interface << endl; - - const_interface << "@interface " << constants_class_name << " : NSObject "; - scope_up(const_interface); - scope_down(const_interface); - - // getter method for each constant defined. - for (c_iter = consts.begin(); c_iter != consts.end(); ++c_iter) { - string name = (*c_iter)->get_name(); - t_type* type = (*c_iter)->get_type()->get_true_type(); - if (type->is_container() || type->is_struct()) { - t_type* type = (*c_iter)->get_type(); - const_interface << endl << "+ (" << type_name(type) << ") " << name << ";" << endl; - } - } - - const_interface << endl << "@end"; - } - - // this gets spit into the header file in ::close_generator - constants_declarations_ = const_interface.str(); - - f_impl_ << "NSString *" << cocoa_prefix_ << capitalize(program_name_) << "ErrorDomain = " - << "@\"" << cocoa_prefix_ << capitalize(program_name_) << "ErrorDomain\";" << endl << endl; - - // variables in the .m hold all simple constant values - for (c_iter = consts.begin(); c_iter != consts.end(); ++c_iter) { - string name = (*c_iter)->get_name(); - t_type* type = (*c_iter)->get_type(); - f_impl_ << type_name(type) << " " << cocoa_prefix_ << name; - t_type* ttype = type->get_true_type(); - if (!ttype->is_container() && !ttype->is_struct()) { - f_impl_ << " = " << render_const_value(f_impl_, type, (*c_iter)->get_value()); - } - f_impl_ << ";" << endl; - } - f_impl_ << endl; - - if (needs_class) { - - f_impl_ << "@implementation " << constants_class_name << endl << endl; - - // initialize complex constants when the class is loaded - f_impl_ << "+ (void) initialize "; - scope_up(f_impl_); - - for (c_iter = consts.begin(); c_iter != consts.end(); ++c_iter) { - t_type* ttype = (*c_iter)->get_type()->get_true_type(); - if (ttype->is_container() || ttype->is_struct()) { - f_impl_ << endl; - print_const_value(f_impl_, - cocoa_prefix_ + (*c_iter)->get_name(), - (*c_iter)->get_type(), - (*c_iter)->get_value(), - false); - f_impl_ << ";" << endl; - } - } - scope_down(f_impl_); - - // getter method for each constant - for (c_iter = consts.begin(); c_iter != consts.end(); ++c_iter) { - string name = (*c_iter)->get_name(); - t_type* type = (*c_iter)->get_type()->get_true_type(); - if (type->is_container() || type->is_struct()) { - f_impl_ << endl << "+ (" << type_name(type) << ") " << name << " "; - scope_up(f_impl_); - indent(f_impl_) << "return " << cocoa_prefix_ << name << ";" << endl; - scope_down(f_impl_); - } - } - - f_impl_ << "@end" << endl << endl; - } -} - -/** - * Generates a struct definition for a thrift data type. This is a class - * with protected data members, read(), write(), and getters and setters. - * - * @param tstruct The struct definition - */ -void t_cocoa_generator::generate_struct(t_struct* tstruct) { - generate_cocoa_struct_interface(f_header_, tstruct, false); - generate_cocoa_struct_implementation(f_impl_, tstruct, false); -} - -/** - * Exceptions are structs, but they inherit from NSException - * - * @param tstruct The struct definition - */ -void t_cocoa_generator::generate_xception(t_struct* txception) { - generate_cocoa_struct_interface(f_header_, txception, true); - generate_cocoa_struct_implementation(f_impl_, txception, true); -} - -/** - * Generate the interface for a struct - * - * @param tstruct The struct definition - */ -void t_cocoa_generator::generate_cocoa_struct_interface(ostream& out, - t_struct* tstruct, - bool is_exception) { - - if (is_exception) { - out << "enum {" << endl - << " " << cocoa_prefix_ << capitalize(program_name_) << "Error" << tstruct->get_name() << " = -" << error_constant_++ << endl - << "};" << endl - << endl; - } - - out << "@interface " << cocoa_prefix_ << tstruct->get_name() << " : "; - - if (is_exception) { - out << "NSError "; - } else { - out << "NSObject "; - } - out << " " << endl; - - out << endl; - - // properties - const vector& members = tstruct->get_members(); - if (members.size() > 0) { - vector::const_iterator m_iter; - for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) { - out << indent() << declare_property(*m_iter) << endl; - out << indent() << declare_property_isset(*m_iter) << endl; - out << indent() << declare_property_unset(*m_iter) << endl; - out << endl; - } - } - - out << endl; - - // initializer for all fields - if (!members.empty()) { - generate_cocoa_struct_initializer_signature(out, tstruct); - out << ";" << endl; - } - out << endl; - - out << "@end" << endl << endl; -} - -/** - * Generate signature for initializer of struct with a parameter for - * each field. - */ -void t_cocoa_generator::generate_cocoa_struct_initializer_signature(ostream& out, - t_struct* tstruct) { - const vector& members = tstruct->get_members(); - vector::const_iterator m_iter; - indent(out) << "- (instancetype) initWith"; - for (m_iter = members.begin(); m_iter != members.end();) { - if (m_iter == members.begin()) { - out << capitalize((*m_iter)->get_name()); - } else { - out << (*m_iter)->get_name(); - } - out << ": (" << type_name((*m_iter)->get_type()) << ") " << (*m_iter)->get_name(); - ++m_iter; - if (m_iter != members.end()) { - out << " "; - } - } -} - -/** - * Generate the initWithCoder method for this struct so it's compatible with - * the NSCoding protocol - */ -void t_cocoa_generator::generate_cocoa_struct_init_with_coder_method(ostream& out, - t_struct* tstruct, - bool is_exception) { - - indent(out) << "- (instancetype) initWithCoder: (NSCoder *) decoder" << endl; - scope_up(out); - - if (is_exception) { - // NSExceptions conform to NSCoding, so we can call super - indent(out) << "self = [super initWithCoder: decoder];" << endl; - } else { - indent(out) << "self = [super init];" << endl; - } - - indent(out) << "if (self) "; - scope_up(out); - - const vector& members = tstruct->get_members(); - vector::const_iterator m_iter; - - for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) { - t_type* t = get_true_type((*m_iter)->get_type()); - out << indent() << "if ([decoder containsValueForKey: @\"" << (*m_iter)->get_name() << "\"])" - << endl; - scope_up(out); - out << indent() << "_" << (*m_iter)->get_name() << " = "; - if (type_can_be_null(t)) { - out << "[decoder decodeObjectForKey: @\"" << (*m_iter)->get_name() << "\"];" - << endl; - } else if (t->is_enum()) { - out << "[decoder decodeIntForKey: @\"" << (*m_iter)->get_name() << "\"];" << endl; - } else { - t_base_type::t_base tbase = ((t_base_type*)t)->get_base(); - switch (tbase) { - case t_base_type::TYPE_BOOL: - out << "[decoder decodeBoolForKey: @\"" << (*m_iter)->get_name() << "\"];" << endl; - break; - case t_base_type::TYPE_I8: - out << "[decoder decodeIntForKey: @\"" << (*m_iter)->get_name() << "\"];" << endl; - break; - case t_base_type::TYPE_I16: - out << "[decoder decodeIntForKey: @\"" << (*m_iter)->get_name() << "\"];" << endl; - break; - case t_base_type::TYPE_I32: - out << "[decoder decodeInt32ForKey: @\"" << (*m_iter)->get_name() << "\"];" << endl; - break; - case t_base_type::TYPE_I64: - out << "[decoder decodeInt64ForKey: @\"" << (*m_iter)->get_name() << "\"];" << endl; - break; - case t_base_type::TYPE_DOUBLE: - out << "[decoder decodeDoubleForKey: @\"" << (*m_iter)->get_name() << "\"];" << endl; - break; - default: - throw "compiler error: don't know how to decode thrift type: " - + t_base_type::t_base_name(tbase); - } - } - out << indent() << "_" << (*m_iter)->get_name() << "IsSet = YES;" << endl; - scope_down(out); - } - - scope_down(out); - - out << indent() << "return self;" << endl; - scope_down(out); - out << endl; -} - -/** - * Generate the encodeWithCoder method for this struct so it's compatible with - * the NSCoding protocol - */ -void t_cocoa_generator::generate_cocoa_struct_encode_with_coder_method(ostream& out, - t_struct* tstruct, - bool is_exception) { - - indent(out) << "- (void) encodeWithCoder: (NSCoder *) encoder" << endl; - scope_up(out); - - if (is_exception) { - // NSExceptions conform to NSCoding, so we can call super - out << indent() << "[super encodeWithCoder: encoder];" << endl; - } - - const vector& members = tstruct->get_members(); - vector::const_iterator m_iter; - - for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) { - t_type* t = get_true_type((*m_iter)->get_type()); - out << indent() << "if (_" << (*m_iter)->get_name() << "IsSet)" << endl; - scope_up(out); - if (type_can_be_null(t)) { - out << indent() << "[encoder encodeObject: _" << (*m_iter)->get_name() << " forKey: @\"" - << (*m_iter)->get_name() << "\"];" << endl; - } else if (t->is_enum()) { - out << indent() << "[encoder encodeInt: _" << (*m_iter)->get_name() << " forKey: @\"" - << (*m_iter)->get_name() << "\"];" << endl; - } else { - t_base_type::t_base tbase = ((t_base_type*)t)->get_base(); - switch (tbase) { - case t_base_type::TYPE_BOOL: - out << indent() << "[encoder encodeBool: _" << (*m_iter)->get_name() << " forKey: @\"" - << (*m_iter)->get_name() << "\"];" << endl; - break; - case t_base_type::TYPE_I8: - out << indent() << "[encoder encodeInt: _" << (*m_iter)->get_name() << " forKey: @\"" - << (*m_iter)->get_name() << "\"];" << endl; - break; - case t_base_type::TYPE_I16: - out << indent() << "[encoder encodeInt: _" << (*m_iter)->get_name() << " forKey: @\"" - << (*m_iter)->get_name() << "\"];" << endl; - break; - case t_base_type::TYPE_I32: - out << indent() << "[encoder encodeInt32: _" << (*m_iter)->get_name() << " forKey: @\"" - << (*m_iter)->get_name() << "\"];" << endl; - break; - case t_base_type::TYPE_I64: - out << indent() << "[encoder encodeInt64: _" << (*m_iter)->get_name() << " forKey: @\"" - << (*m_iter)->get_name() << "\"];" << endl; - break; - case t_base_type::TYPE_DOUBLE: - out << indent() << "[encoder encodeDouble: _" << (*m_iter)->get_name() << " forKey: @\"" - << (*m_iter)->get_name() << "\"];" << endl; - break; - default: - throw "compiler error: don't know how to encode thrift type: " - + t_base_type::t_base_name(tbase); - } - } - scope_down(out); - } - - scope_down(out); - out << endl; -} - -/** - * Generate the copy method for this struct - */ -void t_cocoa_generator::generate_cocoa_struct_copy_method(ostream& out, t_struct* tstruct, bool is_exception) { - out << indent() << "- (instancetype) copyWithZone:(NSZone *)zone" << endl; - scope_up(out); - - if (is_exception) { - out << indent() << type_name(tstruct) << " val = [" << cocoa_prefix_ << tstruct->get_name() << " errorWithDomain: self.domain code: self.code userInfo: self.userInfo];" << endl; - } else { - out << indent() << type_name(tstruct) << " val = [" << cocoa_prefix_ << tstruct->get_name() << " new];" << endl; - } - - const vector& members = tstruct->get_members(); - vector::const_iterator m_iter; - - for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) { - t_type* t = get_true_type((*m_iter)->get_type()); - out << indent() << "if (_" << (*m_iter)->get_name() << "IsSet)" << endl; - scope_up(out); - if (type_can_be_null(t)) { - out << indent() << "val." << (*m_iter)->get_name() << " = [self." << (*m_iter)->get_name() << " copy];"; - } else { - out << indent() << "val." << (*m_iter)->get_name() << " = self." << (*m_iter)->get_name() << ";"; - } - out << endl; - scope_down(out); - } - - out << indent() << "return val;" << endl; - - scope_down(out); - out << endl; -} - -/** - * Generate the hash method for this struct - */ -void t_cocoa_generator::generate_cocoa_struct_hash_method(ostream& out, t_struct* tstruct) { - indent(out) << "- (NSUInteger) hash" << endl; - scope_up(out); - out << indent() << "NSUInteger hash = 17;" << endl; - - const vector& members = tstruct->get_members(); - vector::const_iterator m_iter; - - for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) { - t_type* t = get_true_type((*m_iter)->get_type()); - out << indent() << "hash = (hash * 31) ^ _" << (*m_iter)->get_name() - << "IsSet ? 2654435761 : 0;" << endl; - out << indent() << "if (_" << (*m_iter)->get_name() << "IsSet)" << endl; - scope_up(out); - if (type_can_be_null(t)) { - out << indent() << "hash = (hash * 31) ^ [_" << (*m_iter)->get_name() << " hash];" << endl; - } else { - out << indent() << "hash = (hash * 31) ^ [@(_" << (*m_iter)->get_name() << ") hash];" - << endl; - } - scope_down(out); - } - - out << indent() << "return hash;" << endl; - scope_down(out); - out << endl; -} - -/** - * Generate the isEqual method for this struct - */ -void t_cocoa_generator::generate_cocoa_struct_is_equal_method(ostream& out, t_struct* tstruct, bool is_exception) { - indent(out) << "- (BOOL) isEqual: (id) anObject" << endl; - scope_up(out); - - indent(out) << "if (self == anObject) {" << endl; - indent_up(); - indent(out) << "return YES;" << endl; - indent_down(); - indent(out) << "}" << endl; - - string class_name = cocoa_prefix_ + tstruct->get_name(); - - if (is_exception) { - indent(out) << "if (![super isEqual:anObject]) {" << endl; - indent_up(); - indent(out) << "return NO;" << endl; - indent_down(); - indent(out) << "}" << endl << endl; - } - else { - indent(out) << "if (![anObject isKindOfClass:[" << class_name << " class]]) {" << endl; - indent_up(); - indent(out) << "return NO;" << endl; - indent_down(); - indent(out) << "}" << endl; - } - - const vector& members = tstruct->get_members(); - vector::const_iterator m_iter; - - if (!members.empty()) { - indent(out) << class_name << " *other = (" << class_name << " *)anObject;" << endl; - - for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) { - t_type* t = get_true_type((*m_iter)->get_type()); - string name = (*m_iter)->get_name(); - if (type_can_be_null(t)) { - out << indent() << "if ((_" << name << "IsSet != other->_" << name << "IsSet) ||" << endl - << indent() << " " - << "(_" << name << "IsSet && " - << "((_" << name << " || other->_" << name << ") && " - << "![_" << name << " isEqual:other->_" << name << "]))) {" << endl; - } else { - out << indent() << "if ((_" << name << "IsSet != other->_" << name << "IsSet) ||" << endl - << indent() << " " - << "(_" << name << "IsSet && " - << "(_" << name << " != other->_" << name << "))) {" << endl; - } - indent_up(); - indent(out) << "return NO;" << endl; - indent_down(); - indent(out) << "}" << endl; - } - } - - out << indent() << "return YES;" << endl; - scope_down(out); - out << endl; -} - -/** - * Generate struct implementation. - * - * @param tstruct The struct definition - * @param is_exception Is this an exception? - * @param is_result If this is a result it needs a different writer - */ -void t_cocoa_generator::generate_cocoa_struct_implementation(ostream& out, - t_struct* tstruct, - bool is_exception, - bool is_result) { - indent(out) << "@implementation " << cocoa_prefix_ << tstruct->get_name() << endl << endl; - - const vector& members = tstruct->get_members(); - vector::const_iterator m_iter; - - // exceptions need to call the designated initializer on NSException - if (is_exception) { - out << indent() << "- (instancetype) init" << endl; - scope_up(out); - out << indent() << "return [super initWithDomain: " << cocoa_prefix_ << capitalize(program_name_) << "ErrorDomain" << endl - << indent() << " code: " << cocoa_prefix_ << capitalize(program_name_) << "Error" << tstruct->get_name() << endl - << indent() << " userInfo: nil];" << endl; - scope_down(out); - out << endl; - } else { - // struct - - // default initializer - // setup instance variables with default values - indent(out) << "- (instancetype) init" << endl; - scope_up(out); - indent(out) << "self = [super init];" << endl; - indent(out) << "if (self)"; - scope_up(out); - if (members.size() > 0) { - for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) { - t_type* t = get_true_type((*m_iter)->get_type()); - if ((*m_iter)->get_value() != NULL) { - print_const_value(out, - "self." + (*m_iter)->get_name(), - t, - (*m_iter)->get_value(), - false); - } - } - } - scope_down(out); - indent(out) << "return self;" << endl; - scope_down(out); - out << endl; - } - - // initializer with all fields as params - if (!members.empty()) { - generate_cocoa_struct_initializer_signature(out, tstruct); - out << endl; - scope_up(out); - if (is_exception) { - out << indent() << "self = [self init];" << endl; - } else { - out << indent() << "self = [super init];" << endl; - } - - indent(out) << "if (self)"; - scope_up(out); - for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) { - out << indent() << "_" << (*m_iter)->get_name() << " = "; - if (get_true_type((*m_iter)->get_type())->is_container()) { - out << "[" << (*m_iter)->get_name() << " mutableCopy];" << endl; - } else { - out << (*m_iter)->get_name() << ";" << endl; - } - out << indent() << "_" << (*m_iter)->get_name() << "IsSet = YES;" << endl; - } - scope_down(out); - - out << indent() << "return self;" << endl; - scope_down(out); - out << endl; - } - - // initWithCoder for NSCoding - generate_cocoa_struct_init_with_coder_method(out, tstruct, is_exception); - // encodeWithCoder for NSCoding - generate_cocoa_struct_encode_with_coder_method(out, tstruct, is_exception); - // hash and isEqual for NSObject - generate_cocoa_struct_hash_method(out, tstruct); - generate_cocoa_struct_is_equal_method(out, tstruct, is_exception); - // copy for NSObject - generate_cocoa_struct_copy_method(out, tstruct, is_exception); - - // the rest of the methods - generate_cocoa_struct_field_accessor_implementations(out, tstruct, is_exception); - generate_cocoa_struct_reader(out, tstruct); - if (is_result) { - generate_cocoa_struct_result_writer(out, tstruct); - } else { - generate_cocoa_struct_writer(out, tstruct); - } - generate_cocoa_struct_validator(out, tstruct); - generate_cocoa_struct_description(out, tstruct); - - out << "@end" << endl << endl; -} - -/** - * Generates a function to read all the fields of the struct. - * - * @param tstruct The struct definition - */ -void t_cocoa_generator::generate_cocoa_struct_reader(ostream& out, t_struct* tstruct) { - out << "- (BOOL) read: (id ) inProtocol error: (NSError *__autoreleasing *)__thriftError" << endl; - scope_up(out); - - const vector& fields = tstruct->get_members(); - vector::const_iterator f_iter; - - // Declare stack tmp variables - indent(out) << "NSString * fieldName;" << endl; - indent(out) << "SInt32 fieldType;" << endl; - indent(out) << "SInt32 fieldID;" << endl; - out << endl; - - indent(out) << "if (![inProtocol readStructBeginReturningName: NULL error: __thriftError]) return NO;" << endl; - - // Loop over reading in fields - indent(out) << "while (true)" << endl; - scope_up(out); - - // Read beginning field marker - indent(out) - << "if (![inProtocol readFieldBeginReturningName: &fieldName type: &fieldType fieldID: &fieldID error: __thriftError]) return NO;" - << endl; - - // Check for field STOP marker and break - indent(out) << "if (fieldType == TTypeSTOP) { " << endl; - indent_up(); - indent(out) << "break;" << endl; - indent_down(); - indent(out) << "}" << endl; - - // Switch statement on the field we are reading - indent(out) << "switch (fieldID)" << endl; - - scope_up(out); - - // Generate deserialization code for known cases - for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { - indent(out) << "case " << (*f_iter)->get_key() << ":" << endl; - indent_up(); - indent(out) << "if (fieldType == " << type_to_enum((*f_iter)->get_type()) << ") {" << endl; - indent_up(); - - generate_deserialize_field(out, *f_iter, "fieldValue"); - indent(out) << call_field_setter(*f_iter, "fieldValue") << endl; - - indent_down(); - out << indent() << "} else { " << endl; - if (log_unexpected_) { - out << indent() << " NSLog(@\"%s: field ID %i has unexpected type %i. Skipping.\", " - "__PRETTY_FUNCTION__, (int)fieldID, (int)fieldType);" << endl; - } - - out << indent() << " if (![TProtocolUtil skipType: fieldType onProtocol: inProtocol error: __thriftError]) return NO;" << endl; - out << indent() << "}" << endl << indent() << "break;" << endl; - indent_down(); - } - - // In the default case we skip the field - out << indent() << "default:" << endl; - if (log_unexpected_) { - out << indent() << " NSLog(@\"%s: unexpected field ID %i with type %i. Skipping.\", " - "__PRETTY_FUNCTION__, (int)fieldID, (int)fieldType);" << endl; - } - - out << indent() << " if (![TProtocolUtil skipType: fieldType onProtocol: inProtocol error: __thriftError]) return NO;" << endl; - - out << indent() << " break;" << endl; - - scope_down(out); - - // Read field end marker - indent(out) << "if (![inProtocol readFieldEnd: __thriftError]) return NO;" << endl; - - scope_down(out); - - out << indent() << "if (![inProtocol readStructEnd: __thriftError]) return NO;" << endl; - - // performs various checks (e.g. check that all required fields are set) - if (validate_required_) { - out << indent() << "if (![self validate: __thriftError]) return NO;" << endl; - } - - indent(out) << "return YES;" << endl; - - indent_down(); - out << indent() << "}" << endl << endl; -} - -/** - * Generates a function to write all the fields of the struct - * - * @param tstruct The struct definition - */ -void t_cocoa_generator::generate_cocoa_struct_writer(ostream& out, t_struct* tstruct) { - out << indent() << "- (BOOL) write: (id ) outProtocol error: (NSError *__autoreleasing *)__thriftError {" << endl; - indent_up(); - - string name = tstruct->get_name(); - const vector& fields = tstruct->get_members(); - vector::const_iterator f_iter; - - out << indent() << "if (![outProtocol writeStructBeginWithName: @\"" << name << "\" error: __thriftError]) return NO;" << endl; - - for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { - out << indent() << "if (_" << (*f_iter)->get_name() << "IsSet) {" << endl; - indent_up(); - bool null_allowed = type_can_be_null((*f_iter)->get_type()); - if (null_allowed) { - out << indent() << "if (_" << (*f_iter)->get_name() << " != nil) {" << endl; - indent_up(); - } - - indent(out) << "if (![outProtocol writeFieldBeginWithName: @\"" << (*f_iter)->get_name() - << "\" type: " << type_to_enum((*f_iter)->get_type()) - << " fieldID: " << (*f_iter)->get_key() << " error: __thriftError]) return NO;" << endl; - - // Write field contents - generate_serialize_field(out, *f_iter, "_" + (*f_iter)->get_name()); - - // Write field closer - indent(out) << "if (![outProtocol writeFieldEnd: __thriftError]) return NO;" << endl; - - if (null_allowed) { - scope_down(out); - } - scope_down(out); - } - // Write the struct map - out << indent() << "if (![outProtocol writeFieldStop: __thriftError]) return NO;" << endl - << indent() << "if (![outProtocol writeStructEnd: __thriftError]) return NO;" << endl; - - indent(out) << "return YES;" << endl; - - indent_down(); - out << indent() << "}" << endl << endl; -} - -/** - * Generates a function to write all the fields of the struct, which - * is a function result. These fields are only written if they are - * set, and only one of them can be set at a time. - * - * @param tstruct The struct definition - */ -void t_cocoa_generator::generate_cocoa_struct_result_writer(ostream& out, t_struct* tstruct) { - out << indent() << "- (BOOL) write: (id ) outProtocol error: (NSError *__autoreleasing *)__thriftError {" << endl; - indent_up(); - - string name = tstruct->get_name(); - const vector& fields = tstruct->get_members(); - vector::const_iterator f_iter; - - out << indent() << "if (![outProtocol writeStructBeginWithName: @\"" << name << "\" error: __thriftError]) return NO;" << endl; - - bool first = true; - for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { - if (first) { - first = false; - out << endl << indent() << "if "; - } else { - out << " else if "; - } - - out << "(_" << (*f_iter)->get_name() << "IsSet) {" << endl; - indent_up(); - - bool null_allowed = type_can_be_null((*f_iter)->get_type()); - if (null_allowed) { - out << indent() << "if (_" << (*f_iter)->get_name() << " != nil) {" << endl; - indent_up(); - } - - indent(out) << "if (![outProtocol writeFieldBeginWithName: @\"" << (*f_iter)->get_name() - << "\" type: " << type_to_enum((*f_iter)->get_type()) - << " fieldID: " << (*f_iter)->get_key() << " error: __thriftError]) return NO;" << endl; - - // Write field contents - generate_serialize_field(out, *f_iter, "_" + (*f_iter)->get_name()); - - // Write field closer - indent(out) << "if (![outProtocol writeFieldEnd: __thriftError]) return NO;" << endl; - - if (null_allowed) { - indent_down(); - indent(out) << "}" << endl; - } - - indent_down(); - indent(out) << "}"; - } - // Write the struct map - out << endl << indent() << "if (![outProtocol writeFieldStop: __thriftError]) return NO;" - << endl << indent() << "if (![outProtocol writeStructEnd: __thriftError]) return NO;" - << endl; - - indent(out) << "return YES;" << endl; - - indent_down(); - out << indent() << "}" << endl << endl; -} - -/** - * Generates a function to perform various checks - * (e.g. check that all required fields are set) - * - * @param tstruct The struct definition - */ -void t_cocoa_generator::generate_cocoa_struct_validator(ostream& out, t_struct* tstruct) { - out << indent() << "- (BOOL) validate: (NSError *__autoreleasing *)__thriftError {" << endl; - indent_up(); - - const vector& fields = tstruct->get_members(); - vector::const_iterator f_iter; - - out << indent() << "// check for required fields" << endl; - for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { - t_field* field = (*f_iter); - if ((*f_iter)->get_req() == t_field::T_REQUIRED) { - out << indent() << "if (!_" << field->get_name() << "IsSet) "; - scope_up(out); - indent(out) << "if (__thriftError) "; - scope_up(out); - out << indent() << "*__thriftError = [NSError errorWithDomain: TProtocolErrorDomain" << endl - << indent() << " code: TProtocolErrorUnknown" << endl - << indent() << " userInfo: @{TProtocolErrorExtendedErrorKey: @(TProtocolExtendedErrorMissingRequiredField)," << endl - << indent() << " TProtocolErrorFieldNameKey: @\"" << (*f_iter)->get_name() << "\"}];" << endl; - scope_down(out); - scope_down(out); - } - } - indent(out) << "return YES;" << endl; - indent_down(); - out << indent() << "}" << endl << endl; -} - -/** - * Generate property accessor methods for all fields in the struct. - * getter, setter, isset getter. - * - * @param tstruct The struct definition - */ -void t_cocoa_generator::generate_cocoa_struct_field_accessor_implementations(ostream& out, - t_struct* tstruct, - bool is_exception) { - (void)is_exception; - const vector& fields = tstruct->get_members(); - vector::const_iterator f_iter; - for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { - t_field* field = *f_iter; - t_type* type = get_true_type(field->get_type()); - std::string field_name = field->get_name(); - std::string cap_name = field_name; - cap_name[0] = toupper(cap_name[0]); - - // Simple setter - indent(out) << "- (void) set" << cap_name << ": (" << type_name(type, false, true) << ") " << field_name - << " {" << endl; - indent_up(); - indent(out) << "_" << field_name << " = " << field_name << ";" << endl; - indent(out) << "_" << field_name << "IsSet = YES;" << endl; - indent_down(); - indent(out) << "}" << endl << endl; - - // Unsetter - do we need this? - indent(out) << "- (void) unset" << cap_name << " {" << endl; - indent_up(); - if (type_can_be_null(type)) { - indent(out) << "_" << field_name << " = nil;" << endl; - } - indent(out) << "_" << field_name << "IsSet = NO;" << endl; - indent_down(); - indent(out) << "}" << endl << endl; - } -} - -/** - * Generates a description method for the given struct - * - * @param tstruct The struct definition - */ -void t_cocoa_generator::generate_cocoa_struct_description(ostream& out, t_struct* tstruct) { - - // Allow use of debugDescription so the app can add description via a cateogory/extension - if (debug_descriptions_) { - out << indent() << "- (NSString *) debugDescription {" << endl; - } - else { - out << indent() << "- (NSString *) description {" << endl; - } - indent_up(); - - out << indent() << "NSMutableString * ms = [NSMutableString stringWithString: @\"" - << cocoa_prefix_ << tstruct->get_name() << "(\"];" << endl; - - const vector& fields = tstruct->get_members(); - vector::const_iterator f_iter; - bool first = true; - for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { - if (first) { - first = false; - indent(out) << "[ms appendString: @\"" << (*f_iter)->get_name() << ":\"];" << endl; - } else { - indent(out) << "[ms appendString: @\"," << (*f_iter)->get_name() << ":\"];" << endl; - } - t_type* ttype = (*f_iter)->get_type(); - indent(out) << "[ms appendFormat: @\"" << format_string_for_type(ttype) << "\", " - << format_cast_for_type(ttype) << "_" << (*f_iter)->get_name() << "];" << endl; - } - out << indent() << "[ms appendString: @\")\"];" << endl << indent() - << "return [NSString stringWithString: ms];" << endl; - - indent_down(); - indent(out) << "}" << endl << endl; -} - -/** - * Generates a thrift service. In Objective-C this consists of a - * protocol definition, a client interface and a client implementation. - * - * @param tservice The service definition - */ -void t_cocoa_generator::generate_service(t_service* tservice) { - generate_cocoa_service_protocol(f_header_, tservice); - generate_cocoa_service_client_interface(f_header_, tservice); - generate_cocoa_service_server_interface(f_header_, tservice); - generate_cocoa_service_helpers(tservice); - generate_cocoa_service_client_implementation(f_impl_, tservice); - generate_cocoa_service_server_implementation(f_impl_, tservice); - if (async_clients_) { - generate_cocoa_service_async_protocol(f_header_, tservice); - generate_cocoa_service_client_async_interface(f_header_, tservice); - generate_cocoa_service_client_async_implementation(f_impl_, tservice); - } -} - -/** - * Generates structs for all the service return types - * - * @param tservice The service - */ -void t_cocoa_generator::generate_cocoa_service_helpers(t_service* tservice) { - vector functions = tservice->get_functions(); - vector::iterator f_iter; - for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) { - - t_struct* ts = (*f_iter)->get_arglist(); - - string qname = function_args_helper_struct_type(tservice, *f_iter); - - t_struct qname_ts = t_struct(ts->get_program(), qname); - - const vector& members = ts->get_members(); - vector::const_iterator m_iter; - for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) { - qname_ts.append(*m_iter); - } - - generate_cocoa_struct_interface(f_impl_, &qname_ts, false); - generate_cocoa_struct_implementation(f_impl_, &qname_ts, false, false); - generate_function_helpers(tservice, *f_iter); - } -} - -string t_cocoa_generator::function_result_helper_struct_type(t_service *tservice, t_function* tfunction) { - if (tfunction->is_oneway()) { - return tservice->get_name() + "_" + tfunction->get_name(); - } else { - return tservice->get_name() + "_" + tfunction->get_name() + "_result"; - } -} - -string t_cocoa_generator::function_args_helper_struct_type(t_service *tservice, t_function* tfunction) { - return tservice->get_name() + "_" + tfunction->get_name() + "_args"; -} - -/** - * Generates a struct and helpers for a function. - * - * @param tfunction The function - */ -void t_cocoa_generator::generate_function_helpers(t_service *tservice, t_function* tfunction) { - if (tfunction->is_oneway()) { - return; - } - - // create a result struct with a success field of the return type, - // and a field for each type of exception thrown - t_struct result(program_, function_result_helper_struct_type(tservice, tfunction)); - t_field success(tfunction->get_returntype(), "success", 0); - if (!tfunction->get_returntype()->is_void()) { - result.append(&success); - } - - t_struct* xs = tfunction->get_xceptions(); - const vector& fields = xs->get_members(); - vector::const_iterator f_iter; - for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { - result.append(*f_iter); - } - - // generate the result struct - generate_cocoa_struct_interface(f_impl_, &result, false); - generate_cocoa_struct_implementation(f_impl_, &result, false, true); -} - -/** - * Generates a service protocol definition. - * - * @param tservice The service to generate a protocol definition for - */ -void t_cocoa_generator::generate_cocoa_service_protocol(ostream& out, t_service* tservice) { - out << "@protocol " << cocoa_prefix_ << tservice->get_name() << " " << endl; - - vector functions = tservice->get_functions(); - vector::iterator f_iter; - for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) { - out << "- " << function_signature(*f_iter, true) << ";" - << " // throws "; - t_struct* xs = (*f_iter)->get_xceptions(); - const std::vector& xceptions = xs->get_members(); - vector::const_iterator x_iter; - for (x_iter = xceptions.begin(); x_iter != xceptions.end(); ++x_iter) { - out << type_name((*x_iter)->get_type()) + ", "; - } - out << "TException" << endl; - } - out << "@end" << endl << endl; -} - -/** - * Generates an asynchronous service protocol definition. - * - * @param tservice The service to generate a protocol definition for - */ -void t_cocoa_generator::generate_cocoa_service_async_protocol(ostream& out, t_service* tservice) { - out << "@protocol " << cocoa_prefix_ << tservice->get_name() << "Async" - << " " << endl; - - vector functions = tservice->get_functions(); - vector::iterator f_iter; - for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) { - out << "- " << async_function_signature(*f_iter, false) << ";" << endl; - if (promise_kit_) { - out << "- " << promise_function_signature(*f_iter) << ";" << endl; - } - } - out << "@end" << endl << endl; -} - -/** - * Generates a service client interface definition. - * - * @param tservice The service to generate a client interface definition for - */ -void t_cocoa_generator::generate_cocoa_service_client_interface(ostream& out, - t_service* tservice) { - out << "@interface " << cocoa_prefix_ << tservice->get_name() << "Client : TBaseClient <" - << cocoa_prefix_ << tservice->get_name() << "> " << endl; - - out << "- (id) initWithProtocol: (id ) protocol;" << endl; - out << "- (id) initWithInProtocol: (id ) inProtocol outProtocol: (id ) " - "outProtocol;" << endl; - out << "@end" << endl << endl; -} - -/** - * Generates a service client interface definition. - * - * @param tservice The service to generate a client interface definition for - */ -void t_cocoa_generator::generate_cocoa_service_client_async_interface(ostream& out, - t_service* tservice) { - out << "@interface " << cocoa_prefix_ << tservice->get_name() << "ClientAsync : TBaseClient <" - << cocoa_prefix_ << tservice->get_name() << "Async> " << endl - << endl; - - out << "- (id) initWithProtocolFactory: (id ) protocolFactory " - << "transportFactory: (id ) transportFactory;" << endl; - out << "@end" << endl << endl; -} - -/** - * Generates a service server interface definition. In other words, the TProcess implementation for - *the - * service definition. - * - * @param tservice The service to generate a client interface definition for - */ -void t_cocoa_generator::generate_cocoa_service_server_interface(ostream& out, - t_service* tservice) { - out << "@interface " << cocoa_prefix_ << tservice->get_name() - << "Processor : NSObject " << endl; - - out << "- (id) initWith" << tservice->get_name() << ": (id <" << cocoa_prefix_ - << tservice->get_name() << ">) service;" << endl; - out << "- (id<" << cocoa_prefix_ << tservice->get_name() << ">) service;" << endl; - - out << "@end" << endl << endl; -} - -void t_cocoa_generator::generate_cocoa_service_client_send_function_implementation( - ostream& out, - t_service *tservice, - t_function* tfunction, - bool needs_protocol) { - string funname = tfunction->get_name(); - - t_function send_function(g_type_bool, - string("send_") + tfunction->get_name(), - tfunction->get_arglist()); - - string argsname = function_args_helper_struct_type(tservice, tfunction); - - // Open function - indent(out) << "- (BOOL) send_" << tfunction->get_name() << argument_list(tfunction->get_arglist(), needs_protocol ? "outProtocol" : "", true) << endl; - scope_up(out); - - // Serialize the request - out << indent() << "if (![outProtocol writeMessageBeginWithName: @\"" << funname << "\"" - << (tfunction->is_oneway() ? " type: TMessageTypeONEWAY" : " type: TMessageTypeCALL") - << " sequenceID: 0 error: __thriftError]) return NO;" << endl; - - out << indent() << "if (![outProtocol writeStructBeginWithName: @\"" << argsname - << "\" error: __thriftError]) return NO;" << endl; - - // write out function parameters - t_struct* arg_struct = tfunction->get_arglist(); - const vector& fields = arg_struct->get_members(); - vector::const_iterator fld_iter; - for (fld_iter = fields.begin(); fld_iter != fields.end(); ++fld_iter) { - string fieldName = (*fld_iter)->get_name(); - if (type_can_be_null((*fld_iter)->get_type())) { - out << indent() << "if (" << fieldName << " != nil)"; - scope_up(out); - } - out << indent() << "if (![outProtocol writeFieldBeginWithName: @\"" << fieldName - << "\"" - " type: " << type_to_enum((*fld_iter)->get_type()) - << " fieldID: " << (*fld_iter)->get_key() << " error: __thriftError]) return NO;" << endl; - - generate_serialize_field(out, *fld_iter, fieldName); - - out << indent() << "if (![outProtocol writeFieldEnd: __thriftError]) return NO;" << endl; - - if (type_can_be_null((*fld_iter)->get_type())) { - indent_down(); - out << indent() << "}" << endl; - } - } - - out << indent() << "if (![outProtocol writeFieldStop: __thriftError]) return NO;" << endl; - out << indent() << "if (![outProtocol writeStructEnd: __thriftError]) return NO;" << endl; - out << indent() << "if (![outProtocol writeMessageEnd: __thriftError]) return NO;" << endl; - out << indent() << "return YES;" << endl; - scope_down(out); - out << endl; -} - -void t_cocoa_generator::generate_cocoa_service_client_recv_function_implementation( - ostream& out, - t_service* tservice, - t_function* tfunction, - bool needs_protocol) { - - - // Open function - indent(out) << "- (BOOL) recv_" << tfunction->get_name(); - if (!tfunction->get_returntype()->is_void()) { - out << ": (" << type_name(tfunction->get_returntype(), false, true) << " *) result "; - if (needs_protocol) { - out << "protocol"; - } else { - out << "error"; - } - } - if (needs_protocol) { - out << ": (id) inProtocol error"; - } - out << ": (NSError *__autoreleasing *)__thriftError" << endl; - scope_up(out); - - // TODO(mcslee): Message validation here, was the seqid etc ok? - - // check for an exception - out << indent() << "NSError *incomingException = [self checkIncomingMessageException: inProtocol];" << endl - << indent() << "if (incomingException)"; - scope_up(out); - out << indent() << "if (__thriftError)"; - scope_up(out); - out << indent() << "*__thriftError = incomingException;" << endl; - scope_down(out); - out << indent() << "return NO;" << endl; - scope_down(out); - - // FIXME - could optimize here to reduce creation of temporary objects. - string resultname = function_result_helper_struct_type(tservice, tfunction); - out << indent() << cocoa_prefix_ << resultname << " * resulter = [" << cocoa_prefix_ << resultname << " new];" << endl; - indent(out) << "if (![resulter read: inProtocol error: __thriftError]) return NO;" << endl; - indent(out) << "if (![inProtocol readMessageEnd: __thriftError]) return NO;" << endl; - - // Careful, only return _result if not a void function - if (!tfunction->get_returntype()->is_void()) { - out << indent() << "if (resulter.successIsSet)"; - scope_up(out); - out << indent() << "*result = resulter.success;" << endl; - out << indent() << "return YES;" << endl; - scope_down(out); - } - - t_struct* xs = tfunction->get_xceptions(); - const std::vector& xceptions = xs->get_members(); - vector::const_iterator x_iter; - for (x_iter = xceptions.begin(); x_iter != xceptions.end(); ++x_iter) { - out << indent() << "if (resulter." << (*x_iter)->get_name() << "IsSet)"; - scope_up(out); - out << indent() << "if (__thriftError)"; - scope_up(out); - out << indent() << "*__thriftError = [resulter " << (*x_iter)->get_name() << "];" << endl; - scope_down(out); - out << indent() << "return NO;" << endl; - scope_down(out); - } - - // If you get here it's an exception, unless a void function - if (tfunction->get_returntype()->is_void()) { - indent(out) << "return YES;" << endl; - } else { - out << indent() << "if (__thriftError)"; - scope_up(out); - out << indent() << "*__thriftError = [NSError errorWithDomain: TApplicationErrorDomain" << endl - << indent() << " code: TApplicationErrorMissingResult" << endl - << indent() << " userInfo: @{TApplicationErrorMethodKey: @\"" - << tfunction->get_name() << "\"}];" << endl; - scope_down(out); - out << indent() << "return NO;" << endl; - } - - // Close function - scope_down(out); - out << endl; -} - -/** - * Generates an invocation of a given 'send_' function. - * - * @param tfunction The service to generate an implementation for - */ -void t_cocoa_generator::generate_cocoa_service_client_send_function_invocation( - ostream& out, - t_function* tfunction) { - - t_struct* arg_struct = tfunction->get_arglist(); - const vector& fields = arg_struct->get_members(); - vector::const_iterator fld_iter; - out << indent() << "if (![self send_" << tfunction->get_name(); - bool first = true; - for (fld_iter = fields.begin(); fld_iter != fields.end(); ++fld_iter) { - string fieldName = (*fld_iter)->get_name(); - out << " "; - if (first) { - first = false; - out << ": " << fieldName; - } else { - out << fieldName << ": " << fieldName; - } - } - if (!fields.empty()) { - out << " error"; - } - out << ": __thriftError]) " << invalid_return_statement(tfunction) << endl; -} - -/** - * Generates an invocation of a given 'send_' function. - * - * @param tfunction The service to generate an implementation for - */ -void t_cocoa_generator::generate_cocoa_service_client_send_async_function_invocation( - ostream& out, - t_function* tfunction, - string failureBlockName) { - - t_struct* arg_struct = tfunction->get_arglist(); - const vector& fields = arg_struct->get_members(); - vector::const_iterator fld_iter; - out << indent() << "if (![self send_" << tfunction->get_name(); - bool first = true; - for (fld_iter = fields.begin(); fld_iter != fields.end(); ++fld_iter) { - string fieldName = (*fld_iter)->get_name(); - out << " "; - if (first) { - first = false; - out << ": " << fieldName; - } else { - out << fieldName << ": " << fieldName; - } - } - if (!fields.empty()) { - out << " protocol"; - } - out << ": protocol error: &thriftError]) "; - scope_up(out); - out << indent() << failureBlockName << "(thriftError);" << endl - << indent() << "return;" << endl; - scope_down(out); -} - -/** - * Generates a service client implementation. - * - * @param tservice The service to generate an implementation for - */ -void t_cocoa_generator::generate_cocoa_service_client_implementation(ostream& out, - t_service* tservice) { - - string name = cocoa_prefix_ + tservice->get_name() + "Client"; - - out << "@interface " << name << " () "; - scope_up(out); - out << endl; - out << indent() << "id inProtocol;" << endl; - out << indent() << "id outProtocol;" << endl; - out << endl; - scope_down(out); - out << endl; - out << "@end" << endl << endl; - - out << "@implementation " << name << endl; - - // initializers - out << "- (id) initWithProtocol: (id ) protocol" << endl; - scope_up(out); - out << indent() << "return [self initWithInProtocol: protocol outProtocol: protocol];" << endl; - scope_down(out); - out << endl; - - out << "- (id) initWithInProtocol: (id ) anInProtocol outProtocol: (id ) " - "anOutProtocol" << endl; - scope_up(out); - out << indent() << "self = [super init];" << endl; - out << indent() << "if (self) "; - scope_up(out); - out << indent() << "inProtocol = anInProtocol;" << endl; - out << indent() << "outProtocol = anOutProtocol;" << endl; - scope_down(out); - out << indent() << "return self;" << endl; - scope_down(out); - out << endl; - - // generate client method implementations - vector functions = tservice->get_functions(); - vector::const_iterator f_iter; - for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) { - - generate_cocoa_service_client_send_function_implementation(out, tservice, *f_iter, false); - - if (!(*f_iter)->is_oneway()) { - generate_cocoa_service_client_recv_function_implementation(out, tservice, *f_iter, false); - } - - // Open function - indent(out) << "- " << function_signature(*f_iter, true) << endl; - scope_up(out); - generate_cocoa_service_client_send_function_invocation(out, *f_iter); - - out << indent() << "if (![[outProtocol transport] flush: __thriftError]) " << invalid_return_statement(*f_iter) << endl; - if (!(*f_iter)->is_oneway()) { - if ((*f_iter)->get_returntype()->is_void()) { - out << indent() << "if (![self recv_" << (*f_iter)->get_name() << ": __thriftError]) return NO;" << endl; - out << indent() << "return YES;" << endl; - } else { - out << indent() << type_name((*f_iter)->get_returntype(), false, true) << " __result;" << endl - << indent() << "if (![self recv_" << (*f_iter)->get_name() << ": &__result error: __thriftError]) " - << invalid_return_statement(*f_iter) << endl; - if (type_can_be_null((*f_iter)->get_returntype())) { - out << indent() << "return __result;" << endl; - } else { - out << indent() << "return @(__result);" << endl; - } - } - } - else { - out << indent() << "return YES;" << endl; - } - scope_down(out); - out << endl; - } - - out << "@end" << endl << endl; -} - -/** - * Generates a service client implementation for its asynchronous interface. - * - * @param tservice The service to generate an implementation for - */ -void t_cocoa_generator::generate_cocoa_service_client_async_implementation(ostream& out, - t_service* tservice) { - - string name = cocoa_prefix_ + tservice->get_name() + "ClientAsync"; - - out << "@interface " << name << " () "; - scope_up(out); - out << endl; - out << indent() << "id protocolFactory;" << endl; - out << indent() << "id transportFactory;" << endl; - out << endl; - scope_down(out); - out << endl; - out << "@end" << endl << endl; - - - out << "@implementation " << name << endl - << endl << "- (id) initWithProtocolFactory: (id ) aProtocolFactory " - "transportFactory: (id ) aTransportFactory;" << endl; - - scope_up(out); - out << indent() << "self = [super init];" << endl; - out << indent() << "if (self) {" << endl; - out << indent() << " protocolFactory = aProtocolFactory;" << endl; - out << indent() << " transportFactory = aTransportFactory;" << endl; - out << indent() << "}" << endl; - out << indent() << "return self;" << endl; - scope_down(out); - out << endl; - - // generate client method implementations - vector functions = tservice->get_functions(); - vector::const_iterator f_iter; - for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) { - - generate_cocoa_service_client_send_function_implementation(out, tservice, *f_iter, true); - - if (!(*f_iter)->is_oneway()) { - generate_cocoa_service_client_recv_function_implementation(out, tservice, *f_iter, true); - } - - // Open function - indent(out) << "- " << async_function_signature(*f_iter, false) << endl; - scope_up(out); - - out << indent() << "NSError *thriftError;" << endl - << indent() << "id transport = [transportFactory newTransport];" << endl - << indent() << "id protocol = [protocolFactory newProtocolOnTransport:transport];" << endl - << endl; - - generate_cocoa_service_client_send_async_function_invocation(out, *f_iter, "failureBlock"); - - out << indent() << "[transport flushWithCompletion:^{" << endl; - indent_up(); - - if (!(*f_iter)->is_oneway()) { - out << indent() << "NSError *thriftError;" << endl; - - if (!(*f_iter)->get_returntype()->is_void()) { - out << indent() << type_name((*f_iter)->get_returntype()) << " result;" << endl; - } - out << indent() << "if (![self recv_" << (*f_iter)->get_name(); - if (!(*f_iter)->get_returntype()->is_void()) { - out << ": &result protocol"; - } - out << ": protocol error: &thriftError]) "; - scope_up(out); - out << indent() << "failureBlock(thriftError);" << endl - << indent() << "return;" << endl; - scope_down(out); - } - - out << indent() << "responseBlock("; - if (!(*f_iter)->is_oneway() && !(*f_iter)->get_returntype()->is_void()) { - out << "result"; - } - out << ");" << endl; - - indent_down(); - - out << indent() << "} failure:failureBlock];" << endl; - - scope_down(out); - - out << endl; - - // Promise function - if (promise_kit_) { - - indent(out) << "- " << promise_function_signature(*f_iter) << endl; - scope_up(out); - - out << indent() << "return [AnyPromise promiseWithResolverBlock:^(PMKResolver resolver) {" << endl; - indent_up(); - - out << indent() << "NSError *thriftError;" << endl - << indent() << "id transport = [transportFactory newTransport];" << endl - << indent() << "id protocol = [protocolFactory newProtocolOnTransport:transport];" << endl - << endl; - - generate_cocoa_service_client_send_async_function_invocation(out, *f_iter, "resolver"); - - out << indent() << "[transport flushWithCompletion:^{" << endl; - indent_up(); - - if (!(*f_iter)->is_oneway()) { - out << indent() << "NSError *thriftError;" << endl; - - if (!(*f_iter)->get_returntype()->is_void()) { - out << indent() << type_name((*f_iter)->get_returntype()) << " result;" << endl; - } - out << indent() << "if (![self recv_" << (*f_iter)->get_name(); - if (!(*f_iter)->get_returntype()->is_void()) { - out << ": &result protocol"; - } - out << ": protocol error: &thriftError]) "; - scope_up(out); - out << indent() << "resolver(thriftError);" << endl - << indent() << "return;" << endl; - scope_down(out); - } - - out << indent() << "resolver("; - if ((*f_iter)->is_oneway() || (*f_iter)->get_returntype()->is_void()) { - out << "@YES"; - } else if (type_can_be_null((*f_iter)->get_returntype())) { - out << "result"; - } else { - out << "@(result)"; - } - out << ");" << endl; - - indent_down(); - - out << indent() << "} failure:^(NSError *error) {" << endl; - indent_up(); - out << indent() << "resolver(error);" << endl; - indent_down(); - out << indent() << "}];" << endl; - - indent_down(); - out << indent() << "}];" << endl; - - scope_down(out); - - out << endl; - - } - - } - - out << "@end" << endl << endl; -} - -/** - * Generates a service server implementation. In other words the actual TProcessor implementation - * for the service. - * - * @param tservice The service to generate an implementation for - */ -void t_cocoa_generator::generate_cocoa_service_server_implementation(ostream& out, - t_service* tservice) { - - string name = cocoa_prefix_ + tservice->get_name() + "Processor"; - - out << "@interface " << name << " () "; - - scope_up(out); - out << indent() << "id <" << cocoa_prefix_ << tservice->get_name() << "> service;" << endl; - out << indent() << "NSDictionary * methodMap;" << endl; - scope_down(out); - - out << "@end" << endl << endl; - - out << "@implementation " << name << endl; - - // initializer - out << endl; - out << "- (id) initWith" << tservice->get_name() << ": (id <" << cocoa_prefix_ << tservice->get_name() << ">) aService" << endl; - scope_up(out); - out << indent() << "self = [super init];" << endl; - out << indent() << "if (self) "; - scope_up(out); - out << indent() << "service = aService;" << endl; - out << indent() << "methodMap = [NSMutableDictionary dictionary];" << endl; - - // generate method map for routing incoming calls - vector functions = tservice->get_functions(); - vector::const_iterator f_iter; - for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) { - string funname = (*f_iter)->get_name(); - scope_up(out); - out << indent() << "SEL s = @selector(process_" << funname << "_withSequenceID:inProtocol:outProtocol:error:);" << endl; - out << indent() << "NSMethodSignature * sig = [self methodSignatureForSelector: s];" << endl; - out << indent() << "NSInvocation * invocation = [NSInvocation invocationWithMethodSignature: sig];" << endl; - out << indent() << "[invocation setSelector: s];" << endl; - out << indent() << "[invocation retainArguments];" << endl; - out << indent() << "[methodMap setValue: invocation forKey: @\"" << funname << "\"];" << endl; - scope_down(out); - } - scope_down(out); - out << indent() << "return self;" << endl; - scope_down(out); - - // implementation of the 'service' method which returns the service associated with this - // processor - out << endl; - out << indent() << "- (id<" << cocoa_prefix_ << tservice->get_name() << ">) service" << endl; - out << indent() << "{" << endl; - out << indent() << " return service;" << endl; - out << indent() << "}" << endl; - - // implementation of the TProcess method, which dispatches the incoming call using the method map - out << endl; - out << indent() << "- (BOOL) processOnInputProtocol: (id ) inProtocol" << endl; - out << indent() << " outputProtocol: (id ) outProtocol" << endl; - out << indent() << " error: (NSError *__autoreleasing *)__thriftError" << endl; - out << indent() << "{" << endl; - out << indent() << " NSString * messageName;" << endl; - out << indent() << " SInt32 messageType;" << endl; - out << indent() << " SInt32 seqID;" << endl; - out << indent() << " if (![inProtocol readMessageBeginReturningName: &messageName" << endl; - out << indent() << " type: &messageType" << endl; - out << indent() << " sequenceID: &seqID" << endl; - out << indent() << " error: __thriftError]) return NO;" << endl; - out << indent() << " NSInvocation * invocation = [methodMap valueForKey: messageName];" << endl; - out << indent() << " if (invocation == nil) {" << endl; - out << indent() << " if (![TProtocolUtil skipType: TTypeSTRUCT onProtocol: inProtocol error: __thriftError]) return NO;" << endl; - out << indent() << " if (![inProtocol readMessageEnd: __thriftError]) return NO;" << endl; - out << indent() << " NSError * x = [NSError errorWithDomain: TApplicationErrorDomain" << endl; - out << indent() << " code: TApplicationErrorUnknownMethod" << endl; - out << indent() << " userInfo: @{TApplicationErrorMethodKey: messageName}];" << endl; - out << indent() << " if (![outProtocol writeMessageBeginWithName: messageName" << endl; - out << indent() << " type: TMessageTypeEXCEPTION" << endl; - out << indent() << " sequenceID: seqID" << endl; - out << indent() << " error: __thriftError]) return NO;" << endl; - out << indent() << " if (![x write: outProtocol error: __thriftError]) return NO;" << endl; - out << indent() << " if (![outProtocol writeMessageEnd: __thriftError]) return NO;" << endl; - out << indent() << " if (![[outProtocol transport] flush: __thriftError]) return NO;" << endl; - out << indent() << " return YES;" << endl; - out << indent() << " }" << endl; - out << indent() << " // NSInvocation does not conform to NSCopying protocol" << endl; - out << indent() << " NSInvocation * i = [NSInvocation invocationWithMethodSignature: " - "[invocation methodSignature]];" << endl; - out << indent() << " [i setSelector: [invocation selector]];" << endl; - out << indent() << " [i setArgument: &seqID atIndex: 2];" << endl; - out << indent() << " [i setArgument: &inProtocol atIndex: 3];" << endl; - out << indent() << " [i setArgument: &outProtocol atIndex: 4];" << endl; - out << indent() << " [i setArgument: &__thriftError atIndex: 5];" << endl; - out << indent() << " [i setTarget: self];" << endl; - out << indent() << " [i invoke];" << endl; - out << indent() << " return YES;" << endl; - out << indent() << "}" << endl; - - // generate a process_XXXX method for each service function, which reads args, calls the service, - // and writes results - functions = tservice->get_functions(); - for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) { - out << endl; - string funname = (*f_iter)->get_name(); - out << indent() << "- (BOOL) process_" << funname - << "_withSequenceID: (SInt32) seqID inProtocol: (id) inProtocol outProtocol: " - "(id) outProtocol error:(NSError *__autoreleasing *)__thriftError" << endl; - scope_up(out); - string argstype = cocoa_prefix_ + function_args_helper_struct_type(tservice, *f_iter); - out << indent() << argstype << " * args = [" << argstype << " new];" << endl; - out << indent() << "if (![args read: inProtocol error: __thriftError]) return NO;" << endl; - out << indent() << "if (![inProtocol readMessageEnd: __thriftError]) return NO;" << endl; - - // prepare the result if not oneway - if (!(*f_iter)->is_oneway()) { - string resulttype = cocoa_prefix_ + function_result_helper_struct_type(tservice, *f_iter); - out << indent() << resulttype << " * result = [" << resulttype << " new];" << endl; - } - - // make the call to the actual service object - out << indent(); - if ((*f_iter)->get_returntype()->is_void()) { - out << "BOOL"; - } else if (type_can_be_null((*f_iter)->get_returntype())) { - out << type_name((*f_iter)->get_returntype(), false, true); - } else { - out << "NSNumber *"; - } - out << " serviceResult = "; - if ((*f_iter)->get_returntype()->get_true_type()->is_container()) { - out << "(" << type_name((*f_iter)->get_returntype(), false, true) << ")"; - } - out << "[service " << funname; - // supplying arguments - t_struct* arg_struct = (*f_iter)->get_arglist(); - const vector& fields = arg_struct->get_members(); - vector::const_iterator fld_iter; - bool first = true; - for (fld_iter = fields.begin(); fld_iter != fields.end(); ++fld_iter) { - string fieldName = (*fld_iter)->get_name(); - if (first) { - first = false; - out << ": [args " << fieldName << "]"; - } else { - out << " " << fieldName << ": [args " << fieldName << "]"; - } - } - if (!fields.empty()) { - out << " error"; - } - out << ": __thriftError];" << endl; - out << indent() << "if (!serviceResult) return NO;" << endl; - if (!(*f_iter)->get_returntype()->is_void()) { - out << indent() << "[result setSuccess: " << unbox((*f_iter)->get_returntype(), "serviceResult") << "];" << endl; - } - - // write out the result if not oneway - if (!(*f_iter)->is_oneway()) { - out << indent() << "if (![outProtocol writeMessageBeginWithName: @\"" << funname << "\"" << endl; - out << indent() << " type: TMessageTypeREPLY" << endl; - out << indent() << " sequenceID: seqID" << endl; - out << indent() << " error: __thriftError]) return NO;" << endl; - out << indent() << "if (![result write: outProtocol error: __thriftError]) return NO;" << endl; - out << indent() << "if (![outProtocol writeMessageEnd: __thriftError]) return NO;" << endl; - out << indent() << "if (![[outProtocol transport] flush: __thriftError]) return NO;" << endl; - } - out << indent() << "return YES;" << endl; - - scope_down(out); - } - - out << "@end" << endl << endl; -} - -/** - * Deserializes a field of any type. - * - * @param tfield The field - * @param fieldName The variable name for this field - */ -void t_cocoa_generator::generate_deserialize_field(ostream& out, - t_field* tfield, - string fieldName) { - t_type* type = get_true_type(tfield->get_type()); - - if (type->is_void()) { - throw "CANNOT GENERATE DESERIALIZE CODE FOR void TYPE: " + tfield->get_name(); - } - - if (type->is_struct() || type->is_xception()) { - generate_deserialize_struct(out, (t_struct*)type, fieldName); - } else if (type->is_container()) { - generate_deserialize_container(out, type, fieldName); - } else if (type->is_base_type() || type->is_enum()) { - indent(out) << type_name(type) << " " << fieldName << ";" << endl; - indent(out) << "if (![inProtocol "; - - if (type->is_base_type()) { - t_base_type::t_base tbase = ((t_base_type*)type)->get_base(); - switch (tbase) { - case t_base_type::TYPE_VOID: - throw "compiler error: cannot serialize void field in a struct: " + tfield->get_name(); - break; - case t_base_type::TYPE_STRING: - if (type->is_binary()) { - out << "readBinary:&" << fieldName << " error: __thriftError]"; - } else { - out << "readString:&" << fieldName << " error: __thriftError]"; - } - break; - case t_base_type::TYPE_BOOL: - out << "readBool:&" << fieldName << " error: __thriftError]"; - break; - case t_base_type::TYPE_I8: - out << "readByte:(UInt8 *)&" << fieldName << " error: __thriftError]"; - break; - case t_base_type::TYPE_I16: - out << "readI16:&" << fieldName << " error: __thriftError]"; - break; - case t_base_type::TYPE_I32: - out << "readI32:&" << fieldName << " error: __thriftError]"; - break; - case t_base_type::TYPE_I64: - out << "readI64:&" << fieldName << " error: __thriftError]"; - break; - case t_base_type::TYPE_DOUBLE: - out << "readDouble:&" << fieldName << " error: __thriftError]"; - break; - default: - throw "compiler error: no Objective-C name for base type " - + t_base_type::t_base_name(tbase); - } - } else if (type->is_enum()) { - out << "readI32:&" << fieldName << " error: __thriftError]"; - } - out << ") return NO;" << endl; - } else { - printf("DO NOT KNOW HOW TO DESERIALIZE FIELD '%s' TYPE '%s'\n", - tfield->get_name().c_str(), - type_name(type).c_str()); - } -} - -/** - * Generates an unserializer for a struct, allocates the struct and invokes read: - */ -void t_cocoa_generator::generate_deserialize_struct(ostream& out, - t_struct* tstruct, - string fieldName) { - indent(out) << type_name(tstruct) << fieldName << " = [[" << type_name(tstruct, true) - << " alloc] init];" << endl; - indent(out) << "if (![" << fieldName << " read: inProtocol error: __thriftError]) return NO;" << endl; -} - -/** - * Deserializes a container by reading its size and then iterating - */ -void t_cocoa_generator::generate_deserialize_container(ostream& out, - t_type* ttype, - string fieldName) { - string size = tmp("_size"); - indent(out) << "SInt32 " << size << ";" << endl; - - // Declare variables, read header - if (ttype->is_map()) { - indent(out) << "if (![inProtocol readMapBeginReturningKeyType: NULL valueType: NULL size: &" << size << " error: __thriftError]) return NO;" << endl; - indent(out) << "NSMutableDictionary * " << fieldName - << " = [[NSMutableDictionary alloc] initWithCapacity: " << size << "];" << endl; - } else if (ttype->is_set()) { - indent(out) << "if (![inProtocol readSetBeginReturningElementType: NULL size: &" << size << " error: __thriftError]) return NO;" - << endl; - indent(out) << "NSMutableSet * " << fieldName - << " = [[NSMutableSet alloc] initWithCapacity: " << size << "];" << endl; - } else if (ttype->is_list()) { - indent(out) << "if (![inProtocol readListBeginReturningElementType: NULL size: &" << size << " error: __thriftError]) return NO;" - << endl; - indent(out) << "NSMutableArray * " << fieldName - << " = [[NSMutableArray alloc] initWithCapacity: " << size << "];" << endl; - } - // FIXME - the code above does not verify that the element types of - // the containers being read match the element types of the - // containers we are reading into. Does that matter? - - // For loop iterates over elements - string i = tmp("_i"); - indent(out) << "int " << i << ";" << endl << indent() << "for (" << i << " = 0; " << i << " < " - << size << "; " - << "++" << i << ")" << endl; - - scope_up(out); - - if (ttype->is_map()) { - generate_deserialize_map_element(out, (t_map*)ttype, fieldName); - } else if (ttype->is_set()) { - generate_deserialize_set_element(out, (t_set*)ttype, fieldName); - } else if (ttype->is_list()) { - generate_deserialize_list_element(out, (t_list*)ttype, fieldName); - } - - scope_down(out); - - // Read container end - if (ttype->is_map()) { - indent(out) << "if (![inProtocol readMapEnd: __thriftError]) return NO;" << endl; - } else if (ttype->is_set()) { - indent(out) << "if (![inProtocol readSetEnd: __thriftError]) return NO;" << endl; - } else if (ttype->is_list()) { - indent(out) << "if (![inProtocol readListEnd: __thriftError]) return NO;" << endl; - } -} - -/** - * Take a variable of a given type and wrap it in code to make it - * suitable for putting into a container, if necessary. Basically, - * wrap scaler primitives in NSNumber objects. - */ -string t_cocoa_generator::box(t_type* ttype, string field_name) { - - ttype = get_true_type(ttype); - if (ttype->is_enum()) { - return "@(" + field_name + ")"; - } else if (ttype->is_base_type()) { - t_base_type::t_base tbase = ((t_base_type*)ttype)->get_base(); - switch (tbase) { - case t_base_type::TYPE_VOID: - throw "can't box void"; - case t_base_type::TYPE_BOOL: - case t_base_type::TYPE_I8: - case t_base_type::TYPE_I16: - case t_base_type::TYPE_I32: - case t_base_type::TYPE_I64: - case t_base_type::TYPE_DOUBLE: - return "@(" + field_name + ")"; - default: - break; - } - } - - // do nothing - return field_name; -} - -/** - * Extracts the actual value from a boxed value - */ -string t_cocoa_generator::unbox(t_type* ttype, string field_name) { - ttype = get_true_type(ttype); - if (ttype->is_enum()) { - return "[" + field_name + " intValue]"; - } else if (ttype->is_base_type()) { - t_base_type::t_base tbase = ((t_base_type*)ttype)->get_base(); - switch (tbase) { - case t_base_type::TYPE_VOID: - throw "can't unbox void"; - case t_base_type::TYPE_BOOL: - return "[" + field_name + " boolValue]"; - case t_base_type::TYPE_I8: - return "((SInt8)[" + field_name + " charValue])"; - case t_base_type::TYPE_I16: - return "((SInt16)[" + field_name + " shortValue])"; - case t_base_type::TYPE_I32: - return "((SInt32)[" + field_name + " longValue])"; - case t_base_type::TYPE_I64: - return "((SInt64)[" + field_name + " longLongValue])"; - case t_base_type::TYPE_DOUBLE: - return "[" + field_name + " doubleValue]"; - default: - break; - } - } - - // do nothing - return field_name; -} - -/** - * Generates code to deserialize a map element - */ -void t_cocoa_generator::generate_deserialize_map_element(ostream& out, - t_map* tmap, - string fieldName) { - string key = tmp("_key"); - string val = tmp("_val"); - t_type* keyType = tmap->get_key_type(); - t_type* valType = tmap->get_val_type(); - t_field fkey(keyType, key); - t_field fval(valType, val); - - generate_deserialize_field(out, &fkey, key); - generate_deserialize_field(out, &fval, val); - - indent(out) << "[" << fieldName << " setObject: " << box(valType, val) - << " forKey: " << box(keyType, key) << "];" << endl; -} - -/** - * Deserializes a set element - */ -void t_cocoa_generator::generate_deserialize_set_element(ostream& out, - t_set* tset, - string fieldName) { - string elem = tmp("_elem"); - t_type* type = tset->get_elem_type(); - t_field felem(type, elem); - - generate_deserialize_field(out, &felem, elem); - - indent(out) << "[" << fieldName << " addObject: " << box(type, elem) << "];" << endl; -} - -/** - * Deserializes a list element - */ -void t_cocoa_generator::generate_deserialize_list_element(ostream& out, - t_list* tlist, - string fieldName) { - string elem = tmp("_elem"); - t_type* type = tlist->get_elem_type(); - t_field felem(type, elem); - - generate_deserialize_field(out, &felem, elem); - - indent(out) << "[" << fieldName << " addObject: " << box(type, elem) << "];" << endl; -} - -/** - * Serializes a field of any type. - * - * @param tfield The field to serialize - * @param fieldName Name to of the variable holding the field - */ -void t_cocoa_generator::generate_serialize_field(ostream& out, t_field* tfield, string fieldName) { - t_type* type = get_true_type(tfield->get_type()); - - // Do nothing for void types - if (type->is_void()) { - throw "CANNOT GENERATE SERIALIZE CODE FOR void TYPE: " + tfield->get_name(); - } - - if (type->is_struct() || type->is_xception()) { - generate_serialize_struct(out, (t_struct*)type, fieldName); - } else if (type->is_container()) { - generate_serialize_container(out, type, fieldName); - } else if (type->is_base_type() || type->is_enum()) { - indent(out) << "if (![outProtocol "; - - if (type->is_base_type()) { - t_base_type::t_base tbase = ((t_base_type*)type)->get_base(); - switch (tbase) { - case t_base_type::TYPE_VOID: - throw "compiler error: cannot serialize void field in a struct: " + fieldName; - break; - case t_base_type::TYPE_STRING: - if (type->is_binary()) { - out << "writeBinary: " << fieldName << " error: __thriftError]"; - } else { - out << "writeString: " << fieldName << " error: __thriftError]"; - } - break; - case t_base_type::TYPE_BOOL: - out << "writeBool: " << fieldName << " error: __thriftError]"; - break; - case t_base_type::TYPE_I8: - out << "writeByte: (UInt8)" << fieldName << " error: __thriftError]"; - break; - case t_base_type::TYPE_I16: - out << "writeI16: " << fieldName << " error: __thriftError]"; - break; - case t_base_type::TYPE_I32: - out << "writeI32: " << fieldName << " error: __thriftError]"; - break; - case t_base_type::TYPE_I64: - out << "writeI64: " << fieldName << " error: __thriftError]"; - break; - case t_base_type::TYPE_DOUBLE: - out << "writeDouble: " << fieldName << " error: __thriftError]"; - break; - default: - throw "compiler error: no Objective-C name for base type " - + t_base_type::t_base_name(tbase); - } - } else if (type->is_enum()) { - out << "writeI32: " << fieldName << " error: __thriftError]"; - } - out << ") return NO;" << endl; - } else { - printf("DO NOT KNOW HOW TO SERIALIZE FIELD '%s' TYPE '%s'\n", - tfield->get_name().c_str(), - type_name(type).c_str()); - } -} - -/** - * Serialize a struct. - * - * @param tstruct The struct to serialize - * @param fieldName Name of variable holding struct - */ -void t_cocoa_generator::generate_serialize_struct(ostream& out, - t_struct* tstruct, - string fieldName) { - (void)tstruct; - out << indent() << "if (![" << fieldName << " write: outProtocol error: __thriftError]) return NO;" << endl; -} - -/** - * Serializes a container by writing its size then the elements. - * - * @param ttype The type of container - * @param fieldName Name of variable holding container - */ -void t_cocoa_generator::generate_serialize_container(ostream& out, - t_type* ttype, - string fieldName) { - scope_up(out); - - if (ttype->is_map()) { - indent(out) << "if (![outProtocol writeMapBeginWithKeyType: " - << type_to_enum(((t_map*)ttype)->get_key_type()) - << " valueType: " << type_to_enum(((t_map*)ttype)->get_val_type()) << " size: (SInt32)[" - << fieldName << " count] error: __thriftError]) return NO;" << endl; - } else if (ttype->is_set()) { - indent(out) << "if (![outProtocol writeSetBeginWithElementType: " - << type_to_enum(((t_set*)ttype)->get_elem_type()) << " size: (SInt32)[" << fieldName - << " count] error: __thriftError]) return NO;" << endl; - } else if (ttype->is_list()) { - indent(out) << "if (![outProtocol writeListBeginWithElementType: " - << type_to_enum(((t_list*)ttype)->get_elem_type()) << " size: (SInt32)[" << fieldName - << " count] error: __thriftError]) return NO;" << endl; - } - - string iter = tmp("_iter"); - string key; - if (ttype->is_map()) { - key = tmp("key"); - indent(out) << "NSEnumerator * " << iter << " = [" << fieldName << " keyEnumerator];" << endl; - indent(out) << "id " << key << ";" << endl; - indent(out) << "while ((" << key << " = [" << iter << " nextObject]))" << endl; - } else if (ttype->is_set()) { - key = tmp("obj"); - indent(out) << "NSEnumerator * " << iter << " = [" << fieldName << " objectEnumerator];" - << endl; - indent(out) << "id " << key << ";" << endl; - indent(out) << "while ((" << key << " = [" << iter << " nextObject]))" << endl; - } else if (ttype->is_list()) { - key = tmp("idx"); - indent(out) << "int " << key << ";" << endl; - indent(out) << "for (" << key << " = 0; " << key << " < [" << fieldName << " count]; " << key - << "++)" << endl; - } - - scope_up(out); - - if (ttype->is_map()) { - generate_serialize_map_element(out, (t_map*)ttype, key, fieldName); - } else if (ttype->is_set()) { - generate_serialize_set_element(out, (t_set*)ttype, key); - } else if (ttype->is_list()) { - generate_serialize_list_element(out, (t_list*)ttype, key, fieldName); - } - - scope_down(out); - - if (ttype->is_map()) { - indent(out) << "if (![outProtocol writeMapEnd: __thriftError]) return NO;" << endl; - } else if (ttype->is_set()) { - indent(out) << "if (![outProtocol writeSetEnd: __thriftError]) return NO;" << endl; - } else if (ttype->is_list()) { - indent(out) << "if (![outProtocol writeListEnd: __thriftError]) return NO;" << endl; - } - - scope_down(out); -} - -/** - * Serializes the members of a map. - */ -void t_cocoa_generator::generate_serialize_map_element(ostream& out, - t_map* tmap, - string key, - string mapName) { - t_field kfield(tmap->get_key_type(), key); - generate_serialize_field(out, &kfield, unbox(kfield.get_type(), key)); - t_field vfield(tmap->get_val_type(), "[" + mapName + " objectForKey: " + key + "]"); - generate_serialize_field(out, &vfield, unbox(vfield.get_type(), vfield.get_name())); -} - -/** - * Serializes the members of a set. - */ -void t_cocoa_generator::generate_serialize_set_element(ostream& out, - t_set* tset, - string elementName) { - t_field efield(tset->get_elem_type(), elementName); - generate_serialize_field(out, &efield, unbox(efield.get_type(), elementName)); -} - -/** - * Serializes the members of a list. - */ -void t_cocoa_generator::generate_serialize_list_element(ostream& out, - t_list* tlist, - string index, - string listName) { - t_field efield(tlist->get_elem_type(), "[" + listName + " objectAtIndex: " + index + "]"); - generate_serialize_field(out, &efield, unbox(efield.get_type(), efield.get_name())); -} - -/** - * Returns an Objective-C name - * - * @param ttype The type - * @param class_ref Do we want a Class reference istead of a type reference? - * @return Objective-C type name, i.e. NSDictionary * - */ -string t_cocoa_generator::type_name(t_type* ttype, bool class_ref, bool needs_mutable) { - if (ttype->is_typedef()) { - string name = (needs_mutable && ttype->get_true_type()->is_container()) ? "Mutable" + ttype->get_name() : ttype->get_name(); - t_program* program = ttype->get_program(); - return program ? (program->get_namespace("cocoa") + name) : name; - } - - string result; - if (ttype->is_base_type()) { - return base_type_name((t_base_type*)ttype); - } else if (ttype->is_enum()) { - return cocoa_prefix_ + ttype->get_name(); - } else if (ttype->is_map()) { - t_map *map = (t_map *)ttype; - result = needs_mutable ? "NSMutableDictionary" : "NSDictionary"; - result += "<" + element_type_name(map->get_key_type()) + ", " + element_type_name(map->get_val_type()) + ">"; - } else if (ttype->is_set()) { - t_set *set = (t_set *)ttype; - result = needs_mutable ? "NSMutableSet" : "NSSet"; - result += "<" + element_type_name(set->get_elem_type()) + ">"; - } else if (ttype->is_list()) { - t_list *list = (t_list *)ttype; - result = needs_mutable ? "NSMutableArray" : "NSArray"; - result += "<" + element_type_name(list->get_elem_type()) + ">"; - } else { - // Check for prefix - t_program* program = ttype->get_program(); - if (program != NULL) { - result = program->get_namespace("cocoa") + ttype->get_name(); - } else { - result = ttype->get_name(); - } - } - - if (!class_ref) { - result += " *"; - } - return result; -} - -/** - * Returns an Objective-C type name for container types - * - * @param ttype the type - */ -string t_cocoa_generator::element_type_name(t_type* etype) { - - t_type* ttype = etype->get_true_type(); - - if (etype->is_typedef() && type_can_be_null(ttype)) { - return type_name(etype); - } - - string result; - if (ttype->is_base_type()) { - t_base_type* tbase = (t_base_type*)ttype; - switch (tbase->get_base()) { - case t_base_type::TYPE_STRING: - if (tbase->is_binary()) { - result = "NSData *"; - } - else { - result = "NSString *"; - } - break; - default: - result = "NSNumber *"; - break; - } - } else if (ttype->is_enum()) { - result = "NSNumber *"; - } else if (ttype->is_map()) { - t_map *map = (t_map *)ttype; - result = "NSDictionary<" + element_type_name(map->get_key_type()) + ", " + element_type_name(map->get_val_type()) + "> *"; - } else if (ttype->is_set()) { - t_set *set = (t_set *)ttype; - result = "NSSet<" + element_type_name(set->get_elem_type()) + "> *"; - } else if (ttype->is_list()) { - t_list *list = (t_list *)ttype; - result = "NSArray<" + element_type_name(list->get_elem_type()) + "> *"; - } else if (ttype->is_struct() || ttype->is_xception()) { - result = cocoa_prefix_ + ttype->get_name() + " *"; - } - - return result; -} - -/** - * Returns the Objective-C type that corresponds to the thrift type. - * - * @param tbase The base type - */ -string t_cocoa_generator::base_type_name(t_base_type* type) { - t_base_type::t_base tbase = type->get_base(); - - switch (tbase) { - case t_base_type::TYPE_VOID: - return "void"; - case t_base_type::TYPE_STRING: - if (type->is_binary()) { - return "NSData *"; - } else { - return "NSString *"; - } - case t_base_type::TYPE_BOOL: - return "BOOL"; - case t_base_type::TYPE_I8: - return "SInt8"; - case t_base_type::TYPE_I16: - return "SInt16"; - case t_base_type::TYPE_I32: - return "SInt32"; - case t_base_type::TYPE_I64: - return "SInt64"; - case t_base_type::TYPE_DOUBLE: - return "double"; - default: - throw "compiler error: no Objective-C name for base type " + t_base_type::t_base_name(tbase); - } -} - -/** - * Prints the value of a constant with the given type. Note that type checking - * is NOT performed in this function as it is always run beforehand using the - * validate_types method in main.cc - */ -void t_cocoa_generator::print_const_value(ostream& out, - string name, - t_type* type, - t_const_value* value, - bool defval) { - type = get_true_type(type); - - if (type->is_base_type()) { - string v2 = render_const_value(out, type, value); - indent(out); - if (defval) - out << type_name(type) << " "; - out << name << " = " << v2 << ";" << endl << endl; - } else if (type->is_enum()) { - indent(out); - if (defval) - out << type_name(type) << " "; - out << name << " = " << render_const_value(out, type, value) << ";" << endl << endl; - } else if (type->is_struct() || type->is_xception()) { - indent(out); - const vector& fields = ((t_struct*)type)->get_members(); - vector::const_iterator f_iter; - const map& val = value->get_map(); - map::const_iterator v_iter; - if (defval) - out << type_name(type) << " "; - out << name << " = [" << type_name(type, true) << " new];" - << endl; - for (v_iter = val.begin(); v_iter != val.end(); ++v_iter) { - t_type* field_type = NULL; - for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { - if ((*f_iter)->get_name() == v_iter->first->get_string()) { - field_type = (*f_iter)->get_type(); - } - } - if (field_type == NULL) { - throw "type error: " + type->get_name() + " has no field " + v_iter->first->get_string(); - } - string val = render_const_value(out, field_type, v_iter->second); - std::string cap_name = capitalize(v_iter->first->get_string()); - indent(out) << "[" << name << " set" << cap_name << ":" << val << "];" << endl; - } - } else if (type->is_map()) { - ostringstream mapout; - indent(mapout); - t_type* ktype = ((t_map*)type)->get_key_type(); - t_type* vtype = ((t_map*)type)->get_val_type(); - const map& val = value->get_map(); - map::const_iterator v_iter; - if (defval) - mapout << type_name(type) << " "; - mapout << name << " = @{"; - for (v_iter = val.begin(); v_iter != val.end();) { - mapout << render_const_value(out, ktype, v_iter->first, true) << ": " - << render_const_value(out, vtype, v_iter->second, true); - if (++v_iter != val.end()) { - mapout << ", "; - } - } - mapout << "}"; - out << mapout.str(); - } else if (type->is_list()) { - ostringstream listout; - indent(listout); - t_type* etype = ((t_list*)type)->get_elem_type(); - const vector& val = value->get_list(); - vector::const_iterator v_iter; - if (defval) - listout << type_name(type) << " "; - listout << name << " = @["; - for (v_iter = val.begin(); v_iter != val.end();) { - listout << render_const_value(out, etype, *v_iter, true); - if (++v_iter != val.end()) { - listout << ", "; - } - } - listout << "]"; - out << listout.str(); - } else if (type->is_set()) { - ostringstream setout; - indent(setout); - t_type* etype = ((t_set*)type)->get_elem_type(); - const vector& val = value->get_list(); - vector::const_iterator v_iter; - if (defval) - setout << type_name(type) << " "; - setout << name << " = [NSSet setWithArray:@["; - for (v_iter = val.begin(); v_iter != val.end();) { - setout << render_const_value(out, etype, *v_iter, true); - if (++v_iter != val.end()) { - setout << ", "; - } - } - setout << "]]"; - out << setout.str(); - } else { - throw "compiler error: no const of type " + type->get_name(); - } -} - -string t_cocoa_generator::render_const_value(ostream& out, - t_type* type, - t_const_value* value, - bool box_it) { - type = get_true_type(type); - std::ostringstream render; - - if (type->is_base_type()) { - t_base_type::t_base tbase = ((t_base_type*)type)->get_base(); - switch (tbase) { - case t_base_type::TYPE_STRING: - // We must handle binary constant but the syntax of IDL defines - // nothing about binary constant. - // if type->is_binary()) - // // binary code - render << "@\"" << get_escaped_string(value) << '"'; - break; - case t_base_type::TYPE_BOOL: - render << ((value->get_integer() > 0) ? "YES" : "NO"); - break; - case t_base_type::TYPE_I8: - case t_base_type::TYPE_I16: - case t_base_type::TYPE_I32: - case t_base_type::TYPE_I64: - render << value->get_integer(); - break; - case t_base_type::TYPE_DOUBLE: - if (value->get_type() == t_const_value::CV_INTEGER) { - render << value->get_integer(); - } else { - render << value->get_double(); - } - break; - default: - throw "compiler error: no const of base type " + t_base_type::t_base_name(tbase); - } - } else if (type->is_enum()) { - render << value->get_integer(); - } else { - string t = tmp("tmp"); - print_const_value(out, t, type, value, true); - out << ";" << endl; - render << t; - } - - if (box_it) { - return box(type, render.str()); - } - return render.str(); -} - -#if 0 -/** -ORIGINAL - * Spit out code that evaluates to the specified constant value. - */ -string t_cocoa_generator::render_const_value(string name, - t_type* type, - t_const_value* value, - bool box_it) { - type = get_true_type(type); - std::ostringstream render; - - if (type->is_base_type()) { - t_base_type::t_base tbase = ((t_base_type*)type)->get_base(); - switch (tbase) { - case t_base_type::TYPE_STRING: - render << "@\"" << get_escaped_string(value) << '"'; - break; - case t_base_type::TYPE_BOOL: - render << ((value->get_integer() > 0) ? "YES" : "NO"); - break; - case t_base_type::TYPE_I8: - case t_base_type::TYPE_I16: - case t_base_type::TYPE_I32: - case t_base_type::TYPE_I64: - render << value->get_integer(); - break; - case t_base_type::TYPE_DOUBLE: - if (value->get_type() == t_const_value::CV_INTEGER) { - render << value->get_integer(); - } else { - render << value->get_double(); - } - break; - default: - throw "compiler error: no const of base type " + t_base_type::t_base_name(tbase); - } - } else if (type->is_enum()) { - render << value->get_integer(); - } else if (type->is_struct() || type->is_xception()) { - const vector& fields = ((t_struct*)type)->get_members(); - vector::const_iterator f_iter; - const map& val = value->get_map(); - map::const_iterator v_iter; - if (val.size() > 0) - render << "[[" << type_name(type, true) << " alloc] initWith"; - else - render << "[[" << type_name(type, true) << " alloc] init"; - bool first = true; - for (v_iter = val.begin(); v_iter != val.end(); ++v_iter) { - // FIXME The generated code does not match with initWithXXX - // initializer and causes compile error. - // Try: test/DebugProtoTest.thrift and test/SmallTest.thrift - t_type* field_type = NULL; - for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { - if ((*f_iter)->get_name() == v_iter->first->get_string()) { - field_type = (*f_iter)->get_type(); - } - } - if (field_type == NULL) { - throw "type error: " + type->get_name() + " has no field " + v_iter->first->get_string(); - } - if (first) { - render << capitalize(v_iter->first->get_string()); - first = false; - } else { - render << " " << v_iter->first->get_string(); - } - render << ": " << render_const_value(name, field_type, v_iter->second); - } - render << "]"; - } else if (type->is_map()) { - render << "[[NSDictionary alloc] initWithObjectsAndKeys: "; - t_type* ktype = ((t_map*)type)->get_key_type(); - t_type* vtype = ((t_map*)type)->get_val_type(); - const map& val = value->get_map(); - map::const_iterator v_iter; - bool first = true; - for (v_iter = val.begin(); v_iter != val.end(); ++v_iter) { - string key = render_const_value(name, ktype, v_iter->first, true); - string val = render_const_value(name, vtype, v_iter->second, true); - if (first) { - first = false; - } else { - render << ", "; - } - render << val << ", " << key; - } - if (first) - render << " nil]"; - else - render << ", nil]"; - } else if (type->is_list()) { - render << "[[NSArray alloc] initWithObjects: "; - t_type * etype = ((t_list*)type)->get_elem_type(); - const vector& val = value->get_list(); - bool first = true; - vector::const_iterator v_iter; - for (v_iter = val.begin(); v_iter != val.end(); ++v_iter) { - if (first) { - first = false; - } else { - render << ", "; - } - render << render_const_value(name, etype, *v_iter, true); - } - if (first) - render << " nil]"; - else - render << ", nil]"; - } else if (type->is_set()) { - render << "[[NSSet alloc] initWithObjects: "; - t_type * etype = ((t_set*)type)->get_elem_type(); - const vector& val = value->get_list(); - bool first = true; - vector::const_iterator v_iter; - for (v_iter = val.begin(); v_iter != val.end(); ++v_iter) { - if (first) { - first = false; - } else { - render << ", "; - } - render << render_const_value(name, etype, *v_iter, true); - } - if (first) - render << " nil]"; - else - render << ", nil]"; - } else { - throw "don't know how to render constant for type: " + type->get_name(); - } - - if (box_it) { - return box(type, render.str()); - } - - return render.str(); -} -#endif - -/** - * Declares an Objective-C 2.0 property. - * - * @param tfield The field to declare a property for - */ -string t_cocoa_generator::declare_property(t_field* tfield) { - std::ostringstream render; - render << "@property ("; - - if (type_can_be_copy(tfield->get_type())) { - render << "copy, "; - } else if (type_can_be_null(tfield->get_type())) { - render << "strong, "; - } else { - render << "assign, "; - } - - render << "nonatomic) " << type_name(tfield->get_type(), false, true) << " " - << tfield->get_name() << ";"; - - // Check if the property name is an Objective-C return +1 count signal - if ((tfield->get_name().length() >= 3 && tfield->get_name().substr(0,3) == "new") || - (tfield->get_name().length() >= 6 && tfield->get_name().substr(0,6) == "create") || - (tfield->get_name().length() >= 5 && tfield->get_name().substr(0,5) == "alloc")) { - // Let Objective-C know not to return +1 for object pointers - if (type_can_be_null(tfield->get_type())) { - render << endl; - render << "- (" + type_name(tfield->get_type()) + ") " + decapitalize(tfield->get_name()) + " __attribute__((objc_method_family(none)));"; - } - } - - return render.str(); -} - -/** - * Declares an Objective-C 2.0 property. - * - * @param tfield The field to declare a property for - */ -string t_cocoa_generator::declare_property_isset(t_field* tfield) { - return "@property (assign, nonatomic) BOOL " + decapitalize(tfield->get_name()) + "IsSet;"; -} - -/** - * Declares property unset method. - * - * @param tfield The field to declare a property for - */ -string t_cocoa_generator::declare_property_unset(t_field* tfield) { - return "- (void) unset" + capitalize(tfield->get_name()) + ";"; -} - -/** - * Renders the early out return statement - * - * @param tfunction Function definition - * @return String of rendered invalid return statment - */ -string t_cocoa_generator::invalid_return_statement(t_function *tfunction) { - if ((tfunction->get_returntype()->is_void())) { - return "return NO;"; - } - return "return nil;"; -} - -/** - * Renders a function signature - * - * @param tfunction Function definition - * @return String of rendered function definition - */ -string t_cocoa_generator::function_signature(t_function* tfunction, bool include_error) { - t_type* ttype = tfunction->get_returntype(); - string result; - if (ttype->is_void()) { - result = "(BOOL)"; - } - else if (type_can_be_null(ttype)) { - result = "(" + type_name(ttype) + ")"; - } - else { - result = "(NSNumber *)"; - } - result += " " + tfunction->get_name() + argument_list(tfunction->get_arglist(), "", include_error); - return result; -} - -/** - * Renders a function signature that returns asynchronously instead of - * literally returning. - * - * @param tfunction Function definition - * @return String of rendered function definition - */ -string t_cocoa_generator::async_function_signature(t_function* tfunction, bool include_error) { - t_type* ttype = tfunction->get_returntype(); - t_struct* targlist = tfunction->get_arglist(); - string response_param = "void (^)(" + ((ttype->is_void()) ? "" : type_name(ttype)) + ")"; - std::string result = "(void) " + tfunction->get_name() + argument_list(tfunction->get_arglist(), "", include_error) - + (targlist->get_members().size() ? " response" : "") + ": (" - + response_param + ") responseBlock " - + "failure : (TAsyncFailureBlock) failureBlock"; - return result; -} - -/** - * Renders a function signature that returns a promise instead of - * literally returning. - * - * @param tfunction Function definition - * @return String of rendered function definition - */ -string t_cocoa_generator::promise_function_signature(t_function* tfunction) { - return "(AnyPromise *) " + tfunction->get_name() + argument_list(tfunction->get_arglist(), "", false); -} - -/** - * Renders a colon separated list of types and names, suitable for an - * objective-c parameter list - */ -string t_cocoa_generator::argument_list(t_struct* tstruct, string protocol_name, bool include_error) { - string result = ""; - bool include_protocol = !protocol_name.empty(); - - const vector& fields = tstruct->get_members(); - vector::const_iterator f_iter; - bool first = true; - for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { - string argPrefix = ""; - if (first) { - first = false; - } else { - argPrefix = (*f_iter)->get_name(); - result += " "; - } - result += argPrefix + ": (" + type_name((*f_iter)->get_type()) + ") " + (*f_iter)->get_name(); - } - if (include_protocol) { - if (!first) { - result += " protocol"; - } - result += ": (id) " + protocol_name; - first = false; - } - if (include_error) { - if (!first) { - result += " error"; - } - result += ": (NSError *__autoreleasing *)__thriftError"; - first = false; - } - return result; -} - -/** - * Converts the parse type to an Objective-C enum string for the given type. - */ -string t_cocoa_generator::type_to_enum(t_type* type) { - type = get_true_type(type); - - if (type->is_base_type()) { - t_base_type::t_base tbase = ((t_base_type*)type)->get_base(); - switch (tbase) { - case t_base_type::TYPE_VOID: - throw "NO T_VOID CONSTRUCT"; - case t_base_type::TYPE_STRING: - return "TTypeSTRING"; - case t_base_type::TYPE_BOOL: - return "TTypeBOOL"; - case t_base_type::TYPE_I8: - return "TTypeBYTE"; - case t_base_type::TYPE_I16: - return "TTypeI16"; - case t_base_type::TYPE_I32: - return "TTypeI32"; - case t_base_type::TYPE_I64: - return "TTypeI64"; - case t_base_type::TYPE_DOUBLE: - return "TTypeDOUBLE"; - } - } else if (type->is_enum()) { - return "TTypeI32"; - } else if (type->is_struct() || type->is_xception()) { - return "TTypeSTRUCT"; - } else if (type->is_map()) { - return "TTypeMAP"; - } else if (type->is_set()) { - return "TTypeSET"; - } else if (type->is_list()) { - return "TTypeLIST"; - } - - throw "INVALID TYPE IN type_to_enum: " + type->get_name(); -} - -/** - * Returns a format string specifier for the supplied parse type. - */ -string t_cocoa_generator::format_string_for_type(t_type* type) { - type = get_true_type(type); - - if (type->is_base_type()) { - t_base_type::t_base tbase = ((t_base_type*)type)->get_base(); - switch (tbase) { - case t_base_type::TYPE_VOID: - throw "NO T_VOID CONSTRUCT"; - case t_base_type::TYPE_STRING: - return "\\\"%@\\\""; - case t_base_type::TYPE_BOOL: - return "%i"; - case t_base_type::TYPE_I8: - return "%i"; - case t_base_type::TYPE_I16: - return "%hi"; - case t_base_type::TYPE_I32: - return "%i"; - case t_base_type::TYPE_I64: - return "%qi"; - case t_base_type::TYPE_DOUBLE: - return "%f"; - } - } else if (type->is_enum()) { - return "%i"; - } else if (type->is_struct() || type->is_xception()) { - return "%@"; - } else if (type->is_map()) { - return "%@"; - } else if (type->is_set()) { - return "%@"; - } else if (type->is_list()) { - return "%@"; - } - - throw "INVALID TYPE IN format_string_for_type: " + type->get_name(); -} - -/** - * Returns a format cast for the supplied parse type. - */ -string t_cocoa_generator::format_cast_for_type(t_type* type) { - type = get_true_type(type); - - if (type->is_base_type()) { - t_base_type::t_base tbase = ((t_base_type*)type)->get_base(); - switch (tbase) { - case t_base_type::TYPE_VOID: - throw "NO T_VOID CONSTRUCT"; - case t_base_type::TYPE_STRING: - return ""; // "\\\"%@\\\""; - case t_base_type::TYPE_BOOL: - return ""; // "%i"; - case t_base_type::TYPE_I8: - return ""; // "%i"; - case t_base_type::TYPE_I16: - return ""; // "%hi"; - case t_base_type::TYPE_I32: - return "(int)"; // "%i"; - case t_base_type::TYPE_I64: - return ""; // "%qi"; - case t_base_type::TYPE_DOUBLE: - return ""; // "%f"; - } - } else if (type->is_enum()) { - return "(int)"; // "%i"; - } else if (type->is_struct() || type->is_xception()) { - return ""; // "%@"; - } else if (type->is_map()) { - return ""; // "%@"; - } else if (type->is_set()) { - return ""; // "%@"; - } else if (type->is_list()) { - return ""; // "%@"; - } - - throw "INVALID TYPE IN format_cast_for_type: " + type->get_name(); -} - -/** - * Generate a call to a field's setter. - * - * @param tfield Field the setter is being called on - * @param fieldName Name of variable to pass to setter - */ - -string t_cocoa_generator::call_field_setter(t_field* tfield, string fieldName) { - return "self." + tfield->get_name() + " = " + fieldName + ";"; -} - -THRIFT_REGISTER_GENERATOR( - cocoa, - "Cocoa", - " log_unexpected: Log every time an unexpected field ID or type is encountered.\n" - " debug_descriptions:\n" - " Allow use of debugDescription so the app can add description via a cateogory/extension\n" - " validate_required:\n" - " Throws exception if any required field is not set.\n" - " async_clients: Generate clients which invoke asynchronously via block syntax.\n" - " pods: Generate imports in Cocopods framework format.\n" - " promise_kit: Generate clients which invoke asynchronously via promises.\n") diff --git a/compiler/cpp/src/thrift/generate/t_cpp_generator.cc b/compiler/cpp/src/thrift/generate/t_cpp_generator.cc index 3e8f728d244..ceaa11f21ba 100644 --- a/compiler/cpp/src/thrift/generate/t_cpp_generator.cc +++ b/compiler/cpp/src/thrift/generate/t_cpp_generator.cc @@ -68,6 +68,7 @@ class t_cpp_generator : public t_oop_generator { gen_moveable_ = false; gen_no_ostream_operators_ = false; gen_no_skeleton_ = false; + has_members_ = false; for( iter = parsed_options.begin(); iter != parsed_options.end(); ++iter) { if( iter->first.compare("pure_enums") == 0) { @@ -101,25 +102,27 @@ class t_cpp_generator : public t_oop_generator { * Init and close methods */ - void init_generator(); - void close_generator(); + void init_generator() override; + void close_generator() override; - void generate_consts(std::vector consts); + void generate_consts(std::vector consts) override; /** * Program-level generation functions */ - void generate_typedef(t_typedef* ttypedef); - void generate_enum(t_enum* tenum); + void generate_typedef(t_typedef* ttypedef) override; + void generate_enum(t_enum* tenum) override; void generate_enum_ostream_operator_decl(std::ostream& out, t_enum* tenum); void generate_enum_ostream_operator(std::ostream& out, t_enum* tenum); - void generate_forward_declaration(t_struct* tstruct); - void generate_struct(t_struct* tstruct) { generate_cpp_struct(tstruct, false); } - void generate_xception(t_struct* txception) { generate_cpp_struct(txception, true); } + void generate_enum_to_string_helper_function_decl(std::ostream& out, t_enum* tenum); + void generate_enum_to_string_helper_function(std::ostream& out, t_enum* tenum); + void generate_forward_declaration(t_struct* tstruct) override; + void generate_struct(t_struct* tstruct) override { generate_cpp_struct(tstruct, false); } + void generate_xception(t_struct* txception) override { generate_cpp_struct(txception, true); } void generate_cpp_struct(t_struct* tstruct, bool is_exception); - void generate_service(t_service* tservice); + void generate_service(t_service* tservice) override; void print_const_value(std::ostream& out, std::string name, t_type* type, t_const_value* value); std::string render_const_value(std::ostream& out, @@ -296,6 +299,13 @@ class t_cpp_generator : public t_oop_generator { */ std::string get_include_prefix(const t_program& program) const; + /** + * Returns the legal program name to use for a file generated by program, if the + * program name contains dots then replace it with underscores, otherwise return the + * original program name. + */ + std::string get_legal_program_name(std::string program_name); + /** * True if we should generate pure enums for Thrift enums, instead of wrapper classes. */ @@ -348,6 +358,11 @@ class t_cpp_generator : public t_oop_generator { */ bool gen_no_skeleton_; + /** + * True if thrift has member(s) + */ + bool has_members_; + /** * Strings for namespace, computed once up front then used directly */ @@ -384,6 +399,8 @@ void t_cpp_generator::init_generator() { // Make output directory MKDIR(get_out_dir().c_str()); + program_name_ = get_legal_program_name(program_name_); + // Make output file string f_types_name = get_out_dir() + program_name_ + "_types.h"; f_types_.open(f_types_name); @@ -419,28 +436,29 @@ void t_cpp_generator::init_generator() { << "#include " << endl << endl; // Include C++xx compatibility header - f_types_ << "#include " << endl; + f_types_ << "#include " << endl; + f_types_ << "#include " << endl; // Include other Thrift includes const vector& includes = program_->get_includes(); - for (size_t i = 0; i < includes.size(); ++i) { - f_types_ << "#include \"" << get_include_prefix(*(includes[i])) << includes[i]->get_name() + for (auto include : includes) { + f_types_ << "#include \"" << get_include_prefix(*include) << include->get_name() << "_types.h\"" << endl; // XXX(simpkins): If gen_templates_ is enabled, we currently assume all // included files were also generated with templates enabled. - f_types_tcc_ << "#include \"" << get_include_prefix(*(includes[i])) << includes[i]->get_name() + f_types_tcc_ << "#include \"" << get_include_prefix(*include) << include->get_name() << "_types.tcc\"" << endl; } f_types_ << endl; // Include custom headers const vector& cpp_includes = program_->get_cpp_includes(); - for (size_t i = 0; i < cpp_includes.size(); ++i) { - if (cpp_includes[i][0] == '<') { - f_types_ << "#include " << cpp_includes[i] << endl; + for (const auto & cpp_include : cpp_includes) { + if (cpp_include[0] == '<') { + f_types_ << "#include " << cpp_include << endl; } else { - f_types_ << "#include \"" << cpp_includes[i] << "\"" << endl; + f_types_ << "#include \"" << cpp_include << "\"" << endl; } } f_types_ << endl; @@ -493,6 +511,12 @@ void t_cpp_generator::close_generator() { f_types_.close(); f_types_impl_.close(); f_types_tcc_.close(); + + string f_types_impl_name = get_out_dir() + program_name_ + "_types.cpp"; + + if (!has_members_) { + remove(f_types_impl_name.c_str()); + } } /** @@ -501,6 +525,7 @@ void t_cpp_generator::close_generator() { * @param ttypedef The type definition */ void t_cpp_generator::generate_typedef(t_typedef* ttypedef) { + generate_java_doc(f_types_, ttypedef); f_types_ << indent() << "typedef " << type_name(ttypedef->get_type(), true) << " " << ttypedef->get_symbolic() << ";" << endl << endl; } @@ -521,6 +546,7 @@ void t_cpp_generator::generate_enum_constant_list(std::ostream& f, } else { f << "," << endl; } + generate_java_doc(f, *c_iter); indent(f) << prefix << (*c_iter)->get_name() << suffix; if (include_values) { f << " = " << (*c_iter)->get_value(); @@ -544,6 +570,7 @@ void t_cpp_generator::generate_enum(t_enum* tenum) { std::string enum_name = tenum->get_name(); if (!gen_pure_enums_) { enum_name = "type"; + generate_java_doc(f_types_, tenum); f_types_ << indent() << "struct " << tenum->get_name() << " {" << endl; indent_up(); } @@ -579,10 +606,13 @@ void t_cpp_generator::generate_enum(t_enum* tenum) { << "_VALUES_TO_NAMES(::apache::thrift::TEnumIterator(" << constants.size() << ", _k" << tenum->get_name() << "Values" << ", _k" << tenum->get_name() << "Names), " - << "::apache::thrift::TEnumIterator(-1, NULL, NULL));" << endl << endl; + << "::apache::thrift::TEnumIterator(-1, nullptr, nullptr));" << endl << endl; generate_enum_ostream_operator_decl(f_types_, tenum); generate_enum_ostream_operator(f_types_impl_, tenum); + + generate_enum_to_string_helper_function_decl(f_types_, tenum); + generate_enum_to_string_helper_function(f_types_impl_, tenum); } void t_cpp_generator::generate_enum_ostream_operator_decl(std::ostream& out, t_enum* tenum) { @@ -630,61 +660,102 @@ void t_cpp_generator::generate_enum_ostream_operator(std::ostream& out, t_enum* } } +void t_cpp_generator::generate_enum_to_string_helper_function_decl(std::ostream& out, t_enum* tenum) { + out << "std::string to_string(const "; + if (gen_pure_enums_) { + out << tenum->get_name(); + } else { + out << tenum->get_name() << "::type&"; + } + out << " val);" << endl; + out << endl; +} + +void t_cpp_generator::generate_enum_to_string_helper_function(std::ostream& out, t_enum* tenum) { + if (!has_custom_ostream(tenum)) { + out << "std::string to_string(const "; + if (gen_pure_enums_) { + out << tenum->get_name(); + } else { + out << tenum->get_name() << "::type&"; + } + out << " val) " ; + scope_up(out); + + out << indent() << "std::map::const_iterator it = _" + << tenum->get_name() << "_VALUES_TO_NAMES.find(val);" << endl; + out << indent() << "if (it != _" << tenum->get_name() << "_VALUES_TO_NAMES.end()) {" << endl; + indent_up(); + out << indent() << "return std::string(it->second);" << endl; + indent_down(); + out << indent() << "} else {" << endl; + indent_up(); + out << indent() << "return std::to_string(static_cast(val));" << endl; + indent_down(); + out << indent() << "}" << endl; + + scope_down(out); + out << endl; + } +} + /** * Generates a class that holds all the constants. */ void t_cpp_generator::generate_consts(std::vector consts) { string f_consts_name = get_out_dir() + program_name_ + "_constants.h"; ofstream_with_content_based_conditional_update f_consts; - f_consts.open(f_consts_name); + if (consts.size() > 0) { + f_consts.open(f_consts_name); - string f_consts_impl_name = get_out_dir() + program_name_ + "_constants.cpp"; - ofstream_with_content_based_conditional_update f_consts_impl; - f_consts_impl.open(f_consts_impl_name); + string f_consts_impl_name = get_out_dir() + program_name_ + "_constants.cpp"; + ofstream_with_content_based_conditional_update f_consts_impl; + f_consts_impl.open(f_consts_impl_name); - // Print header - f_consts << autogen_comment(); - f_consts_impl << autogen_comment(); + // Print header + f_consts << autogen_comment(); + f_consts_impl << autogen_comment(); - // Start ifndef - f_consts << "#ifndef " << program_name_ << "_CONSTANTS_H" << endl << "#define " << program_name_ - << "_CONSTANTS_H" << endl << endl << "#include \"" << get_include_prefix(*get_program()) - << program_name_ << "_types.h\"" << endl << endl << ns_open_ << endl << endl; + // Start ifndef + f_consts << "#ifndef " << program_name_ << "_CONSTANTS_H" << endl << "#define " << program_name_ + << "_CONSTANTS_H" << endl << endl << "#include \"" << get_include_prefix(*get_program()) + << program_name_ << "_types.h\"" << endl << endl << ns_open_ << endl << endl; - f_consts_impl << "#include \"" << get_include_prefix(*get_program()) << program_name_ - << "_constants.h\"" << endl << endl << ns_open_ << endl << endl; + f_consts_impl << "#include \"" << get_include_prefix(*get_program()) << program_name_ + << "_constants.h\"" << endl << endl << ns_open_ << endl << endl; - f_consts << "class " << program_name_ << "Constants {" << endl << " public:" << endl << " " - << program_name_ << "Constants();" << endl << endl; - indent_up(); - vector::iterator c_iter; - for (c_iter = consts.begin(); c_iter != consts.end(); ++c_iter) { - string name = (*c_iter)->get_name(); - t_type* type = (*c_iter)->get_type(); - f_consts << indent() << type_name(type) << " " << name << ";" << endl; - } - indent_down(); - f_consts << "};" << endl; + f_consts << "class " << program_name_ << "Constants {" << endl << " public:" << endl << " " + << program_name_ << "Constants();" << endl << endl; + indent_up(); + vector::iterator c_iter; + for (c_iter = consts.begin(); c_iter != consts.end(); ++c_iter) { + string name = (*c_iter)->get_name(); + t_type* type = (*c_iter)->get_type(); + f_consts << indent() << type_name(type) << " " << name << ";" << endl; + } + indent_down(); + f_consts << "};" << endl; - f_consts_impl << "const " << program_name_ << "Constants g_" << program_name_ << "_constants;" - << endl << endl << program_name_ << "Constants::" << program_name_ - << "Constants() {" << endl; - indent_up(); - for (c_iter = consts.begin(); c_iter != consts.end(); ++c_iter) { - print_const_value(f_consts_impl, - (*c_iter)->get_name(), - (*c_iter)->get_type(), - (*c_iter)->get_value()); - } - indent_down(); - indent(f_consts_impl) << "}" << endl; + f_consts_impl << "const " << program_name_ << "Constants g_" << program_name_ << "_constants;" + << endl << endl << program_name_ << "Constants::" << program_name_ + << "Constants() {" << endl; + indent_up(); + for (c_iter = consts.begin(); c_iter != consts.end(); ++c_iter) { + print_const_value(f_consts_impl, + (*c_iter)->get_name(), + (*c_iter)->get_type(), + (*c_iter)->get_value()); + } + indent_down(); + indent(f_consts_impl) << "}" << endl; - f_consts << endl << "extern const " << program_name_ << "Constants g_" << program_name_ - << "_constants;" << endl << endl << ns_close_ << endl << endl << "#endif" << endl; - f_consts.close(); + f_consts << endl << "extern const " << program_name_ << "Constants g_" << program_name_ + << "_constants;" << endl << endl << ns_close_ << endl << endl << "#endif" << endl; + f_consts.close(); - f_consts_impl << endl << ns_close_ << endl << endl; - f_consts_impl.close(); + f_consts_impl << endl << ns_close_ << endl << endl; + f_consts_impl.close(); + } } /** @@ -710,7 +781,7 @@ void t_cpp_generator::print_const_value(ostream& out, map::const_iterator v_iter; bool is_nonrequired_field = false; for (v_iter = val.begin(); v_iter != val.end(); ++v_iter) { - t_type* field_type = NULL; + t_type* field_type = nullptr; is_nonrequired_field = false; for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { if ((*f_iter)->get_name() == v_iter->first->get_string()) { @@ -718,7 +789,7 @@ void t_cpp_generator::print_const_value(ostream& out, is_nonrequired_field = (*f_iter)->get_req() != t_field::T_REQUIRED; } } - if (field_type == NULL) { + if (field_type == nullptr) { throw "type error: " + type->get_name() + " has no field " + v_iter->first->get_string(); } string val = render_const_value(out, name, field_type, v_iter->second); @@ -847,6 +918,8 @@ void t_cpp_generator::generate_cpp_struct(t_struct* tstruct, bool is_exception) if (is_exception) { generate_exception_what_method(f_types_impl_, tstruct); } + + has_members_ = true; } void t_cpp_generator::generate_copy_constructor(ostream& out, @@ -1008,7 +1081,7 @@ void t_cpp_generator::generate_struct_declaration(ostream& out, if ((*m_iter)->get_req() == t_field::T_REQUIRED) { continue; } - string isSet = ((*m_iter)->get_value() != NULL) ? "true" : "false"; + string isSet = ((*m_iter)->get_value() != nullptr) ? "true" : "false"; if (first) { first = false; out << ": " << (*m_iter)->get_name() << "(" << isSet << ")"; @@ -1030,6 +1103,8 @@ void t_cpp_generator::generate_struct_declaration(ostream& out, out << endl; + generate_java_doc(out, tstruct); + // Open struct def out << indent() << "class " << tstruct->get_name() << extends << " {" << endl << indent() << " public:" << endl << endl; @@ -1067,7 +1142,7 @@ void t_cpp_generator::generate_struct_declaration(ostream& out, } dval += (t->is_string() || is_reference(*m_iter)) ? "" : "0"; t_const_value* cv = (*m_iter)->get_value(); - if (cv != NULL) { + if (cv != nullptr) { dval = render_const_value(out, (*m_iter)->get_name(), t, cv); } if (!init_ctor) { @@ -1088,7 +1163,7 @@ void t_cpp_generator::generate_struct_declaration(ostream& out, if (!t->is_base_type()) { t_const_value* cv = (*m_iter)->get_value(); - if (cv != NULL) { + if (cv != nullptr) { print_const_value(out, (*m_iter)->get_name(), t, cv); } } @@ -1097,11 +1172,12 @@ void t_cpp_generator::generate_struct_declaration(ostream& out, } if (tstruct->annotations_.find("final") == tstruct->annotations_.end()) { - out << endl << indent() << "virtual ~" << tstruct->get_name() << "() throw();" << endl; + out << endl << indent() << "virtual ~" << tstruct->get_name() << "() noexcept;" << endl; } // Declare all fields for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) { + generate_java_doc(out, *m_iter); indent(out) << declare_field(*m_iter, false, (pointers && !(*m_iter)->get_type()->is_xception()), @@ -1119,7 +1195,7 @@ void t_cpp_generator::generate_struct_declaration(ostream& out, continue; } if (is_reference((*m_iter))) { - out << endl << indent() << "void __set_" << (*m_iter)->get_name() << "(::apache::thrift::stdcxx::shared_ptr<" + out << endl << indent() << "void __set_" << (*m_iter)->get_name() << "(::std::shared_ptr<" << type_name((*m_iter)->get_type(), false, false) << ">"; out << " val);" << endl; } else { @@ -1188,7 +1264,7 @@ void t_cpp_generator::generate_struct_declaration(ostream& out, if (is_user_struct && !has_custom_ostream(tstruct)) { out << indent() << "virtual "; - generate_struct_print_method_decl(out, NULL); + generate_struct_print_method_decl(out, nullptr); out << ";" << endl; } @@ -1205,8 +1281,13 @@ void t_cpp_generator::generate_struct_declaration(ostream& out, if (swap) { // Generate a namespace-scope swap() function - out << indent() << "void swap(" << tstruct->get_name() << " &a, " << tstruct->get_name() - << " &b);" << endl << endl; + if (tstruct->get_name() == "a" || tstruct->get_name() == "b") { + out << indent() << "void swap(" << tstruct->get_name() << " &a1, " << tstruct->get_name() + << " &a2);" << endl << endl; + } else { + out << indent() << "void swap(" << tstruct->get_name() << " &a, " << tstruct->get_name() + << " &b);" << endl << endl; + } } if (is_user_struct) { @@ -1226,7 +1307,7 @@ void t_cpp_generator::generate_struct_definition(ostream& out, // Destructor if (tstruct->annotations_.find("final") == tstruct->annotations_.end()) { force_cpp_out << endl << indent() << tstruct->get_name() << "::~" << tstruct->get_name() - << "() throw() {" << endl; + << "() noexcept {" << endl; indent_up(); indent_down(); @@ -1237,9 +1318,8 @@ void t_cpp_generator::generate_struct_definition(ostream& out, if (setters) { for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) { if (is_reference((*m_iter))) { - std::string type = type_name((*m_iter)->get_type()); out << endl << indent() << "void " << tstruct->get_name() << "::__set_" - << (*m_iter)->get_name() << "(::apache::thrift::stdcxx::shared_ptr<" + << (*m_iter)->get_name() << "(::std::shared_ptr<" << type_name((*m_iter)->get_type(), false, false) << ">"; out << " val) {" << endl; } else { @@ -1526,8 +1606,14 @@ void t_cpp_generator::generate_struct_result_writer(ostream& out, * @param tstruct The struct */ void t_cpp_generator::generate_struct_swap(ostream& out, t_struct* tstruct) { - out << indent() << "void swap(" << tstruct->get_name() << " &a, " << tstruct->get_name() - << " &b) {" << endl; + if (tstruct->get_name() == "a" || tstruct->get_name() == "b") { + out << indent() << "void swap(" << tstruct->get_name() << " &a1, " << tstruct->get_name() + << " &a2) {" << endl; + } else { + out << indent() << "void swap(" << tstruct->get_name() << " &a, " << tstruct->get_name() + << " &b) {" << endl; + } + indent_up(); // Let argument-dependent name lookup find the correct swap() function to @@ -1537,25 +1623,37 @@ void t_cpp_generator::generate_struct_swap(ostream& out, t_struct* tstruct) { bool has_nonrequired_fields = false; const vector& fields = tstruct->get_members(); - for (vector::const_iterator f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { - t_field* tfield = *f_iter; - + for (auto tfield : fields) { if (tfield->get_req() != t_field::T_REQUIRED) { has_nonrequired_fields = true; } - out << indent() << "swap(a." << tfield->get_name() << ", b." << tfield->get_name() << ");" - << endl; + if (tstruct->get_name() == "a" || tstruct->get_name() == "b") { + out << indent() << "swap(a1." << tfield->get_name() << ", a2." << tfield->get_name() << ");" + << endl; + } else { + out << indent() << "swap(a." << tfield->get_name() << ", b." << tfield->get_name() << ");" + << endl; + } } if (has_nonrequired_fields) { - out << indent() << "swap(a.__isset, b.__isset);" << endl; + if (tstruct->get_name() == "a" || tstruct->get_name() == "b") { + out << indent() << "swap(a1.__isset, a2.__isset);" << endl; + } else { + out << indent() << "swap(a.__isset, b.__isset);" << endl; + } } // handle empty structs if (fields.size() == 0) { - out << indent() << "(void) a;" << endl; - out << indent() << "(void) b;" << endl; + if (tstruct->get_name() == "a" || tstruct->get_name() == "b") { + out << indent() << "(void) a1;" << endl; + out << indent() << "(void) a2;" << endl; + } else { + out << indent() << "(void) a;" << endl; + out << indent() << "(void) b;" << endl; + } } scope_down(out); @@ -1598,7 +1696,7 @@ void t_cpp_generator::generate_exception_what_method_decl(std::ostream& out, if (external) { out << tstruct->get_name() << "::"; } - out << "what() const throw()"; + out << "what() const noexcept"; } namespace struct_ostream_operator_generator { @@ -1717,8 +1815,8 @@ void t_cpp_generator::generate_service(t_service* tservice) { f_header_ << "#ifndef " << svcname << "_H" << endl << "#define " << svcname << "_H" << endl << endl; if (gen_cob_style_) { - f_header_ << "#include " << endl << // TMemoryBuffer - "#include " << endl + f_header_ << "#include " << endl // TMemoryBuffer + << "#include " << endl << "namespace apache { namespace thrift { namespace async {" << endl << "class TAsyncChannel;" << endl << "}}}" << endl; } @@ -1727,11 +1825,12 @@ void t_cpp_generator::generate_service(t_service* tservice) { f_header_ << "#include " << endl; } f_header_ << "#include " << endl; + f_header_ << "#include " << endl; f_header_ << "#include \"" << get_include_prefix(*get_program()) << program_name_ << "_types.h\"" << endl; t_service* extends_service = tservice->get_extends(); - if (extends_service != NULL) { + if (extends_service != nullptr) { f_header_ << "#include \"" << get_include_prefix(*(extends_service->get_program())) << extends_service->get_name() << ".h\"" << endl; } @@ -1878,7 +1977,7 @@ void t_cpp_generator::generate_service_interface(t_service* tservice, string sty } string extends = ""; - if (tservice->get_extends() != NULL) { + if (tservice->get_extends() != nullptr) { extends = " : virtual public " + type_name(tservice->get_extends()) + style + "If"; if (style == "CobCl" && gen_templates_) { // TODO(simpkins): If gen_templates_ is enabled, we currently assume all @@ -1890,6 +1989,9 @@ void t_cpp_generator::generate_service_interface(t_service* tservice, string sty if (style == "CobCl" && gen_templates_) { f_header_ << "template " << endl; } + + generate_java_doc(f_header_, tservice); + f_header_ << "class " << service_if_name << extends << " {" << endl << " public:" << endl; indent_up(); f_header_ << indent() << "virtual ~" << service_if_name << "() {}" << endl; @@ -1940,7 +2042,7 @@ void t_cpp_generator::generate_service_interface_factory(t_service* tservice, st // type. Implementations can use dynamic_cast to cast the pointer to the // subclass type if desired. t_service* base_service = tservice; - while (base_service->get_extends() != NULL) { + while (base_service->get_extends() != nullptr) { base_service = base_service->get_extends(); } string base_if_name = type_name(base_service) + style + "If"; @@ -1948,7 +2050,7 @@ void t_cpp_generator::generate_service_interface_factory(t_service* tservice, st // Generate the abstract factory class string factory_name = service_if_name + "Factory"; string extends; - if (tservice->get_extends() != NULL) { + if (tservice->get_extends() != nullptr) { extends = " : virtual public " + type_name(tservice->get_extends()) + style + "IfFactory"; } @@ -1968,7 +2070,7 @@ void t_cpp_generator::generate_service_interface_factory(t_service* tservice, st f_header_ << "class " << singleton_factory_name << " : virtual public " << factory_name << " {" << endl << " public:" << endl; indent_up(); - f_header_ << indent() << singleton_factory_name << "(const ::apache::thrift::stdcxx::shared_ptr<" << service_if_name + f_header_ << indent() << singleton_factory_name << "(const ::std::shared_ptr<" << service_if_name << ">& iface) : iface_(iface) {}" << endl << indent() << "virtual ~" << singleton_factory_name << "() {}" << endl << endl << indent() << "virtual " << service_if_name << "* getHandler(" @@ -1976,7 +2078,7 @@ void t_cpp_generator::generate_service_interface_factory(t_service* tservice, st << " return iface_.get();" << endl << indent() << "}" << endl << indent() << "virtual void releaseHandler(" << base_if_name << "* /* handler */) {}" << endl; - f_header_ << endl << " protected:" << endl << indent() << "::apache::thrift::stdcxx::shared_ptr<" << service_if_name + f_header_ << endl << " protected:" << endl << indent() << "::std::shared_ptr<" << service_if_name << "> iface_;" << endl; indent_down(); @@ -1990,7 +2092,7 @@ void t_cpp_generator::generate_service_interface_factory(t_service* tservice, st */ void t_cpp_generator::generate_service_null(t_service* tservice, string style) { string extends = ""; - if (tservice->get_extends() != NULL) { + if (tservice->get_extends() != nullptr) { extends = " , virtual public " + type_name(tservice->get_extends()) + style + "Null"; } f_header_ << "class " << service_name_ << style << "Null : virtual public " << service_name_ @@ -2080,7 +2182,11 @@ void t_cpp_generator::generate_service_async_skeleton(t_service* tservice) { << "// filename to avoid overwriting it and rewrite as asynchronous any functions" << endl << "// that would otherwise introduce unwanted latency." << endl << endl << "#include \"" << get_include_prefix(*get_program()) << svcname << ".h\"" << endl - << "#include " << endl << endl + << "#include " << endl + << "#include " << endl + << "#include " << endl + << "#include " << endl + << "#include " << endl << endl << "using namespace ::apache::thrift;" << endl << "using namespace ::apache::thrift::protocol;" << endl << "using namespace ::apache::thrift::transport;" << endl @@ -2093,17 +2199,33 @@ void t_cpp_generator::generate_service_async_skeleton(t_service* tservice) { f_skeleton << "using namespace " << string(ns, 0, ns.size() - 2) << ";" << endl << endl; } + f_skeleton << "class " << svcname << "Handler : virtual public " << svcname << "If {" << endl + << " public:" << endl; + indent_up(); + f_skeleton << indent() << svcname << "Handler() {" << endl << indent() + << " // Your initialization goes here" << endl << indent() << "}" << endl << endl; + + vector functions = tservice->get_functions(); + vector::iterator f_iter; + for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) { + generate_java_doc(f_skeleton, *f_iter); + f_skeleton << indent() << function_signature(*f_iter, "") << " {" << endl << indent() + << " // Your implementation goes here" << endl << indent() << " printf(\"" + << (*f_iter)->get_name() << "\\n\");" << endl << indent() << "}" << endl << endl; + } + + indent_down(); + f_skeleton << "};" << endl << endl; + f_skeleton << "class " << svcname << "AsyncHandler : " << "public " << svcname << "CobSvIf {" << endl << " public:" << endl; indent_up(); f_skeleton << indent() << svcname << "AsyncHandler() {" << endl << indent() - << " syncHandler_ = std::auto_ptr<" << svcname << "Handler>(new " << svcname + << " syncHandler_ = std::unique_ptr<" << svcname << "Handler>(new " << svcname << "Handler);" << endl << indent() << " // Your initialization goes here" << endl << indent() << "}" << endl; f_skeleton << indent() << "virtual ~" << service_name_ << "AsyncHandler();" << endl; - vector functions = tservice->get_functions(); - vector::iterator f_iter; for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) { f_skeleton << endl << indent() << function_signature(*f_iter, "CobSv", "", true) << " {" << endl; @@ -2121,10 +2243,25 @@ void t_cpp_generator::generate_service_async_skeleton(t_service* tservice) { scope_down(f_skeleton); } - f_skeleton << endl << " protected:" << endl << indent() << "std::auto_ptr<" << svcname + f_skeleton << endl << " protected:" << endl << indent() << "std::unique_ptr<" << svcname << "Handler> syncHandler_;" << endl; indent_down(); f_skeleton << "};" << endl << endl; + + f_skeleton << indent() << "int main(int argc, char **argv) {" << endl; + indent_up(); + f_skeleton + << indent() << "int port = 9090;" << endl << indent() << "::std::shared_ptr<" << svcname + << "AsyncHandler> handler(new " << svcname << "AsyncHandler());" << endl << indent() + << "::std::shared_ptr<" << svcname << "AsyncProcessor> processor(new " << svcname << "AsyncProcessor(handler));" << endl + << indent() << "::std::shared_ptr protocolFactory(new TBinaryProtocolFactory());" + << endl + << indent() << "::std::shared_ptr protocolProcessor(new TAsyncProtocolProcessor(processor, protocolFactory));" + << endl << endl << indent() + << "TEvhttpServer server(protocolProcessor, port);" + << endl << indent() << "server.serve();" << endl << indent() << "return 0;" << endl; + indent_down(); + f_skeleton << "}" << endl << endl; } /** @@ -2141,12 +2278,12 @@ void t_cpp_generator::generate_service_multiface(t_service* tservice) { string extends = ""; string extends_multiface = ""; - if (tservice->get_extends() != NULL) { + if (tservice->get_extends() != nullptr) { extends = type_name(tservice->get_extends()); extends_multiface = ", public " + extends + "Multiface"; } - string list_type = string("std::vector >"; + string list_type = string("std::vector >"; // Generate the header portion f_header_ << "class " << service_name_ << "Multiface : " @@ -2157,7 +2294,7 @@ void t_cpp_generator::generate_service_multiface(t_service* tservice) { << "& ifaces) : ifaces_(ifaces) {" << endl; if (!extends.empty()) { f_header_ << indent() - << " std::vector >::iterator iter;" + << " std::vector >::iterator iter;" << endl << indent() << " for (iter = ifaces.begin(); iter != ifaces.end(); ++iter) {" << endl << indent() << " " << extends << "Multiface::add(*iter);" << endl << indent() << " }" << endl; @@ -2170,7 +2307,7 @@ void t_cpp_generator::generate_service_multiface(t_service* tservice) { f_header_ << " protected:" << endl; indent_up(); f_header_ << indent() << list_type << " ifaces_;" << endl << indent() << service_name_ - << "Multiface() {}" << endl << indent() << "void add(::apache::thrift::stdcxx::shared_ptr<" + << "Multiface() {}" << endl << indent() << "void add(::std::shared_ptr<" << service_name_ << "If> iface) {" << endl; if (!extends.empty()) { f_header_ << indent() << " " << extends << "Multiface::add(iface);" << endl; @@ -2182,6 +2319,7 @@ void t_cpp_generator::generate_service_multiface(t_service* tservice) { indent_up(); for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) { + generate_java_doc(f_header_, *f_iter); t_struct* arglist = (*f_iter)->get_arglist(); const vector& args = arglist->get_members(); vector::const_iterator a_iter; @@ -2252,7 +2390,7 @@ void t_cpp_generator::generate_service_client(t_service* tservice, string style) } else { protocol_type = "::apache::thrift::protocol::TProtocol"; } - string prot_ptr = "apache::thrift::stdcxx::shared_ptr< " + protocol_type + ">"; + string prot_ptr = "std::shared_ptr< " + protocol_type + ">"; string client_suffix = "Client" + template_suffix; string if_suffix = "If"; if (style == "Cob") { @@ -2261,7 +2399,7 @@ void t_cpp_generator::generate_service_client(t_service* tservice, string style) string extends = ""; string extends_client = ""; - if (tservice->get_extends() != NULL) { + if (tservice->get_extends() != nullptr) { // TODO(simpkins): If gen_templates_ is enabled, we currently assume all // parent services were also generated with templates enabled. extends = type_name(tservice->get_extends()); @@ -2282,27 +2420,49 @@ void t_cpp_generator::generate_service_client(t_service* tservice, string style) indent_up(); if (style != "Cob") { f_header_ << indent() << service_name_ << style << "Client" << short_suffix << "(" << prot_ptr - << " prot) "; + << " prot"; + if (style == "Concurrent") { + f_header_ << ", std::shared_ptr<::apache::thrift::async::TConcurrentClientSyncInfo> sync"; + } + f_header_ << ") "; if (extends.empty()) { + if (style == "Concurrent") { + f_header_ << ": sync_(sync)" << endl; + } f_header_ << "{" << endl; f_header_ << indent() << " setProtocol" << short_suffix << "(prot);" << endl << indent() << "}" << endl; } else { f_header_ << ":" << endl; - f_header_ << indent() << " " << extends << style << client_suffix << "(prot, prot) {}" - << endl; + f_header_ << indent() << " " << extends << style << client_suffix << "(prot, prot"; + if (style == "Concurrent") { + f_header_ << ", sync"; + } + f_header_ << ") {}" << endl; } f_header_ << indent() << service_name_ << style << "Client" << short_suffix << "(" << prot_ptr - << " iprot, " << prot_ptr << " oprot) "; + << " iprot, " << prot_ptr << " oprot"; + if (style == "Concurrent") { + f_header_ << ", std::shared_ptr<::apache::thrift::async::TConcurrentClientSyncInfo> sync"; + } + f_header_ << ") "; + if (extends.empty()) { + if (style == "Concurrent") { + f_header_ << ": sync_(sync)" << endl; + } f_header_ << "{" << endl; f_header_ << indent() << " setProtocol" << short_suffix << "(iprot,oprot);" << endl << indent() << "}" << endl; } else { f_header_ << ":" << indent() << " " << extends << style << client_suffix - << "(iprot, oprot) {}" << endl; + << "(iprot, oprot"; + if (style == "Concurrent") { + f_header_ << ", sync"; + } + f_header_ << ") {}" << endl; } // create the setProtocol methods @@ -2329,18 +2489,18 @@ void t_cpp_generator::generate_service_client(t_service* tservice, string style) // Note that these are not currently templated for simplicity. // TODO(simpkins): should they be templated? f_header_ << indent() - << "apache::thrift::stdcxx::shared_ptr< ::apache::thrift::protocol::TProtocol> getInputProtocol() {" + << "std::shared_ptr< ::apache::thrift::protocol::TProtocol> getInputProtocol() {" << endl << indent() << " return " << _this << "piprot_;" << endl << indent() << "}" << endl; f_header_ << indent() - << "apache::thrift::stdcxx::shared_ptr< ::apache::thrift::protocol::TProtocol> getOutputProtocol() {" + << "std::shared_ptr< ::apache::thrift::protocol::TProtocol> getOutputProtocol() {" << endl << indent() << " return " << _this << "poprot_;" << endl << indent() << "}" << endl; } else /* if (style == "Cob") */ { f_header_ << indent() << service_name_ << style << "Client" << short_suffix << "(" - << "apache::thrift::stdcxx::shared_ptr< ::apache::thrift::async::TAsyncChannel> channel, " + << "std::shared_ptr< ::apache::thrift::async::TAsyncChannel> channel, " << "::apache::thrift::protocol::TProtocolFactory* protocolFactory) :" << endl; if (extends.empty()) { f_header_ << indent() << " channel_(channel)," << endl << indent() @@ -2350,9 +2510,9 @@ void t_cpp_generator::generate_service_client(t_service* tservice, string style) if (gen_templates_) { // TProtocolFactory classes return generic TProtocol pointers. // We have to dynamic cast to the Protocol_ type we are expecting. - f_header_ << indent() << " piprot_(::apache::thrift::stdcxx::dynamic_pointer_cast(" + f_header_ << indent() << " piprot_(::std::dynamic_pointer_cast(" << "protocolFactory->getProtocol(itrans_)))," << endl << indent() - << " poprot_(::apache::thrift::stdcxx::dynamic_pointer_cast(" + << " poprot_(::std::dynamic_pointer_cast(" << "protocolFactory->getProtocol(otrans_))) {" << endl; // Throw a TException if either dynamic cast failed. f_header_ << indent() << " if (!piprot_ || !poprot_) {" << endl << indent() @@ -2373,8 +2533,10 @@ void t_cpp_generator::generate_service_client(t_service* tservice, string style) } if (style == "Cob") { + generate_java_doc(f_header_, tservice); + f_header_ << indent() - << "::apache::thrift::stdcxx::shared_ptr< ::apache::thrift::async::TAsyncChannel> getChannel() {" << endl + << "::std::shared_ptr< ::apache::thrift::async::TAsyncChannel> getChannel() {" << endl << indent() << " return " << _this << "channel_;" << endl << indent() << "}" << endl; if (!gen_no_client_completion_) { f_header_ << indent() << "virtual void completed__(bool /* success */) {}" << endl; @@ -2384,6 +2546,7 @@ void t_cpp_generator::generate_service_client(t_service* tservice, string style) vector functions = tservice->get_functions(); vector::const_iterator f_iter; for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) { + generate_java_doc(f_header_, *f_iter); indent(f_header_) << function_signature(*f_iter, ifstyle) << ";" << endl; // TODO(dreiss): Use private inheritance to avoid generating thise in cob-style. if (style == "Concurrent" && !(*f_iter)->is_oneway()) { @@ -2426,11 +2589,11 @@ void t_cpp_generator::generate_service_client(t_service* tservice, string style) if (style == "Cob") { f_header_ << indent() - << "::apache::thrift::stdcxx::shared_ptr< ::apache::thrift::async::TAsyncChannel> channel_;" << endl + << "::std::shared_ptr< ::apache::thrift::async::TAsyncChannel> channel_;" << endl << indent() - << "::apache::thrift::stdcxx::shared_ptr< ::apache::thrift::transport::TMemoryBuffer> itrans_;" << endl + << "::std::shared_ptr< ::apache::thrift::transport::TMemoryBuffer> itrans_;" << endl << indent() - << "::apache::thrift::stdcxx::shared_ptr< ::apache::thrift::transport::TMemoryBuffer> otrans_;" + << "::std::shared_ptr< ::apache::thrift::transport::TMemoryBuffer> otrans_;" << endl; } f_header_ << @@ -2441,7 +2604,7 @@ void t_cpp_generator::generate_service_client(t_service* tservice, string style) if (style == "Concurrent") { f_header_ << - indent() << "::apache::thrift::async::TConcurrentClientSyncInfo sync_;"< sync_;"<is_oneway()) { out << indent() << _this << "channel_->sendAndRecvMessage(" - << "::apache::thrift::stdcxx::bind(cob, this), " << _this << "otrans_.get(), " << _this << "itrans_.get());" + << "::std::bind(cob, this), " << _this << "otrans_.get(), " << _this << "itrans_.get());" << endl; } else { out << indent() << _this << "channel_->sendMessage(" - << "::apache::thrift::stdcxx::bind(cob, this), " << _this << "otrans_.get());" << endl; + << "::std::bind(cob, this), " << _this << "otrans_.get());" << endl; } } scope_down(out); @@ -2547,7 +2710,7 @@ void t_cpp_generator::generate_service_client(t_service* tservice, string style) string cseqidVal = "0"; if (style == "Concurrent") { if (!(*f_iter)->is_oneway()) { - cseqidVal = "this->sync_.generateSeqId()"; + cseqidVal = "this->sync_->generateSeqId()"; } } // Serialize the request @@ -2555,7 +2718,7 @@ void t_cpp_generator::generate_service_client(t_service* tservice, string style) indent() << "int32_t cseqid = " << cseqidVal << ";" << endl; if(style == "Concurrent") { out << - indent() << "::apache::thrift::async::TConcurrentSendSentry sentry(&this->sync_);" << endl; + indent() << "::apache::thrift::async::TConcurrentSendSentry sentry(this->sync_.get());" << endl; } if (style == "Cob") { out << @@ -2620,7 +2783,7 @@ void t_cpp_generator::generate_service_client(t_service* tservice, string style) endl << indent() << "// the read mutex gets dropped and reacquired as part of waitForWork()" << endl << indent() << "// The destructor of this sentry wakes up other clients" << endl << - indent() << "::apache::thrift::async::TConcurrentRecvSentry sentry(&this->sync_, seqid);" << endl; + indent() << "::apache::thrift::async::TConcurrentRecvSentry sentry(this->sync_.get(), seqid);" << endl; } if (style == "Cob" && !gen_no_client_completion_) { out << indent() << "bool completed = false;" << endl << endl << indent() << "try {"; @@ -2630,7 +2793,7 @@ void t_cpp_generator::generate_service_client(t_service* tservice, string style) if (style == "Concurrent") { out << indent() << "while(true) {" << endl << - indent() << " if(!this->sync_.getPending(fname, mtype, rseqid)) {" << endl; + indent() << " if(!this->sync_->getPending(fname, mtype, rseqid)) {" << endl; indent_up(); indent_up(); } @@ -2774,10 +2937,10 @@ void t_cpp_generator::generate_service_client(t_service* tservice, string style) out << indent() << " }" << endl << indent() << " // seqid != rseqid" << endl << - indent() << " this->sync_.updatePending(fname, mtype, rseqid);" << endl << + indent() << " this->sync_->updatePending(fname, mtype, rseqid);" << endl << endl << indent() << " // this will temporarily unlock the readMutex, and let other clients get work done" << endl << - indent() << " this->sync_.waitForWork(seqid);" << endl << + indent() << " this->sync_->waitForWork(seqid);" << endl << indent() << "} // end while(true)" << endl; } if (style == "Cob" && !gen_no_client_completion_) { @@ -2867,8 +3030,8 @@ ProcessorGenerator::ProcessorGenerator(t_cpp_generator* generator, class_name_ = service_name_ + pstyle_ + "Processor"; if_name_ = service_name_ + "CobSvIf"; - finish_cob_ = "::apache::thrift::stdcxx::function cob, "; - finish_cob_decl_ = "::apache::thrift::stdcxx::function, "; + finish_cob_ = "::std::function cob, "; + finish_cob_decl_ = "::std::function, "; cob_arg_ = "cob, "; ret_type_ = "void "; } else { @@ -2892,7 +3055,7 @@ ProcessorGenerator::ProcessorGenerator(t_cpp_generator* generator, factory_class_name_ += "T"; } - if (service_->get_extends() != NULL) { + if (service_->get_extends() != nullptr) { extends_ = type_name(service_->get_extends()) + pstyle_ + "Processor"; if (generator_->gen_templates_) { // TODO(simpkins): If gen_templates_ is enabled, we currently assume all @@ -2908,7 +3071,7 @@ void ProcessorGenerator::generate_class_definition() { vector::iterator f_iter; string parent_class; - if (service_->get_extends() != NULL) { + if (service_->get_extends() != nullptr) { parent_class = extends_; } else { if (style_ == "Cob") { @@ -2929,7 +3092,7 @@ void ProcessorGenerator::generate_class_definition() { // Protected data members f_header_ << " protected:" << endl; indent_up(); - f_header_ << indent() << "::apache::thrift::stdcxx::shared_ptr<" << if_name_ << "> iface_;" << endl; + f_header_ << indent() << "::std::shared_ptr<" << if_name_ << "> iface_;" << endl; f_header_ << indent() << "virtual " << ret_type_ << "dispatchCall(" << finish_cob_ << "::apache::thrift::protocol::TProtocol* iprot, " << "::apache::thrift::protocol::TProtocol* oprot, " @@ -2959,7 +3122,7 @@ void ProcessorGenerator::generate_class_definition() { << " ProcessFunctions(ProcessFunction g, " << "SpecializedProcessFunction s) :" << endl << indent() << " generic(g)," << endl << indent() << " specialized(s) {}" << endl << indent() - << " ProcessFunctions() : generic(NULL), specialized(NULL) " + << " ProcessFunctions() : generic(nullptr), specialized(nullptr) " << "{}" << endl << indent() << "};" << endl << indent() << "typedef std::map " << "ProcessMap;" << endl; @@ -2985,29 +3148,29 @@ void ProcessorGenerator::generate_class_definition() { ? "" : ", const " + type_name((*f_iter)->get_returntype()) + "& _return"); f_header_ << indent() << "void return_" << (*f_iter)->get_name() - << "(::apache::thrift::stdcxx::function cob, int32_t seqid, " + << "(::std::function cob, int32_t seqid, " << "::apache::thrift::protocol::TProtocol* oprot, " << "void* ctx" << ret_arg << ");" << endl; if (generator_->gen_templates_) { f_header_ << indent() << "void return_" << (*f_iter)->get_name() - << "(::apache::thrift::stdcxx::function cob, int32_t seqid, " + << "(::std::function cob, int32_t seqid, " << "Protocol_* oprot, void* ctx" << ret_arg << ");" << endl; } // XXX Don't declare throw if it doesn't exist f_header_ << indent() << "void throw_" << (*f_iter)->get_name() - << "(::apache::thrift::stdcxx::function cob, int32_t seqid, " + << "(::std::function cob, int32_t seqid, " << "::apache::thrift::protocol::TProtocol* oprot, void* ctx, " << "::apache::thrift::TDelayedException* _throw);" << endl; if (generator_->gen_templates_) { f_header_ << indent() << "void throw_" << (*f_iter)->get_name() - << "(::apache::thrift::stdcxx::function cob, int32_t seqid, " + << "(::std::function cob, int32_t seqid, " << "Protocol_* oprot, void* ctx, " << "::apache::thrift::TDelayedException* _throw);" << endl; } } } - f_header_ << " public:" << endl << indent() << class_name_ << "(::apache::thrift::stdcxx::shared_ptr<" << if_name_ + f_header_ << " public:" << endl << indent() << class_name_ << "(::std::shared_ptr<" << if_name_ << "> iface) :" << endl; if (!extends_.empty()) { f_header_ << indent() << " " << extends_ << "(iface)," << endl; @@ -3020,7 +3183,7 @@ void ProcessorGenerator::generate_class_definition() { if (generator_->gen_templates_) { f_header_ << "ProcessFunctions(" << endl; if (generator_->gen_templates_only_) { - indent(f_header_) << " NULL," << endl; + indent(f_header_) << " nullptr," << endl; } else { indent(f_header_) << " &" << class_name_ << "::process_" << (*f_iter)->get_name() << "," << endl; @@ -3145,14 +3308,14 @@ void ProcessorGenerator::generate_factory() { << endl << " public:" << endl; indent_up(); - f_header_ << indent() << factory_class_name_ << "(const ::apache::thrift::stdcxx::shared_ptr< " << if_factory_name + f_header_ << indent() << factory_class_name_ << "(const ::std::shared_ptr< " << if_factory_name << " >& handlerFactory) :" << endl << indent() << " handlerFactory_(handlerFactory) {}" << endl << endl << indent() - << "::apache::thrift::stdcxx::shared_ptr< ::apache::thrift::" + << "::std::shared_ptr< ::apache::thrift::" << (style_ == "Cob" ? "async::TAsyncProcessor" : "TProcessor") << " > " << "getProcessor(const ::apache::thrift::TConnectionInfo& connInfo);" << endl; - f_header_ << endl << " protected:" << endl << indent() << "::apache::thrift::stdcxx::shared_ptr< " + f_header_ << endl << " protected:" << endl << indent() << "::std::shared_ptr< " << if_factory_name << " > handlerFactory_;" << endl; indent_down(); @@ -3167,17 +3330,17 @@ void ProcessorGenerator::generate_factory() { } // Generate the getProcessor() method - f_out_ << template_header_ << indent() << "::apache::thrift::stdcxx::shared_ptr< ::apache::thrift::" + f_out_ << template_header_ << indent() << "::std::shared_ptr< ::apache::thrift::" << (style_ == "Cob" ? "async::TAsyncProcessor" : "TProcessor") << " > " << factory_class_name_ << template_suffix_ << "::getProcessor(" << "const ::apache::thrift::TConnectionInfo& connInfo) {" << endl; indent_up(); f_out_ << indent() << "::apache::thrift::ReleaseHandler< " << if_factory_name - << " > cleanup(handlerFactory_);" << endl << indent() << "::apache::thrift::stdcxx::shared_ptr< " + << " > cleanup(handlerFactory_);" << endl << indent() << "::std::shared_ptr< " << if_name_ << " > handler(" << "handlerFactory_->getHandler(connInfo), cleanup);" << endl << indent() - << "::apache::thrift::stdcxx::shared_ptr< ::apache::thrift::" + << "::std::shared_ptr< ::apache::thrift::" << (style_ == "Cob" ? "async::TAsyncProcessor" : "TProcessor") << " > " << "processor(new " << class_name_ << template_suffix_ << "(handler));" << endl << indent() << "return processor;" << endl; @@ -3282,18 +3445,18 @@ void t_cpp_generator::generate_process_function(t_service* tservice, out << indent() << "(void) seqid;" << endl << indent() << "(void) oprot;" << endl; } - out << indent() << "void* ctx = NULL;" << endl << indent() - << "if (this->eventHandler_.get() != NULL) {" << endl << indent() + out << indent() << "void* ctx = nullptr;" << endl << indent() + << "if (this->eventHandler_.get() != nullptr) {" << endl << indent() << " ctx = this->eventHandler_->getContext(" << service_func_name << ", callContext);" << endl << indent() << "}" << endl << indent() << "::apache::thrift::TProcessorContextFreer freer(" << "this->eventHandler_.get(), ctx, " << service_func_name << ");" << endl << endl - << indent() << "if (this->eventHandler_.get() != NULL) {" << endl << indent() + << indent() << "if (this->eventHandler_.get() != nullptr) {" << endl << indent() << " this->eventHandler_->preRead(ctx, " << service_func_name << ");" << endl << indent() << "}" << endl << endl << indent() << argsname << " args;" << endl << indent() << "args.read(iprot);" << endl << indent() << "iprot->readMessageEnd();" << endl << indent() << "uint32_t bytes = iprot->getTransport()->readEnd();" << endl << endl << indent() - << "if (this->eventHandler_.get() != NULL) {" << endl << indent() + << "if (this->eventHandler_.get() != nullptr) {" << endl << indent() << " this->eventHandler_->postRead(ctx, " << service_func_name << ", bytes);" << endl << indent() << "}" << endl << endl; @@ -3361,7 +3524,7 @@ void t_cpp_generator::generate_process_function(t_service* tservice, } indent_up(); - out << indent() << "if (this->eventHandler_.get() != NULL) {" << endl << indent() + out << indent() << "if (this->eventHandler_.get() != nullptr) {" << endl << indent() << " this->eventHandler_->handlerError(ctx, " << service_func_name << ");" << endl << indent() << "}" << endl; @@ -3379,7 +3542,7 @@ void t_cpp_generator::generate_process_function(t_service* tservice, // Shortcut out here for oneway functions if (tfunction->is_oneway()) { - out << indent() << "if (this->eventHandler_.get() != NULL) {" << endl << indent() + out << indent() << "if (this->eventHandler_.get() != nullptr) {" << endl << indent() << " this->eventHandler_->asyncComplete(ctx, " << service_func_name << ");" << endl << indent() << "}" << endl << endl << indent() << "return;" << endl; indent_down(); @@ -3388,14 +3551,14 @@ void t_cpp_generator::generate_process_function(t_service* tservice, } // Serialize the result into a struct - out << indent() << "if (this->eventHandler_.get() != NULL) {" << endl << indent() + out << indent() << "if (this->eventHandler_.get() != nullptr) {" << endl << indent() << " this->eventHandler_->preWrite(ctx, " << service_func_name << ");" << endl << indent() << "}" << endl << endl << indent() << "oprot->writeMessageBegin(\"" << tfunction->get_name() << "\", ::apache::thrift::protocol::T_REPLY, seqid);" << endl << indent() << "result.write(oprot);" << endl << indent() << "oprot->writeMessageEnd();" << endl << indent() << "bytes = oprot->getTransport()->writeEnd();" << endl << indent() << "oprot->getTransport()->flush();" << endl << endl << indent() - << "if (this->eventHandler_.get() != NULL) {" << endl << indent() + << "if (this->eventHandler_.get() != nullptr) {" << endl << indent() << " this->eventHandler_->postWrite(ctx, " << service_func_name << ", bytes);" << endl << indent() << "}" << endl; @@ -3412,7 +3575,7 @@ void t_cpp_generator::generate_process_function(t_service* tservice, out << indent() << "template " << endl; } out << "void " << tservice->get_name() << "AsyncProcessor" << class_suffix << "::process_" - << tfunction->get_name() << "(::apache::thrift::stdcxx::function cob, int32_t seqid, " + << tfunction->get_name() << "(::std::function cob, int32_t seqid, " << prot_type << "* iprot, " << prot_type << "* oprot)" << endl; scope_up(out); @@ -3434,32 +3597,32 @@ void t_cpp_generator::generate_process_function(t_service* tservice, } out << indent() << tservice->get_name() + "_" + tfunction->get_name() << "_args args;" << endl - << indent() << "void* ctx = NULL;" << endl << indent() - << "if (this->eventHandler_.get() != NULL) {" << endl << indent() - << " ctx = this->eventHandler_->getContext(" << service_func_name << ", NULL);" << endl + << indent() << "void* ctx = nullptr;" << endl << indent() + << "if (this->eventHandler_.get() != nullptr) {" << endl << indent() + << " ctx = this->eventHandler_->getContext(" << service_func_name << ", nullptr);" << endl << indent() << "}" << endl << indent() << "::apache::thrift::TProcessorContextFreer freer(" << "this->eventHandler_.get(), ctx, " << service_func_name << ");" << endl << endl << indent() << "try {" << endl; indent_up(); - out << indent() << "if (this->eventHandler_.get() != NULL) {" << endl << indent() + out << indent() << "if (this->eventHandler_.get() != nullptr) {" << endl << indent() << " this->eventHandler_->preRead(ctx, " << service_func_name << ");" << endl << indent() << "}" << endl << indent() << "args.read(iprot);" << endl << indent() << "iprot->readMessageEnd();" << endl << indent() << "uint32_t bytes = iprot->getTransport()->readEnd();" << endl << indent() - << "if (this->eventHandler_.get() != NULL) {" << endl << indent() + << "if (this->eventHandler_.get() != nullptr) {" << endl << indent() << " this->eventHandler_->postRead(ctx, " << service_func_name << ", bytes);" << endl << indent() << "}" << endl; scope_down(out); // TODO(dreiss): Handle TExceptions? Expose to server? out << indent() << "catch (const std::exception&) {" << endl << indent() - << " if (this->eventHandler_.get() != NULL) {" << endl << indent() + << " if (this->eventHandler_.get() != nullptr) {" << endl << indent() << " this->eventHandler_->handlerError(ctx, " << service_func_name << ");" << endl << indent() << " }" << endl << indent() << " return cob(false);" << endl << indent() << "}" << endl; if (tfunction->is_oneway()) { - out << indent() << "if (this->eventHandler_.get() != NULL) {" << endl << indent() + out << indent() << "if (this->eventHandler_.get() != nullptr) {" << endl << indent() << " this->eventHandler_->asyncComplete(ctx, " << service_func_name << ");" << endl << indent() << "}" << endl; } @@ -3469,28 +3632,28 @@ void t_cpp_generator::generate_process_function(t_service* tservice, // No return. Just hand off our cob. // TODO(dreiss): Call the cob immediately? out << indent() << "iface_->" << tfunction->get_name() << "(" - << "::apache::thrift::stdcxx::bind(cob, true)" << endl; + << "::std::bind(cob, true)" << endl; indent_up(); indent_up(); } else { string ret_arg, ret_placeholder; if (!tfunction->get_returntype()->is_void()) { ret_arg = ", const " + type_name(tfunction->get_returntype()) + "& _return"; - ret_placeholder = ", ::apache::thrift::stdcxx::placeholders::_1"; + ret_placeholder = ", ::std::placeholders::_1"; } // When gen_templates_ is true, the return_ and throw_ functions are // overloaded. We have to declare pointers to them so that the compiler // can resolve the correct overloaded version. out << indent() << "void (" << tservice->get_name() << "AsyncProcessor" << class_suffix - << "::*return_fn)(::apache::thrift::stdcxx::function " + << "::*return_fn)(::std::function " << "cob, int32_t seqid, " << prot_type << "* oprot, void* ctx" << ret_arg << ") =" << endl; out << indent() << " &" << tservice->get_name() << "AsyncProcessor" << class_suffix << "::return_" << tfunction->get_name() << ";" << endl; if (!xceptions.empty()) { out << indent() << "void (" << tservice->get_name() << "AsyncProcessor" << class_suffix - << "::*throw_fn)(::apache::thrift::stdcxx::function " + << "::*throw_fn)(::std::function " << "cob, int32_t seqid, " << prot_type << "* oprot, void* ctx, " << "::apache::thrift::TDelayedException* _throw) =" << endl; out << indent() << " &" << tservice->get_name() << "AsyncProcessor" << class_suffix @@ -3500,11 +3663,11 @@ void t_cpp_generator::generate_process_function(t_service* tservice, out << indent() << "iface_->" << tfunction->get_name() << "(" << endl; indent_up(); indent_up(); - out << indent() << "::apache::thrift::stdcxx::bind(return_fn, this, cob, seqid, oprot, ctx" << ret_placeholder + out << indent() << "::std::bind(return_fn, this, cob, seqid, oprot, ctx" << ret_placeholder << ")"; if (!xceptions.empty()) { - out << ',' << endl << indent() << "::apache::thrift::stdcxx::bind(throw_fn, this, cob, seqid, oprot, " - << "ctx, ::apache::thrift::stdcxx::placeholders::_1)"; + out << ',' << endl << indent() << "::std::bind(throw_fn, this, cob, seqid, oprot, " + << "ctx, ::std::placeholders::_1)"; } } @@ -3529,7 +3692,7 @@ void t_cpp_generator::generate_process_function(t_service* tservice, out << indent() << "template " << endl; } out << "void " << tservice->get_name() << "AsyncProcessor" << class_suffix << "::return_" - << tfunction->get_name() << "(::apache::thrift::stdcxx::function cob, int32_t seqid, " + << tfunction->get_name() << "(::std::function cob, int32_t seqid, " << prot_type << "* oprot, void* ctx" << ret_arg_decl << ')' << endl; scope_up(out); @@ -3552,19 +3715,19 @@ void t_cpp_generator::generate_process_function(t_service* tservice, << "*>(&_return);" << endl << indent() << "result.__isset.success = true;" << endl; } // Serialize the result into a struct - out << endl << indent() << "if (this->eventHandler_.get() != NULL) {" << endl << indent() - << " ctx = this->eventHandler_->getContext(" << service_func_name << ", NULL);" << endl + out << endl << indent() << "if (this->eventHandler_.get() != nullptr) {" << endl << indent() + << " ctx = this->eventHandler_->getContext(" << service_func_name << ", nullptr);" << endl << indent() << "}" << endl << indent() << "::apache::thrift::TProcessorContextFreer freer(" << "this->eventHandler_.get(), ctx, " << service_func_name << ");" << endl << endl - << indent() << "if (this->eventHandler_.get() != NULL) {" << endl << indent() + << indent() << "if (this->eventHandler_.get() != nullptr) {" << endl << indent() << " this->eventHandler_->preWrite(ctx, " << service_func_name << ");" << endl << indent() << "}" << endl << endl << indent() << "oprot->writeMessageBegin(\"" << tfunction->get_name() << "\", ::apache::thrift::protocol::T_REPLY, seqid);" << endl << indent() << "result.write(oprot);" << endl << indent() << "oprot->writeMessageEnd();" << endl << indent() << "uint32_t bytes = oprot->getTransport()->writeEnd();" << endl << indent() << "oprot->getTransport()->flush();" << endl << indent() - << "if (this->eventHandler_.get() != NULL) {" << endl << indent() + << "if (this->eventHandler_.get() != nullptr) {" << endl << indent() << " this->eventHandler_->postWrite(ctx, " << service_func_name << ", bytes);" << endl << indent() << "}" << endl << indent() << "return cob(true);" << endl; scope_down(out); @@ -3577,7 +3740,7 @@ void t_cpp_generator::generate_process_function(t_service* tservice, out << indent() << "template " << endl; } out << "void " << tservice->get_name() << "AsyncProcessor" << class_suffix << "::throw_" - << tfunction->get_name() << "(::apache::thrift::stdcxx::function cob, int32_t seqid, " + << tfunction->get_name() << "(::std::function cob, int32_t seqid, " << prot_type << "* oprot, void* ctx, " << "::apache::thrift::TDelayedException* _throw)" << endl; scope_up(out); @@ -3593,8 +3756,8 @@ void t_cpp_generator::generate_process_function(t_service* tservice, } // Get the event handler context - out << endl << indent() << "if (this->eventHandler_.get() != NULL) {" << endl << indent() - << " ctx = this->eventHandler_->getContext(" << service_func_name << ", NULL);" << endl + out << endl << indent() << "if (this->eventHandler_.get() != nullptr) {" << endl << indent() + << " ctx = this->eventHandler_->getContext(" << service_func_name << ", nullptr);" << endl << indent() << "}" << endl << indent() << "::apache::thrift::TProcessorContextFreer freer(" << "this->eventHandler_.get(), ctx, " << service_func_name << ");" << endl << endl; @@ -3620,7 +3783,7 @@ void t_cpp_generator::generate_process_function(t_service* tservice, // Handle the case where an undeclared exception is thrown out << " catch (std::exception& e) {" << endl; indent_up(); - out << indent() << "if (this->eventHandler_.get() != NULL) {" << endl << indent() + out << indent() << "if (this->eventHandler_.get() != nullptr) {" << endl << indent() << " this->eventHandler_->handlerError(ctx, " << service_func_name << ");" << endl << indent() << "}" << endl << endl << indent() << "::apache::thrift::TApplicationException x(e.what());" << endl << indent() @@ -3636,14 +3799,14 @@ void t_cpp_generator::generate_process_function(t_service* tservice, scope_down(out); // Serialize the result into a struct - out << indent() << "if (this->eventHandler_.get() != NULL) {" << endl << indent() + out << indent() << "if (this->eventHandler_.get() != nullptr) {" << endl << indent() << " this->eventHandler_->preWrite(ctx, " << service_func_name << ");" << endl << indent() << "}" << endl << endl << indent() << "oprot->writeMessageBegin(\"" << tfunction->get_name() << "\", ::apache::thrift::protocol::T_REPLY, seqid);" << endl << indent() << "result.write(oprot);" << endl << indent() << "oprot->writeMessageEnd();" << endl << indent() << "uint32_t bytes = oprot->getTransport()->writeEnd();" << endl << indent() << "oprot->getTransport()->flush();" << endl << indent() - << "if (this->eventHandler_.get() != NULL) {" << endl << indent() + << "if (this->eventHandler_.get() != nullptr) {" << endl << indent() << " this->eventHandler_->postWrite(ctx, " << service_func_name << ", bytes);" << endl << indent() << "}" << endl << indent() << "return cob(true);" << endl; scope_down(out); @@ -3707,13 +3870,13 @@ void t_cpp_generator::generate_service_skeleton(t_service* tservice) { f_skeleton << indent() << "int main(int argc, char **argv) {" << endl; indent_up(); f_skeleton - << indent() << "int port = 9090;" << endl << indent() << "::apache::thrift::stdcxx::shared_ptr<" << svcname + << indent() << "int port = 9090;" << endl << indent() << "::std::shared_ptr<" << svcname << "Handler> handler(new " << svcname << "Handler());" << endl << indent() - << "::apache::thrift::stdcxx::shared_ptr processor(new " << svcname << "Processor(handler));" << endl - << indent() << "::apache::thrift::stdcxx::shared_ptr serverTransport(new TServerSocket(port));" + << "::std::shared_ptr processor(new " << svcname << "Processor(handler));" << endl + << indent() << "::std::shared_ptr serverTransport(new TServerSocket(port));" << endl << indent() - << "::apache::thrift::stdcxx::shared_ptr transportFactory(new TBufferedTransportFactory());" << endl - << indent() << "::apache::thrift::stdcxx::shared_ptr protocolFactory(new TBinaryProtocolFactory());" + << "::std::shared_ptr transportFactory(new TBufferedTransportFactory());" << endl + << indent() << "::std::shared_ptr protocolFactory(new TBinaryProtocolFactory());" << endl << endl << indent() << "TSimpleServer server(processor, serverTransport, transportFactory, protocolFactory);" << endl << indent() << "server.serve();" << endl << indent() << "return 0;" << endl; @@ -3802,7 +3965,7 @@ void t_cpp_generator::generate_deserialize_struct(ostream& out, bool pointer) { if (pointer) { indent(out) << "if (!" << prefix << ") { " << endl; - indent(out) << " " << prefix << " = ::apache::thrift::stdcxx::shared_ptr<" << type_name(tstruct) << ">(new " + indent(out) << " " << prefix << " = ::std::shared_ptr<" << type_name(tstruct) << ">(new " << type_name(tstruct) << ");" << endl; indent(out) << "}" << endl; indent(out) << "xfer += " << prefix << "->read(iprot);" << endl; @@ -4226,7 +4389,7 @@ string t_cpp_generator::type_name(t_type* ttype, bool in_typedef, bool arg) { // Check if it needs to be namespaced string pname; t_program* program = ttype->get_program(); - if (program != NULL && program != program_) { + if (program != nullptr && program != program_) { pname = class_prefix + namespace_prefix(program->get_namespace("cpp")) + ttype->get_name(); } else { pname = class_prefix + ttype->get_name(); @@ -4294,7 +4457,7 @@ string t_cpp_generator::declare_field(t_field* tfield, } result += type_name(tfield->get_type()); if (is_reference(tfield)) { - result = "::apache::thrift::stdcxx::shared_ptr<" + result + ">"; + result = "::std::shared_ptr<" + result + ">"; } if (pointer) { result += "*"; @@ -4373,13 +4536,13 @@ string t_cpp_generator::function_signature(t_function* tfunction, cob_type = (ttype->is_void() ? "()" : ("(" + type_name(ttype) + " const& _return)")); if (has_xceptions) { exn_cob - = ", ::apache::thrift::stdcxx::function /* exn_cob */"; + = ", ::std::function /* exn_cob */"; } } else { throw "UNKNOWN STYLE"; } - return "void " + prefix + tfunction->get_name() + "(::apache::thrift::stdcxx::function cob" + return "void " + prefix + tfunction->get_name() + "(::std::function cob" + exn_cob + argument_list(arglist, name_params, true) + ")"; } else { throw "UNKNOWN STYLE"; @@ -4472,6 +4635,23 @@ string t_cpp_generator::get_include_prefix(const t_program& program) const { return ""; } +string t_cpp_generator::get_legal_program_name(std::string program_name) +{ + std::size_t found = 0; + + while(true) { + found = program_name.find('.'); + + if(found != string::npos) { + program_name = program_name.replace(found, 1, "_"); + } else { + break; + } + } + + return program_name; +} + THRIFT_REGISTER_GENERATOR( cpp, "C++", diff --git a/compiler/cpp/src/thrift/generate/t_csharp_generator.cc b/compiler/cpp/src/thrift/generate/t_csharp_generator.cc deleted file mode 100644 index 37d6f9dd95e..00000000000 --- a/compiler/cpp/src/thrift/generate/t_csharp_generator.cc +++ /dev/null @@ -1,3242 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - * - * Contains some contributions under the Thrift Software License. - * Please see doc/old-thrift-license.txt in the Thrift distribution for - * details. - */ - -#include - -#include -#include -#include -#include -#include - -#include -#include -#include - -#include "thrift/platform.h" -#include "thrift/generate/t_oop_generator.h" - -using std::map; -using std::ostream; -using std::ostringstream; -using std::string; -using std::stringstream; -using std::vector; - -static const string endl = "\n"; // avoid ostream << std::endl flushes - -struct member_mapping_scope { - void* scope_member; - std::map mapping_table; -}; - -class t_csharp_generator : public t_oop_generator { -public: - t_csharp_generator(t_program* program, - const std::map& parsed_options, - const std::string& option_string) - : t_oop_generator(program) { - (void)option_string; - - std::map::const_iterator iter; - - async_ = false; - nullable_ = false; - hashcode_ = false; - union_ = false; - serialize_ = false; - wcf_ = false; - wcf_namespace_.clear(); - for( iter = parsed_options.begin(); iter != parsed_options.end(); ++iter) { - if( iter->first.compare("async") == 0) { - async_ = true; - } else if( iter->first.compare("nullable") == 0) { - nullable_ = true; - } else if( iter->first.compare("hashcode") == 0) { - hashcode_ = true; - } else if( iter->first.compare("union") == 0) { - union_ = true; - } else if( iter->first.compare("serial") == 0) { - serialize_ = true; - wcf_namespace_ = iter->second; // since there can be only one namespace - } else if( iter->first.compare("wcf") == 0) { - wcf_ = true; - wcf_namespace_ = iter->second; - } else { - throw "unknown option csharp:" + iter->first; - } - } - - out_dir_base_ = "gen-csharp"; - } - void init_generator(); - void close_generator(); - - void generate_consts(std::vector consts); - - void generate_typedef(t_typedef* ttypedef); - void generate_enum(t_enum* tenum); - void generate_struct(t_struct* tstruct); - void generate_union(t_struct* tunion); - void generate_xception(t_struct* txception); - void generate_service(t_service* tservice); - void generate_property(ostream& out, t_field* tfield, bool isPublic, bool generateIsset); - void generate_csharp_property(ostream& out, - t_field* tfield, - bool isPublic, - bool includeIsset = true, - std::string fieldPrefix = ""); - bool print_const_value(std::ostream& out, - std::string name, - t_type* type, - t_const_value* value, - bool in_static, - bool defval = false, - bool needtype = false); - std::string render_const_value(std::ostream& out, - std::string name, - t_type* type, - t_const_value* value); - void print_const_constructor(std::ostream& out, std::vector consts); - void print_const_def_value(std::ostream& out, - std::string name, - t_type* type, - t_const_value* value); - - void generate_csharp_struct(t_struct* tstruct, bool is_exception); - void generate_csharp_union(t_struct* tunion); - void generate_csharp_struct_definition(std::ostream& out, - t_struct* tstruct, - bool is_xception = false, - bool in_class = false, - bool is_result = false); - void generate_csharp_union_definition(std::ostream& out, t_struct* tunion); - void generate_csharp_union_class(std::ostream& out, t_struct* tunion, t_field* tfield); - void generate_csharp_wcffault(std::ostream& out, t_struct* tstruct); - void generate_csharp_struct_reader(std::ostream& out, t_struct* tstruct); - void generate_csharp_struct_result_writer(std::ostream& out, t_struct* tstruct); - void generate_csharp_struct_writer(std::ostream& out, t_struct* tstruct); - void generate_csharp_struct_tostring(std::ostream& out, t_struct* tstruct); - void generate_csharp_struct_equals(std::ostream& out, t_struct* tstruct); - void generate_csharp_struct_hashcode(std::ostream& out, t_struct* tstruct); - void generate_csharp_union_reader(std::ostream& out, t_struct* tunion); - - void generate_function_helpers(t_function* tfunction); - void generate_service_interface(t_service* tservice); - void generate_separate_service_interfaces(t_service* tservice); - void generate_sync_service_interface(t_service* tservice); - void generate_async_service_interface(t_service* tservice); - void generate_combined_service_interface(t_service* tservice); - void generate_silverlight_async_methods(t_service* tservice); - void generate_service_helpers(t_service* tservice); - void generate_service_client(t_service* tservice); - void generate_service_server(t_service* tservice); - void generate_service_server_sync(t_service* tservice); - void generate_service_server_async(t_service* tservice); - void generate_process_function(t_service* tservice, t_function* function); - void generate_process_function_async(t_service* tservice, t_function* function); - - void generate_deserialize_field(std::ostream& out, - t_field* tfield, - std::string prefix = "", - bool is_propertyless = false); - void generate_deserialize_struct(std::ostream& out, t_struct* tstruct, std::string prefix = ""); - void generate_deserialize_container(std::ostream& out, t_type* ttype, std::string prefix = ""); - void generate_deserialize_set_element(std::ostream& out, t_set* tset, std::string prefix = ""); - void generate_deserialize_map_element(std::ostream& out, t_map* tmap, std::string prefix = ""); - void generate_deserialize_list_element(std::ostream& out, t_list* list, std::string prefix = ""); - void generate_serialize_field(std::ostream& out, - t_field* tfield, - std::string prefix = "", - bool is_element = false, - bool is_propertyless = false); - void generate_serialize_struct(std::ostream& out, t_struct* tstruct, std::string prefix = ""); - void generate_serialize_container(std::ostream& out, t_type* ttype, std::string prefix = ""); - void generate_serialize_map_element(std::ostream& out, - t_map* tmap, - std::string iter, - std::string map); - void generate_serialize_set_element(std::ostream& out, t_set* tmap, std::string iter); - void generate_serialize_list_element(std::ostream& out, t_list* tlist, std::string iter); - - void generate_csharp_doc(std::ostream& out, t_field* field); - void generate_csharp_doc(std::ostream& out, t_doc* tdoc); - void generate_csharp_doc(std::ostream& out, t_function* tdoc); - void generate_csharp_docstring_comment(std::ostream& out, string contents); - - void start_csharp_namespace(std::ostream& out); - void end_csharp_namespace(std::ostream& out); - - std::string csharp_type_usings(); - std::string csharp_thrift_usings(); - - std::string type_name(t_type* ttype, - bool in_countainer = false, - bool in_init = false, - bool in_param = false, - bool is_required = false); - std::string base_type_name(t_base_type* tbase, - bool in_container = false, - bool in_param = false, - bool is_required = false); - std::string declare_field(t_field* tfield, bool init = false, std::string prefix = ""); - std::string function_signature_async_begin(t_function* tfunction, std::string prefix = ""); - std::string function_signature_async_end(t_function* tfunction, std::string prefix = ""); - std::string function_signature_async(t_function* tfunction, std::string prefix = ""); - std::string function_signature(t_function* tfunction, std::string prefix = ""); - std::string argument_list(t_struct* tstruct); - std::string type_to_enum(t_type* ttype); - std::string prop_name(t_field* tfield, bool suppress_mapping = false); - std::string get_enum_class_name(t_type* type); - - bool field_has_default(t_field* tfield) { return tfield->get_value() != NULL; } - - bool field_is_required(t_field* tfield) { return tfield->get_req() == t_field::T_REQUIRED; } - - bool type_can_be_null(t_type* ttype) { - while (ttype->is_typedef()) { - ttype = ((t_typedef*)ttype)->get_type(); - } - - return ttype->is_container() || ttype->is_struct() || ttype->is_xception() - || ttype->is_string(); - } - -private: - std::string namespace_name_; - ofstream_with_content_based_conditional_update f_service_; - std::string namespace_dir_; - bool async_; - bool nullable_; - bool union_; - bool hashcode_; - bool serialize_; - bool wcf_; - std::string wcf_namespace_; - - std::map csharp_keywords; - std::vector member_mapping_scopes; - - void init_keywords(); - std::string normalize_name(std::string name); - std::string make_valid_csharp_identifier(std::string const& fromName); - void prepare_member_name_mapping(t_struct* tstruct); - void prepare_member_name_mapping(void* scope, - const vector& members, - const string& structname); - void cleanup_member_name_mapping(void* scope); - string get_mapped_member_name(string oldname); -}; - -void t_csharp_generator::init_generator() { - MKDIR(get_out_dir().c_str()); - namespace_name_ = program_->get_namespace("csharp"); - - string dir = namespace_name_; - string subdir = get_out_dir().c_str(); - string::size_type loc; - - while ((loc = dir.find(".")) != string::npos) { - subdir = subdir + "/" + dir.substr(0, loc); - MKDIR(subdir.c_str()); - dir = dir.substr(loc + 1); - } - if (dir.size() > 0) { - subdir = subdir + "/" + dir; - MKDIR(subdir.c_str()); - } - - namespace_dir_ = subdir; - init_keywords(); - - while( ! member_mapping_scopes.empty()) { - cleanup_member_name_mapping( member_mapping_scopes.back().scope_member); - } - - pverbose("C# options:\n"); - pverbose("- async ...... %s\n", (async_ ? "ON" : "off")); - pverbose("- nullable ... %s\n", (nullable_ ? "ON" : "off")); - pverbose("- union ...... %s\n", (union_ ? "ON" : "off")); - pverbose("- hashcode ... %s\n", (hashcode_ ? "ON" : "off")); - pverbose("- serialize .. %s\n", (serialize_ ? "ON" : "off")); - pverbose("- wcf ........ %s\n", (wcf_ ? "ON" : "off")); -} - -std::string t_csharp_generator::normalize_name(std::string name) { - string tmp(name); - std::transform(tmp.begin(), tmp.end(), tmp.begin(), static_cast(std::tolower)); - - // un-conflict keywords by prefixing with "@" - if (csharp_keywords.find(tmp) != csharp_keywords.end()) { - return "@" + name; - } - - // no changes necessary - return name; -} - -void t_csharp_generator::init_keywords() { - csharp_keywords.clear(); - - // C# keywords - csharp_keywords["abstract"] = 1; - csharp_keywords["as"] = 1; - csharp_keywords["base"] = 1; - csharp_keywords["bool"] = 1; - csharp_keywords["break"] = 1; - csharp_keywords["byte"] = 1; - csharp_keywords["case"] = 1; - csharp_keywords["catch"] = 1; - csharp_keywords["char"] = 1; - csharp_keywords["checked"] = 1; - csharp_keywords["class"] = 1; - csharp_keywords["const"] = 1; - csharp_keywords["continue"] = 1; - csharp_keywords["decimal"] = 1; - csharp_keywords["default"] = 1; - csharp_keywords["delegate"] = 1; - csharp_keywords["do"] = 1; - csharp_keywords["double"] = 1; - csharp_keywords["else"] = 1; - csharp_keywords["enum"] = 1; - csharp_keywords["event"] = 1; - csharp_keywords["explicit"] = 1; - csharp_keywords["extern"] = 1; - csharp_keywords["false"] = 1; - csharp_keywords["finally"] = 1; - csharp_keywords["fixed"] = 1; - csharp_keywords["float"] = 1; - csharp_keywords["for"] = 1; - csharp_keywords["foreach"] = 1; - csharp_keywords["goto"] = 1; - csharp_keywords["if"] = 1; - csharp_keywords["implicit"] = 1; - csharp_keywords["in"] = 1; - csharp_keywords["int"] = 1; - csharp_keywords["interface"] = 1; - csharp_keywords["internal"] = 1; - csharp_keywords["is"] = 1; - csharp_keywords["lock"] = 1; - csharp_keywords["long"] = 1; - csharp_keywords["namespace"] = 1; - csharp_keywords["new"] = 1; - csharp_keywords["null"] = 1; - csharp_keywords["object"] = 1; - csharp_keywords["operator"] = 1; - csharp_keywords["out"] = 1; - csharp_keywords["override"] = 1; - csharp_keywords["params"] = 1; - csharp_keywords["private"] = 1; - csharp_keywords["protected"] = 1; - csharp_keywords["public"] = 1; - csharp_keywords["readonly"] = 1; - csharp_keywords["ref"] = 1; - csharp_keywords["return"] = 1; - csharp_keywords["sbyte"] = 1; - csharp_keywords["sealed"] = 1; - csharp_keywords["short"] = 1; - csharp_keywords["sizeof"] = 1; - csharp_keywords["stackalloc"] = 1; - csharp_keywords["static"] = 1; - csharp_keywords["string"] = 1; - csharp_keywords["struct"] = 1; - csharp_keywords["switch"] = 1; - csharp_keywords["this"] = 1; - csharp_keywords["throw"] = 1; - csharp_keywords["true"] = 1; - csharp_keywords["try"] = 1; - csharp_keywords["typeof"] = 1; - csharp_keywords["uint"] = 1; - csharp_keywords["ulong"] = 1; - csharp_keywords["unchecked"] = 1; - csharp_keywords["unsafe"] = 1; - csharp_keywords["ushort"] = 1; - csharp_keywords["using"] = 1; - csharp_keywords["virtual"] = 1; - csharp_keywords["void"] = 1; - csharp_keywords["volatile"] = 1; - csharp_keywords["while"] = 1; - - // C# contextual keywords - csharp_keywords["add"] = 1; - csharp_keywords["alias"] = 1; - csharp_keywords["ascending"] = 1; - csharp_keywords["async"] = 1; - csharp_keywords["await"] = 1; - csharp_keywords["descending"] = 1; - csharp_keywords["dynamic"] = 1; - csharp_keywords["from"] = 1; - csharp_keywords["get"] = 1; - csharp_keywords["global"] = 1; - csharp_keywords["group"] = 1; - csharp_keywords["into"] = 1; - csharp_keywords["join"] = 1; - csharp_keywords["let"] = 1; - csharp_keywords["orderby"] = 1; - csharp_keywords["partial"] = 1; - csharp_keywords["remove"] = 1; - csharp_keywords["select"] = 1; - csharp_keywords["set"] = 1; - csharp_keywords["value"] = 1; - csharp_keywords["var"] = 1; - csharp_keywords["where"] = 1; - csharp_keywords["yield"] = 1; -} - -void t_csharp_generator::start_csharp_namespace(ostream& out) { - if (!namespace_name_.empty()) { - out << "namespace " << namespace_name_ << "\n"; - scope_up(out); - } -} - -void t_csharp_generator::end_csharp_namespace(ostream& out) { - if (!namespace_name_.empty()) { - scope_down(out); - } -} - -string t_csharp_generator::csharp_type_usings() { - return string() + "using System;\n" + "using System.Collections;\n" - + "using System.Collections.Generic;\n" + "using System.Text;\n" + "using System.IO;\n" - + ((async_) ? "using System.Threading.Tasks;\n" : "") + "using Thrift;\n" - + "using Thrift.Collections;\n" + ((serialize_ || wcf_) ? "#if !SILVERLIGHT\n" : "") - + ((serialize_ || wcf_) ? "using System.Xml.Serialization;\n" : "") - + ((serialize_ || wcf_) ? "#endif\n" : "") + (wcf_ ? "//using System.ServiceModel;\n" : "") - + "using System.Runtime.Serialization;\n"; -} - -string t_csharp_generator::csharp_thrift_usings() { - return string() + "using Thrift.Protocol;\n" + "using Thrift.Transport;\n"; -} - -void t_csharp_generator::close_generator() { -} -void t_csharp_generator::generate_typedef(t_typedef* ttypedef) { - (void)ttypedef; -} - -void t_csharp_generator::generate_enum(t_enum* tenum) { - string f_enum_name = namespace_dir_ + "/" + (tenum->get_name()) + ".cs"; - ofstream_with_content_based_conditional_update f_enum; - f_enum.open(f_enum_name.c_str()); - - f_enum << autogen_comment() << endl; - - start_csharp_namespace(f_enum); - - generate_csharp_doc(f_enum, tenum); - - indent(f_enum) << "public enum " << tenum->get_name() << "\n"; - scope_up(f_enum); - - vector constants = tenum->get_constants(); - vector::iterator c_iter; - for (c_iter = constants.begin(); c_iter != constants.end(); ++c_iter) { - generate_csharp_doc(f_enum, *c_iter); - - int value = (*c_iter)->get_value(); - indent(f_enum) << (*c_iter)->get_name() << " = " << value << "," << endl; - } - - scope_down(f_enum); - - end_csharp_namespace(f_enum); - - f_enum.close(); -} - -void t_csharp_generator::generate_consts(std::vector consts) { - if (consts.empty()) { - return; - } - string f_consts_name = namespace_dir_ + '/' + program_name_ + ".Constants.cs"; - ofstream_with_content_based_conditional_update f_consts; - f_consts.open(f_consts_name.c_str()); - - f_consts << autogen_comment() << csharp_type_usings() << endl; - - start_csharp_namespace(f_consts); - - indent(f_consts) << "public static class " << make_valid_csharp_identifier(program_name_) - << "Constants" << endl; - scope_up(f_consts); - - vector::iterator c_iter; - bool need_static_constructor = false; - for (c_iter = consts.begin(); c_iter != consts.end(); ++c_iter) { - generate_csharp_doc(f_consts, (*c_iter)); - if (print_const_value(f_consts, - (*c_iter)->get_name(), - (*c_iter)->get_type(), - (*c_iter)->get_value(), - false)) { - need_static_constructor = true; - } - } - - if (need_static_constructor) { - print_const_constructor(f_consts, consts); - } - - scope_down(f_consts); - end_csharp_namespace(f_consts); - f_consts.close(); -} - -void t_csharp_generator::print_const_def_value(std::ostream& out, - string name, - t_type* type, - t_const_value* value) { - if (type->is_struct() || type->is_xception()) { - const vector& fields = ((t_struct*)type)->get_members(); - vector::const_iterator f_iter; - const map& val = value->get_map(); - map::const_iterator v_iter; - prepare_member_name_mapping((t_struct*)type); - for (v_iter = val.begin(); v_iter != val.end(); ++v_iter) { - t_field* field = NULL; - for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { - if ((*f_iter)->get_name() == v_iter->first->get_string()) { - field = (*f_iter); - } - } - if (field == NULL) { - throw "type error: " + type->get_name() + " has no field " + v_iter->first->get_string(); - } - t_type* field_type = field->get_type(); - string val = render_const_value(out, name, field_type, v_iter->second); - indent(out) << name << "." << prop_name(field) << " = " << val << ";" << endl; - } - cleanup_member_name_mapping((t_struct*)type); - } else if (type->is_map()) { - t_type* ktype = ((t_map*)type)->get_key_type(); - t_type* vtype = ((t_map*)type)->get_val_type(); - const map& val = value->get_map(); - map::const_iterator v_iter; - for (v_iter = val.begin(); v_iter != val.end(); ++v_iter) { - string key = render_const_value(out, name, ktype, v_iter->first); - string val = render_const_value(out, name, vtype, v_iter->second); - indent(out) << name << "[" << key << "]" - << " = " << val << ";" << endl; - } - } else if (type->is_list() || type->is_set()) { - t_type* etype; - if (type->is_list()) { - etype = ((t_list*)type)->get_elem_type(); - } else { - etype = ((t_set*)type)->get_elem_type(); - } - - const vector& val = value->get_list(); - vector::const_iterator v_iter; - for (v_iter = val.begin(); v_iter != val.end(); ++v_iter) { - string val = render_const_value(out, name, etype, *v_iter); - indent(out) << name << ".Add(" << val << ");" << endl; - } - } -} - -void t_csharp_generator::print_const_constructor(std::ostream& out, std::vector consts) { - indent(out) << "static " << make_valid_csharp_identifier(program_name_).c_str() << "Constants()" - << endl; - scope_up(out); - vector::iterator c_iter; - for (c_iter = consts.begin(); c_iter != consts.end(); ++c_iter) { - string name = (*c_iter)->get_name(); - t_type* type = (*c_iter)->get_type(); - t_const_value* value = (*c_iter)->get_value(); - - print_const_def_value(out, name, type, value); - } - scope_down(out); -} - -// it seems like all that methods that call this are using in_static to be the opposite of what it -// would imply -bool t_csharp_generator::print_const_value(std::ostream& out, - string name, - t_type* type, - t_const_value* value, - bool in_static, - bool defval, - bool needtype) { - indent(out); - bool need_static_construction = !in_static; - while (type->is_typedef()) { - type = ((t_typedef*)type)->get_type(); - } - - if (!defval || needtype) { - out << (in_static ? "" : type->is_base_type() ? "public const " : "public static ") - << type_name(type) << " "; - } - if (type->is_base_type()) { - string v2 = render_const_value(out, name, type, value); - out << name << " = " << v2 << ";" << endl; - need_static_construction = false; - } else if (type->is_enum()) { - out << name << " = " << type_name(type, false, true) << "." << value->get_identifier_name() - << ";" << endl; - need_static_construction = false; - } else if (type->is_struct() || type->is_xception()) { - out << name << " = new " << type_name(type) << "();" << endl; - } else if (type->is_map()) { - out << name << " = new " << type_name(type, true, true) << "();" << endl; - } else if (type->is_list() || type->is_set()) { - out << name << " = new " << type_name(type) << "();" << endl; - } - - if (defval && !type->is_base_type() && !type->is_enum()) { - print_const_def_value(out, name, type, value); - } - - return need_static_construction; -} - -std::string t_csharp_generator::render_const_value(ostream& out, - string name, - t_type* type, - t_const_value* value) { - (void)name; - std::ostringstream render; - - if (type->is_base_type()) { - t_base_type::t_base tbase = ((t_base_type*)type)->get_base(); - switch (tbase) { - case t_base_type::TYPE_STRING: - render << '"' << get_escaped_string(value) << '"'; - break; - case t_base_type::TYPE_BOOL: - render << ((value->get_integer() > 0) ? "true" : "false"); - break; - case t_base_type::TYPE_I8: - case t_base_type::TYPE_I16: - case t_base_type::TYPE_I32: - case t_base_type::TYPE_I64: - render << value->get_integer(); - break; - case t_base_type::TYPE_DOUBLE: - if (value->get_type() == t_const_value::CV_INTEGER) { - render << value->get_integer(); - } else { - render << value->get_double(); - } - break; - default: - throw "compiler error: no const of base type " + t_base_type::t_base_name(tbase); - } - } else if (type->is_enum()) { - render << type->get_name() << "." << value->get_identifier_name(); - } else { - string t = tmp("tmp"); - print_const_value(out, t, type, value, true, true, true); - render << t; - } - - return render.str(); -} - -void t_csharp_generator::generate_struct(t_struct* tstruct) { - if (union_ && tstruct->is_union()) { - generate_csharp_union(tstruct); - } else { - generate_csharp_struct(tstruct, false); - } -} - -void t_csharp_generator::generate_xception(t_struct* txception) { - generate_csharp_struct(txception, true); -} - -void t_csharp_generator::generate_csharp_struct(t_struct* tstruct, bool is_exception) { - string f_struct_name = namespace_dir_ + "/" + (tstruct->get_name()) + ".cs"; - ofstream_with_content_based_conditional_update f_struct; - - f_struct.open(f_struct_name.c_str()); - - f_struct << autogen_comment() << csharp_type_usings() << csharp_thrift_usings() << endl; - - generate_csharp_struct_definition(f_struct, tstruct, is_exception); - - f_struct.close(); -} - -void t_csharp_generator::generate_csharp_struct_definition(ostream& out, - t_struct* tstruct, - bool is_exception, - bool in_class, - bool is_result) { - - if (!in_class) { - start_csharp_namespace(out); - } - - out << endl; - - generate_csharp_doc(out, tstruct); - prepare_member_name_mapping(tstruct); - - indent(out) << "#if !SILVERLIGHT" << endl; - indent(out) << "[Serializable]" << endl; - indent(out) << "#endif" << endl; - if ((serialize_ || wcf_) && !is_exception) { - indent(out) << "[DataContract(Namespace=\"" << wcf_namespace_ << "\")]" - << endl; // do not make exception classes directly WCF serializable, we provide a - // separate "fault" for that - } - bool is_final = (tstruct->annotations_.find("final") != tstruct->annotations_.end()); - - indent(out) << "public " << (is_final ? "sealed " : "") << "partial class " - << normalize_name(tstruct->get_name()) << " : "; - - if (is_exception) { - out << "TException, "; - } - out << "TBase"; - - out << endl; - - scope_up(out); - - const vector& members = tstruct->get_members(); - vector::const_iterator m_iter; - - // make private members with public Properties - for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) { - // if the field is requied, then we use auto-properties - if (!field_is_required((*m_iter)) && (!nullable_ || field_has_default((*m_iter)))) { - indent(out) << "private " << declare_field(*m_iter, false, "_") << endl; - } - } - out << endl; - - bool has_non_required_fields = false; - bool has_non_required_default_value_fields = false; - bool has_required_fields = false; - for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) { - generate_csharp_doc(out, *m_iter); - generate_property(out, *m_iter, true, true); - bool is_required = field_is_required((*m_iter)); - bool has_default = field_has_default((*m_iter)); - if (is_required) { - has_required_fields = true; - } else { - if (has_default) { - has_non_required_default_value_fields = true; - } - has_non_required_fields = true; - } - } - - bool generate_isset = (nullable_ && has_non_required_default_value_fields) - || (!nullable_ && has_non_required_fields); - if (generate_isset) { - out << endl; - if (serialize_ || wcf_) { - out << indent() << "[XmlIgnore] // XmlSerializer" << endl << indent() - << "[DataMember(Order = 1)] // XmlObjectSerializer, DataContractJsonSerializer, etc." - << endl; - } - out << indent() << "public Isset __isset;" << endl << indent() << "#if !SILVERLIGHT" << endl - << indent() << "[Serializable]" << endl << indent() << "#endif" << endl; - if (serialize_ || wcf_) { - indent(out) << "[DataContract]" << endl; - } - indent(out) << "public struct Isset {" << endl; - indent_up(); - for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) { - bool is_required = field_is_required((*m_iter)); - bool has_default = field_has_default((*m_iter)); - // if it is required, don't need Isset for that variable - // if it is not required, if it has a default value, we need to generate Isset - // if we are not nullable, then we generate Isset - if (!is_required && (!nullable_ || has_default)) { - if (serialize_ || wcf_) { - indent(out) << "[DataMember]" << endl; - } - indent(out) << "public bool " << normalize_name((*m_iter)->get_name()) << ";" << endl; - } - } - - indent_down(); - indent(out) << "}" << endl << endl; - - if (generate_isset && (serialize_ || wcf_)) { - indent(out) << "#region XmlSerializer support" << endl << endl; - - for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) { - bool is_required = field_is_required((*m_iter)); - bool has_default = field_has_default((*m_iter)); - // if it is required, don't need Isset for that variable - // if it is not required, if it has a default value, we need to generate Isset - // if we are not nullable, then we generate Isset - if (!is_required && (!nullable_ || has_default)) { - indent(out) << "public bool ShouldSerialize" << prop_name((*m_iter)) << "()" << endl; - indent(out) << "{" << endl; - indent_up(); - indent(out) << "return __isset." << normalize_name((*m_iter)->get_name()) << ";" << endl; - indent_down(); - indent(out) << "}" << endl << endl; - } - } - - indent(out) << "#endregion XmlSerializer support" << endl << endl; - } - } - - // We always want a default, no argument constructor for Reading - indent(out) << "public " << normalize_name(tstruct->get_name()) << "() {" << endl; - indent_up(); - - for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) { - t_type* t = (*m_iter)->get_type(); - while (t->is_typedef()) { - t = ((t_typedef*)t)->get_type(); - } - if ((*m_iter)->get_value() != NULL) { - if (field_is_required((*m_iter))) { - print_const_value(out, "this." + prop_name(*m_iter), t, (*m_iter)->get_value(), true, true); - } else { - print_const_value(out, - "this._" + (*m_iter)->get_name(), - t, - (*m_iter)->get_value(), - true, - true); - // Optionals with defaults are marked set - indent(out) << "this.__isset." << normalize_name((*m_iter)->get_name()) << " = true;" - << endl; - } - } - } - indent_down(); - indent(out) << "}" << endl << endl; - - if (has_required_fields) { - indent(out) << "public " << tstruct->get_name() << "("; - bool first = true; - for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) { - if (field_is_required((*m_iter))) { - if (first) { - first = false; - } else { - out << ", "; - } - out << type_name((*m_iter)->get_type()) << " " << normalize_name((*m_iter)->get_name()); - } - } - out << ") : this() {" << endl; - indent_up(); - - for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) { - if (field_is_required((*m_iter))) { - indent(out) << "this." << prop_name((*m_iter)) << " = " << normalize_name((*m_iter)->get_name()) << ";" - << endl; - } - } - - indent_down(); - indent(out) << "}" << endl << endl; - } - - generate_csharp_struct_reader(out, tstruct); - if (is_result) { - generate_csharp_struct_result_writer(out, tstruct); - } else { - generate_csharp_struct_writer(out, tstruct); - } - if (hashcode_) { - generate_csharp_struct_equals(out, tstruct); - generate_csharp_struct_hashcode(out, tstruct); - } - generate_csharp_struct_tostring(out, tstruct); - scope_down(out); - out << endl; - - // generate a corresponding WCF fault to wrap the exception - if ((serialize_ || wcf_) && is_exception) { - generate_csharp_wcffault(out, tstruct); - } - - cleanup_member_name_mapping(tstruct); - if (!in_class) { - end_csharp_namespace(out); - } -} - -void t_csharp_generator::generate_csharp_wcffault(ostream& out, t_struct* tstruct) { - out << endl; - indent(out) << "#if !SILVERLIGHT" << endl; - indent(out) << "[Serializable]" << endl; - indent(out) << "#endif" << endl; - indent(out) << "[DataContract]" << endl; - bool is_final = (tstruct->annotations_.find("final") != tstruct->annotations_.end()); - - indent(out) << "public " << (is_final ? "sealed " : "") << "partial class " << tstruct->get_name() - << "Fault" << endl; - - scope_up(out); - - const vector& members = tstruct->get_members(); - vector::const_iterator m_iter; - - // make private members with public Properties - for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) { - indent(out) << "private " << declare_field(*m_iter, false, "_") << endl; - } - out << endl; - - for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) { - generate_property(out, *m_iter, true, false); - } - - scope_down(out); - out << endl; -} - -void t_csharp_generator::generate_csharp_struct_reader(ostream& out, t_struct* tstruct) { - indent(out) << "public void Read (TProtocol iprot)" << endl; - scope_up(out); - - out << indent() << "iprot.IncrementRecursionDepth();" << endl; - out << indent() << "try" << endl; - scope_up(out); - - const vector& fields = tstruct->get_members(); - vector::const_iterator f_iter; - - // Required variables aren't in __isset, so we need tmp vars to check them - for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { - if (field_is_required((*f_iter))) { - indent(out) << "bool isset_" << (*f_iter)->get_name() << " = false;" << endl; - } - } - - indent(out) << "TField field;" << endl << indent() << "iprot.ReadStructBegin();" << endl; - - indent(out) << "while (true)" << endl; - scope_up(out); - - indent(out) << "field = iprot.ReadFieldBegin();" << endl; - - indent(out) << "if (field.Type == TType.Stop) { " << endl; - indent_up(); - indent(out) << "break;" << endl; - indent_down(); - indent(out) << "}" << endl; - - indent(out) << "switch (field.ID)" << endl; - - scope_up(out); - - for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { - bool is_required = field_is_required((*f_iter)); - indent(out) << "case " << (*f_iter)->get_key() << ":" << endl; - indent_up(); - indent(out) << "if (field.Type == " << type_to_enum((*f_iter)->get_type()) << ") {" << endl; - indent_up(); - - generate_deserialize_field(out, *f_iter); - if (is_required) { - indent(out) << "isset_" << (*f_iter)->get_name() << " = true;" << endl; - } - - indent_down(); - out << indent() << "} else { " << endl << indent() << " TProtocolUtil.Skip(iprot, field.Type);" - << endl << indent() << "}" << endl << indent() << "break;" << endl; - indent_down(); - } - - indent(out) << "default: " << endl; - indent_up(); - indent(out) << "TProtocolUtil.Skip(iprot, field.Type);" << endl; - indent(out) << "break;" << endl; - indent_down(); - - scope_down(out); - - indent(out) << "iprot.ReadFieldEnd();" << endl; - - scope_down(out); - - indent(out) << "iprot.ReadStructEnd();" << endl; - - for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { - if (field_is_required((*f_iter))) { - indent(out) << "if (!isset_" << (*f_iter)->get_name() << ")" << endl; - indent_up(); - out << indent() - << "throw new TProtocolException(TProtocolException.INVALID_DATA, " - << "\"required field " << prop_name((*f_iter)) << " not set\");" - << endl; - indent_down(); - } - } - - scope_down(out); - out << indent() << "finally" << endl; - scope_up(out); - out << indent() << "iprot.DecrementRecursionDepth();" << endl; - scope_down(out); - - indent_down(); - - indent(out) << "}" << endl << endl; -} - -void t_csharp_generator::generate_csharp_struct_writer(ostream& out, t_struct* tstruct) { - out << indent() << "public void Write(TProtocol oprot) {" << endl; - indent_up(); - - out << indent() << "oprot.IncrementRecursionDepth();" << endl; - out << indent() << "try" << endl; - scope_up(out); - - string name = tstruct->get_name(); - const vector& fields = tstruct->get_sorted_members(); - vector::const_iterator f_iter; - - indent(out) << "TStruct struc = new TStruct(\"" << name << "\");" << endl; - indent(out) << "oprot.WriteStructBegin(struc);" << endl; - - if (fields.size() > 0) { - indent(out) << "TField field = new TField();" << endl; - for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { - bool is_required = field_is_required((*f_iter)); - bool has_default = field_has_default((*f_iter)); - bool null_allowed = type_can_be_null((*f_iter)->get_type()); - - if (is_required) - { - if (null_allowed) { - indent(out) << "if (" << prop_name((*f_iter)) << " == null)" << endl; - indent_up(); - out << indent() - << "throw new TProtocolException(TProtocolException.INVALID_DATA, " - << "\"required field " << prop_name((*f_iter)) << " not set\");" - << endl; - indent_down(); - } - } - else - { - if (nullable_ && !has_default) { - indent(out) << "if (" << prop_name((*f_iter)) << " != null) {" << endl; - } - else if (null_allowed) { - out << indent() - << "if (" << prop_name((*f_iter)) << " != null && __isset." - << normalize_name((*f_iter)->get_name()) << ") {" - << endl; - } - else { - indent(out) << "if (__isset." << normalize_name((*f_iter)->get_name()) << ") {" << endl; - } - indent_up(); - } - indent(out) << "field.Name = \"" << (*f_iter)->get_name() << "\";" << endl; - indent(out) << "field.Type = " << type_to_enum((*f_iter)->get_type()) << ";" << endl; - indent(out) << "field.ID = " << (*f_iter)->get_key() << ";" << endl; - indent(out) << "oprot.WriteFieldBegin(field);" << endl; - - generate_serialize_field(out, *f_iter); - - indent(out) << "oprot.WriteFieldEnd();" << endl; - if (!is_required) { - indent_down(); - indent(out) << "}" << endl; - } - } - } - - indent(out) << "oprot.WriteFieldStop();" << endl; - indent(out) << "oprot.WriteStructEnd();" << endl; - - scope_down(out); - out << indent() << "finally" << endl; - scope_up(out); - out << indent() << "oprot.DecrementRecursionDepth();" << endl; - scope_down(out); - - indent_down(); - - indent(out) << "}" << endl << endl; -} - -void t_csharp_generator::generate_csharp_struct_result_writer(ostream& out, t_struct* tstruct) { - indent(out) << "public void Write(TProtocol oprot) {" << endl; - indent_up(); - - out << indent() << "oprot.IncrementRecursionDepth();" << endl; - out << indent() << "try" << endl; - scope_up(out); - - string name = tstruct->get_name(); - const vector& fields = tstruct->get_sorted_members(); - vector::const_iterator f_iter; - - indent(out) << "TStruct struc = new TStruct(\"" << name << "\");" << endl; - indent(out) << "oprot.WriteStructBegin(struc);" << endl; - - if (fields.size() > 0) { - indent(out) << "TField field = new TField();" << endl; - bool first = true; - for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { - if (first) { - first = false; - out << endl << indent() << "if "; - } else { - out << " else if "; - } - - if (nullable_) { - out << "(this." << prop_name((*f_iter)) << " != null) {" << endl; - } else { - out << "(this.__isset." << normalize_name((*f_iter)->get_name()) << ") {" << endl; - } - indent_up(); - - bool null_allowed = !nullable_ && type_can_be_null((*f_iter)->get_type()); - if (null_allowed) { - indent(out) << "if (" << prop_name(*f_iter) << " != null) {" << endl; - indent_up(); - } - - indent(out) << "field.Name = \"" << prop_name(*f_iter) << "\";" << endl; - indent(out) << "field.Type = " << type_to_enum((*f_iter)->get_type()) << ";" << endl; - indent(out) << "field.ID = " << (*f_iter)->get_key() << ";" << endl; - indent(out) << "oprot.WriteFieldBegin(field);" << endl; - - generate_serialize_field(out, *f_iter); - - indent(out) << "oprot.WriteFieldEnd();" << endl; - - if (null_allowed) { - indent_down(); - indent(out) << "}" << endl; - } - - indent_down(); - indent(out) << "}"; - } - } - - out << endl << indent() << "oprot.WriteFieldStop();" << endl << indent() - << "oprot.WriteStructEnd();" << endl; - - scope_down(out); - out << indent() << "finally" << endl; - scope_up(out); - out << indent() << "oprot.DecrementRecursionDepth();" << endl; - scope_down(out); - - indent_down(); - - indent(out) << "}" << endl << endl; -} - -void t_csharp_generator::generate_csharp_struct_tostring(ostream& out, t_struct* tstruct) { - indent(out) << "public override string ToString() {" << endl; - indent_up(); - - indent(out) << "StringBuilder __sb = new StringBuilder(\"" << tstruct->get_name() << "(\");" - << endl; - - const vector& fields = tstruct->get_members(); - vector::const_iterator f_iter; - - bool useFirstFlag = false; - for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { - if (!field_is_required((*f_iter))) { - indent(out) << "bool __first = true;" << endl; - useFirstFlag = true; - } - break; - } - - bool had_required = false; // set to true after first required field has been processed - - for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { - bool is_required = field_is_required((*f_iter)); - bool has_default = field_has_default((*f_iter)); - if (nullable_ && !has_default && !is_required) { - indent(out) << "if (" << prop_name((*f_iter)) << " != null) {" << endl; - indent_up(); - } else if (!is_required) { - bool null_allowed = type_can_be_null((*f_iter)->get_type()); - if (null_allowed) { - indent(out) << "if (" << prop_name((*f_iter)) << " != null && __isset." - << normalize_name((*f_iter)->get_name()) << ") {" << endl; - indent_up(); - } else { - indent(out) << "if (__isset." << normalize_name((*f_iter)->get_name()) << ") {" << endl; - indent_up(); - } - } - - if (useFirstFlag && (!had_required)) { - indent(out) << "if(!__first) { __sb.Append(\", \"); }" << endl; - if (!is_required) { - indent(out) << "__first = false;" << endl; - } - indent(out) << "__sb.Append(\"" << prop_name((*f_iter)) << ": \");" << endl; - } else { - indent(out) << "__sb.Append(\", " << prop_name((*f_iter)) << ": \");" << endl; - } - - t_type* ttype = (*f_iter)->get_type(); - if (ttype->is_xception() || ttype->is_struct()) { - indent(out) << "__sb.Append(" << prop_name((*f_iter)) - << "== null ? \"\" : " << prop_name((*f_iter)) << ".ToString());" << endl; - } else { - indent(out) << "__sb.Append(" << prop_name((*f_iter)) << ");" << endl; - } - - if (!is_required) { - indent_down(); - indent(out) << "}" << endl; - } else { - had_required = true; // now __first must be false, so we don't need to check it anymore - } - } - - indent(out) << "__sb.Append(\")\");" << endl; - indent(out) << "return __sb.ToString();" << endl; - - indent_down(); - indent(out) << "}" << endl << endl; -} - -void t_csharp_generator::generate_csharp_union(t_struct* tunion) { - string f_union_name = namespace_dir_ + "/" + (tunion->get_name()) + ".cs"; - ofstream_with_content_based_conditional_update f_union; - - f_union.open(f_union_name.c_str()); - - f_union << autogen_comment() << csharp_type_usings() << csharp_thrift_usings() << endl; - - generate_csharp_union_definition(f_union, tunion); - - f_union.close(); -} - -void t_csharp_generator::generate_csharp_union_definition(std::ostream& out, t_struct* tunion) { - // Let's define the class first - start_csharp_namespace(out); - - indent(out) << "public abstract partial class " << tunion->get_name() << " : TAbstractBase {" - << endl; - - indent_up(); - - indent(out) << "public abstract void Write(TProtocol protocol);" << endl; - indent(out) << "public readonly bool Isset;" << endl; - indent(out) << "public abstract object Data { get; }" << endl; - - indent(out) << "protected " << tunion->get_name() << "(bool isset) {" << endl; - indent_up(); - indent(out) << "Isset = isset;" << endl; - indent_down(); - indent(out) << "}" << endl << endl; - - indent(out) << "public class ___undefined : " << tunion->get_name() << " {" << endl; - indent_up(); - - indent(out) << "public override object Data { get { return null; } }" << endl; - - indent(out) << "public ___undefined() : base(false) {}" << endl << endl; - - indent(out) << "public override void Write(TProtocol protocol) {" << endl; - indent_up(); - indent(out) << "throw new TProtocolException( TProtocolException.INVALID_DATA, \"Cannot persist " - "an union type which is not set.\");" << endl; - indent_down(); - indent(out) << "}" << endl << endl; - - indent_down(); - indent(out) << "}" << endl << endl; - - const vector& fields = tunion->get_members(); - vector::const_iterator f_iter; - - for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { - generate_csharp_union_class(out, tunion, (*f_iter)); - } - - generate_csharp_union_reader(out, tunion); - - indent_down(); - indent(out) << "}" << endl << endl; - - end_csharp_namespace(out); -} - -void t_csharp_generator::generate_csharp_union_class(std::ostream& out, - t_struct* tunion, - t_field* tfield) { - indent(out) << "public class " << tfield->get_name() << " : " << tunion->get_name() << " {" - << endl; - indent_up(); - indent(out) << "private " << type_name(tfield->get_type()) << " _data;" << endl; - indent(out) << "public override object Data { get { return _data; } }" << endl; - indent(out) << "public " << tfield->get_name() << "(" << type_name(tfield->get_type()) - << " data) : base(true) {" << endl; - indent_up(); - indent(out) << "this._data = data;" << endl; - indent_down(); - indent(out) << "}" << endl; - indent(out) << "public override void Write(TProtocol oprot) {" << endl; - indent_up(); - - out << indent() << "oprot.IncrementRecursionDepth();" << endl; - out << indent() << "try" << endl; - scope_up(out); - - indent(out) << "TStruct struc = new TStruct(\"" << tunion->get_name() << "\");" << endl; - indent(out) << "oprot.WriteStructBegin(struc);" << endl; - - indent(out) << "TField field = new TField();" << endl; - indent(out) << "field.Name = \"" << tfield->get_name() << "\";" << endl; - indent(out) << "field.Type = " << type_to_enum(tfield->get_type()) << ";" << endl; - indent(out) << "field.ID = " << tfield->get_key() << ";" << endl; - indent(out) << "oprot.WriteFieldBegin(field);" << endl; - - generate_serialize_field(out, tfield, "_data", true, true); - - indent(out) << "oprot.WriteFieldEnd();" << endl; - indent(out) << "oprot.WriteFieldStop();" << endl; - indent(out) << "oprot.WriteStructEnd();" << endl; - indent_down(); - - scope_down(out); - out << indent() << "finally" << endl; - scope_up(out); - out << indent() << "oprot.DecrementRecursionDepth();" << endl; - scope_down(out); - - indent(out) << "}" << endl; - - indent_down(); - indent(out) << "}" << endl << endl; -} - -void t_csharp_generator::generate_csharp_struct_equals(ostream& out, t_struct* tstruct) { - indent(out) << "public override bool Equals(object that) {" << endl; - indent_up(); - - indent(out) << "var other = that as " << type_name(tstruct) << ";" << endl; - indent(out) << "if (other == null) return false;" << endl; - indent(out) << "if (ReferenceEquals(this, other)) return true;" << endl; - - const vector& fields = tstruct->get_members(); - vector::const_iterator f_iter; - - bool first = true; - - for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { - if (first) { - first = false; - indent(out) << "return "; - indent_up(); - } else { - out << endl; - indent(out) << "&& "; - } - if (!field_is_required((*f_iter)) && !(nullable_ && !field_has_default((*f_iter)))) { - out << "((__isset." << normalize_name((*f_iter)->get_name()) << " == other.__isset." - << normalize_name((*f_iter)->get_name()) << ") && ((!__isset." - << normalize_name((*f_iter)->get_name()) << ") || ("; - } - t_type* ttype = (*f_iter)->get_type(); - if (ttype->is_container() || ttype->is_binary()) { - out << "TCollections.Equals("; - } else { - out << "System.Object.Equals("; - } - out << prop_name((*f_iter)) << ", other." << prop_name((*f_iter)) << ")"; - if (!field_is_required((*f_iter)) && !(nullable_ && !field_has_default((*f_iter)))) { - out << ")))"; - } - } - if (first) { - indent(out) << "return true;" << endl; - } else { - out << ";" << endl; - indent_down(); - } - - indent_down(); - indent(out) << "}" << endl << endl; -} - -void t_csharp_generator::generate_csharp_struct_hashcode(ostream& out, t_struct* tstruct) { - indent(out) << "public override int GetHashCode() {" << endl; - indent_up(); - - indent(out) << "int hashcode = 0;" << endl; - indent(out) << "unchecked {" << endl; - indent_up(); - - const vector& fields = tstruct->get_members(); - vector::const_iterator f_iter; - - for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { - t_type* ttype = (*f_iter)->get_type(); - indent(out) << "hashcode = (hashcode * 397) ^ "; - if (field_is_required((*f_iter))) { - out << "("; - } else if (nullable_) { - out << "(" << prop_name((*f_iter)) << " == null ? 0 : "; - } else { - out << "(!__isset." << normalize_name((*f_iter)->get_name()) << " ? 0 : "; - } - if (ttype->is_container()) { - out << "(TCollections.GetHashCode(" << prop_name((*f_iter)) << "))"; - } else { - out << "(" << prop_name((*f_iter)) << ".GetHashCode())"; - } - out << ");" << endl; - } - - indent_down(); - indent(out) << "}" << endl; - indent(out) << "return hashcode;" << endl; - - indent_down(); - indent(out) << "}" << endl << endl; -} - -void t_csharp_generator::generate_service(t_service* tservice) { - string f_service_name = namespace_dir_ + "/" + service_name_ + ".cs"; - f_service_.open(f_service_name.c_str()); - - f_service_ << autogen_comment() << csharp_type_usings() << csharp_thrift_usings() << endl; - - start_csharp_namespace(f_service_); - - indent(f_service_) << "public partial class " << normalize_name(service_name_) << " {" << endl; - indent_up(); - - generate_service_interface(tservice); - generate_service_client(tservice); - generate_service_server(tservice); - generate_service_helpers(tservice); - - indent_down(); - - indent(f_service_) << "}" << endl; - end_csharp_namespace(f_service_); - f_service_.close(); -} - -void t_csharp_generator::generate_service_interface(t_service* tservice) { - generate_separate_service_interfaces(tservice); -} - -void t_csharp_generator::generate_separate_service_interfaces(t_service* tservice) { - generate_sync_service_interface(tservice); - - if (async_) { - generate_async_service_interface(tservice); - } - - generate_combined_service_interface(tservice); -} - -void t_csharp_generator::generate_sync_service_interface(t_service* tservice) { - string extends = ""; - string extends_iface = ""; - if (tservice->get_extends() != NULL) { - extends = type_name(tservice->get_extends()); - extends_iface = " : " + extends + ".ISync"; - } - - generate_csharp_doc(f_service_, tservice); - - if (wcf_) { - indent(f_service_) << "[ServiceContract(Namespace=\"" << wcf_namespace_ << "\")]" << endl; - } - indent(f_service_) << "public interface ISync" << extends_iface << " {" << endl; - - indent_up(); - vector functions = tservice->get_functions(); - vector::iterator f_iter; - for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) { - generate_csharp_doc(f_service_, *f_iter); - - // if we're using WCF, add the corresponding attributes - if (wcf_) { - indent(f_service_) << "[OperationContract]" << endl; - - const std::vector& xceptions = (*f_iter)->get_xceptions()->get_members(); - vector::const_iterator x_iter; - for (x_iter = xceptions.begin(); x_iter != xceptions.end(); ++x_iter) { - indent(f_service_) << "[FaultContract(typeof(" - + type_name((*x_iter)->get_type(), false, false) + "Fault))]" << endl; - } - } - - indent(f_service_) << function_signature(*f_iter) << ";" << endl; - } - indent_down(); - f_service_ << indent() << "}" << endl << endl; -} - -void t_csharp_generator::generate_async_service_interface(t_service* tservice) { - string extends = ""; - string extends_iface = ""; - if (tservice->get_extends() != NULL) { - extends = type_name(tservice->get_extends()); - extends_iface = " : " + extends + ".IAsync"; - } - - generate_csharp_doc(f_service_, tservice); - - if (wcf_) { - indent(f_service_) << "[ServiceContract(Namespace=\"" << wcf_namespace_ << "\")]" << endl; - } - indent(f_service_) << "public interface IAsync" << extends_iface << " {" << endl; - - indent_up(); - vector functions = tservice->get_functions(); - vector::iterator f_iter; - for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) { - generate_csharp_doc(f_service_, *f_iter); - - // if we're using WCF, add the corresponding attributes - if (wcf_) { - indent(f_service_) << "[OperationContract]" << endl; - - const std::vector& xceptions = (*f_iter)->get_xceptions()->get_members(); - vector::const_iterator x_iter; - for (x_iter = xceptions.begin(); x_iter != xceptions.end(); ++x_iter) { - indent(f_service_) << "[FaultContract(typeof(" - + type_name((*x_iter)->get_type(), false, false) + "Fault))]" << endl; - } - } - - indent(f_service_) << function_signature_async(*f_iter) << ";" << endl; - } - indent_down(); - f_service_ << indent() << "}" << endl << endl; -} - -void t_csharp_generator::generate_combined_service_interface(t_service* tservice) { - string extends_iface = " : ISync"; - - if (async_) { - extends_iface += ", IAsync"; - } - - generate_csharp_doc(f_service_, tservice); - - if (wcf_) { - indent(f_service_) << "[ServiceContract(Namespace=\"" << wcf_namespace_ << "\")]" << endl; - } - - indent(f_service_) << "public interface Iface" << extends_iface << " {" << endl; - - indent_up(); - - // We need to generate extra old style async methods for silverlight. Since - // this isn't something you'd want to implement server-side, just put them into - // the main Iface interface. - generate_silverlight_async_methods(tservice); - - indent_down(); - - f_service_ << indent() << "}" << endl << endl; -} - -void t_csharp_generator::generate_silverlight_async_methods(t_service* tservice) { - vector functions = tservice->get_functions(); - vector::iterator f_iter; - for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) { - generate_csharp_doc(f_service_, *f_iter); - - // For backwards compatibility, include the Begin_, End_ methods if we're generating - // with the async flag. I'm not sure this is necessary, so someone with more knowledge - // can maybe remove these checks if they know it's safe. - if (!async_) { - indent(f_service_) << "#if SILVERLIGHT" << endl; - } - - indent(f_service_) << function_signature_async_begin(*f_iter, "Begin_") << ";" << endl; - indent(f_service_) << function_signature_async_end(*f_iter, "End_") << ";" << endl; - - if (!async_) { - indent(f_service_) << "#endif" << endl; - } - } -} - -void t_csharp_generator::generate_service_helpers(t_service* tservice) { - vector functions = tservice->get_functions(); - vector::iterator f_iter; - - for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) { - t_struct* ts = (*f_iter)->get_arglist(); - generate_csharp_struct_definition(f_service_, ts, false, true); - generate_function_helpers(*f_iter); - } -} - -void t_csharp_generator::generate_service_client(t_service* tservice) { - string extends = ""; - string extends_client = ""; - if (tservice->get_extends() != NULL) { - extends = type_name(tservice->get_extends()); - extends_client = extends + ".Client, "; - } else { - extends_client = "IDisposable, "; - } - - generate_csharp_doc(f_service_, tservice); - - indent(f_service_) << "public class Client : " << extends_client << "Iface {" << endl; - indent_up(); - indent(f_service_) << "public Client(TProtocol prot) : this(prot, prot)" << endl; - scope_up(f_service_); - scope_down(f_service_); - f_service_ << endl; - - indent(f_service_) << "public Client(TProtocol iprot, TProtocol oprot)"; - if (!extends.empty()) { - f_service_ << " : base(iprot, oprot)"; - } - f_service_ << endl; - - scope_up(f_service_); - if (extends.empty()) { - f_service_ << indent() << "iprot_ = iprot;" << endl << indent() << "oprot_ = oprot;" << endl; - } - scope_down(f_service_); - - f_service_ << endl; - - if (extends.empty()) { - f_service_ << indent() << "protected TProtocol iprot_;" << endl << indent() - << "protected TProtocol oprot_;" << endl << indent() << "protected int seqid_;" - << endl << endl; - - f_service_ << indent() << "public TProtocol InputProtocol" << endl; - scope_up(f_service_); - indent(f_service_) << "get { return iprot_; }" << endl; - scope_down(f_service_); - - f_service_ << indent() << "public TProtocol OutputProtocol" << endl; - scope_up(f_service_); - indent(f_service_) << "get { return oprot_; }" << endl; - scope_down(f_service_); - f_service_ << endl << endl; - - indent(f_service_) << "#region \" IDisposable Support \"" << endl; - indent(f_service_) << "private bool _IsDisposed;" << endl << endl; - indent(f_service_) << "// IDisposable" << endl; - indent(f_service_) << "public void Dispose()" << endl; - scope_up(f_service_); - indent(f_service_) << "Dispose(true);" << endl; - scope_down(f_service_); - indent(f_service_) << endl << endl; - indent(f_service_) << "protected virtual void Dispose(bool disposing)" << endl; - scope_up(f_service_); - indent(f_service_) << "if (!_IsDisposed)" << endl; - scope_up(f_service_); - indent(f_service_) << "if (disposing)" << endl; - scope_up(f_service_); - indent(f_service_) << "if (iprot_ != null)" << endl; - scope_up(f_service_); - indent(f_service_) << "((IDisposable)iprot_).Dispose();" << endl; - scope_down(f_service_); - indent(f_service_) << "if (oprot_ != null)" << endl; - scope_up(f_service_); - indent(f_service_) << "((IDisposable)oprot_).Dispose();" << endl; - scope_down(f_service_); - scope_down(f_service_); - scope_down(f_service_); - indent(f_service_) << "_IsDisposed = true;" << endl; - scope_down(f_service_); - indent(f_service_) << "#endregion" << endl; - f_service_ << endl << endl; - } - - vector functions = tservice->get_functions(); - vector::const_iterator f_iter; - for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) { - string funname = (*f_iter)->get_name(); - - indent(f_service_) << endl; - - if (!async_) { - indent(f_service_) << "#if SILVERLIGHT" << endl; - indent(f_service_) << endl; - } - // Begin_ - indent(f_service_) << "public " << function_signature_async_begin(*f_iter, "Begin_") << endl; - scope_up(f_service_); - indent(f_service_) << "return " - << "send_" << funname << "(callback, state"; - - t_struct* arg_struct = (*f_iter)->get_arglist(); - prepare_member_name_mapping(arg_struct); - - const vector& fields = arg_struct->get_members(); - vector::const_iterator fld_iter; - for (fld_iter = fields.begin(); fld_iter != fields.end(); ++fld_iter) { - f_service_ << ", "; - f_service_ << normalize_name((*fld_iter)->get_name()); - } - f_service_ << ");" << endl; - scope_down(f_service_); - f_service_ << endl; - - // End - indent(f_service_) << "public " << function_signature_async_end(*f_iter, "End_") << endl; - scope_up(f_service_); - indent(f_service_) << "oprot_.Transport.EndFlush(asyncResult);" << endl; - if (!(*f_iter)->is_oneway()) { - f_service_ << indent(); - if (!(*f_iter)->get_returntype()->is_void()) { - f_service_ << "return "; - } - f_service_ << "recv_" << funname << "();" << endl; - } - scope_down(f_service_); - f_service_ << endl; - - // async - bool first; - if (async_) { - indent(f_service_) << "public async " << function_signature_async(*f_iter, "") << endl; - scope_up(f_service_); - - if (!(*f_iter)->get_returntype()->is_void()) { - indent(f_service_) << type_name((*f_iter)->get_returntype()) << " retval;" << endl; - indent(f_service_) << "retval = "; - } else { - indent(f_service_); - } - f_service_ << "await Task.Run(() =>" << endl; - scope_up(f_service_); - indent(f_service_); - if (!(*f_iter)->get_returntype()->is_void()) { - f_service_ << "return "; - } - f_service_ << funname << "("; - first = true; - for (fld_iter = fields.begin(); fld_iter != fields.end(); ++fld_iter) { - if (first) { - first = false; - } else { - f_service_ << ", "; - } - f_service_ << (*fld_iter)->get_name(); - } - f_service_ << ");" << endl; - indent_down(); - indent(f_service_) << "});" << endl; - if (!(*f_iter)->get_returntype()->is_void()) { - indent(f_service_) << "return retval;" << endl; - } - scope_down(f_service_); - f_service_ << endl; - } - - if (!async_) { - indent(f_service_) << "#endif" << endl << endl; - } - - generate_csharp_doc(f_service_, *f_iter); - indent(f_service_) << "public " << function_signature(*f_iter) << endl; - scope_up(f_service_); - - // silverlight invoke - if (!async_) { - indent(f_service_) << "#if SILVERLIGHT" << endl; - - indent(f_service_) << "var asyncResult = Begin_" << funname << "(null, null"; - for (fld_iter = fields.begin(); fld_iter != fields.end(); ++fld_iter) { - f_service_ << ", " << normalize_name((*fld_iter)->get_name()); - } - f_service_ << ");" << endl; - - if (!(*f_iter)->is_oneway()) { - f_service_ << indent(); - if (!(*f_iter)->get_returntype()->is_void()) { - f_service_ << "return "; - } - f_service_ << "End_" << funname << "(asyncResult);" << endl; - } - f_service_ << endl; - - indent(f_service_) << "#else" << endl; - } - - // synchronous invoke - indent(f_service_) << "send_" << funname << "("; - - first = true; - for (fld_iter = fields.begin(); fld_iter != fields.end(); ++fld_iter) { - if (first) { - first = false; - } else { - f_service_ << ", "; - } - f_service_ << normalize_name((*fld_iter)->get_name()); - } - f_service_ << ");" << endl; - - if (!(*f_iter)->is_oneway()) { - f_service_ << indent(); - if (!(*f_iter)->get_returntype()->is_void()) { - f_service_ << "return "; - } - f_service_ << "recv_" << funname << "();" << endl; - } - f_service_ << endl; - - if (!async_) { - indent(f_service_) << "#endif" << endl; - } - scope_down(f_service_); - - // Send - t_function send_function(g_type_void, - string("send_") + (*f_iter)->get_name(), - (*f_iter)->get_arglist()); - - string argsname = (*f_iter)->get_name() + "_args"; - - if (!async_) { - indent(f_service_) << "#if SILVERLIGHT" << endl; - } - - indent(f_service_) << "public " << function_signature_async_begin(&send_function) << endl; - scope_up(f_service_); - - f_service_ << indent() << "oprot_.WriteMessageBegin(new TMessage(\"" << funname << "\", " - << ((*f_iter)->is_oneway() ? "TMessageType.Oneway" : "TMessageType.Call") - << ", seqid_));" << endl << indent() << argsname << " args = new " << argsname - << "();" << endl; - - for (fld_iter = fields.begin(); fld_iter != fields.end(); ++fld_iter) { - f_service_ << indent() << "args." << prop_name(*fld_iter) << " = " - << normalize_name((*fld_iter)->get_name()) << ";" << endl; - } - - f_service_ << indent() << "args.Write(oprot_);" << endl << indent() - << "oprot_.WriteMessageEnd();" << endl; - indent(f_service_) << "return oprot_.Transport.BeginFlush(callback, state);" << endl; - - scope_down(f_service_); - f_service_ << endl; - - if (!async_) { - indent(f_service_) << "#else" << endl; - f_service_ << endl; - } - - indent(f_service_) << "public " << function_signature(&send_function) << endl; - scope_up(f_service_); - - f_service_ << indent() << "oprot_.WriteMessageBegin(new TMessage(\"" << funname << "\", " - << ((*f_iter)->is_oneway() ? "TMessageType.Oneway" : "TMessageType.Call") - << ", seqid_));" << endl << indent() << argsname << " args = new " << argsname - << "();" << endl; - - for (fld_iter = fields.begin(); fld_iter != fields.end(); ++fld_iter) { - f_service_ << indent() << "args." << prop_name(*fld_iter) << " = " - << normalize_name((*fld_iter)->get_name()) << ";" << endl; - } - - f_service_ << indent() << "args.Write(oprot_);" << endl << indent() - << "oprot_.WriteMessageEnd();" << endl; - - indent(f_service_) << "oprot_.Transport.Flush();" << endl; - cleanup_member_name_mapping(arg_struct); - scope_down(f_service_); - - if (!async_) { - indent(f_service_) << "#endif" << endl; - } - - f_service_ << endl; - - if (!(*f_iter)->is_oneway()) { - string resultname = (*f_iter)->get_name() + "_result"; - - t_struct noargs(program_); - t_function recv_function((*f_iter)->get_returntype(), - string("recv_") + (*f_iter)->get_name(), - &noargs, - (*f_iter)->get_xceptions()); - indent(f_service_) << "public " << function_signature(&recv_function) << endl; - scope_up(f_service_); - - t_struct* xs = (*f_iter)->get_xceptions(); - prepare_member_name_mapping(xs, xs->get_members(), resultname); - - f_service_ << indent() << "TMessage msg = iprot_.ReadMessageBegin();" << endl << indent() - << "if (msg.Type == TMessageType.Exception) {" << endl; - indent_up(); - f_service_ << indent() << "TApplicationException x = TApplicationException.Read(iprot_);" - << endl << indent() << "iprot_.ReadMessageEnd();" << endl << indent() << "throw x;" - << endl; - indent_down(); - f_service_ << indent() << "}" << endl << indent() << resultname << " result = new " - << resultname << "();" << endl << indent() << "result.Read(iprot_);" << endl - << indent() << "iprot_.ReadMessageEnd();" << endl; - - if (!(*f_iter)->get_returntype()->is_void()) { - if (nullable_) { - if (type_can_be_null((*f_iter)->get_returntype())) { - f_service_ << indent() << "if (result.Success != null) {" << endl << indent() - << " return result.Success;" << endl << indent() << "}" << endl; - } else { - f_service_ << indent() << "if (result.Success.HasValue) {" << endl << indent() - << " return result.Success.Value;" << endl << indent() << "}" << endl; - } - } else { - f_service_ << indent() << "if (result.__isset.success) {" << endl << indent() - << " return result.Success;" << endl << indent() << "}" << endl; - } - } - - const std::vector& xceptions = xs->get_members(); - vector::const_iterator x_iter; - for (x_iter = xceptions.begin(); x_iter != xceptions.end(); ++x_iter) { - if (nullable_) { - f_service_ << indent() << "if (result." << prop_name(*x_iter) << " != null) {" << endl - << indent() << " throw result." << prop_name(*x_iter) << ";" << endl - << indent() << "}" << endl; - } else { - f_service_ << indent() << "if (result.__isset." << normalize_name((*x_iter)->get_name()) - << ") {" << endl << indent() << " throw result." << prop_name(*x_iter) << ";" - << endl << indent() << "}" << endl; - } - } - - if ((*f_iter)->get_returntype()->is_void()) { - indent(f_service_) << "return;" << endl; - } else { - f_service_ << indent() - << "throw new " - "TApplicationException(TApplicationException.ExceptionType.MissingResult, \"" - << (*f_iter)->get_name() << " failed: unknown result\");" << endl; - } - - cleanup_member_name_mapping((*f_iter)->get_xceptions()); - scope_down(f_service_); - f_service_ << endl; - } - } - - indent_down(); - indent(f_service_) << "}" << endl; -} - -void t_csharp_generator::generate_service_server(t_service* tservice) { - if (async_) { - generate_service_server_async(tservice); - generate_service_server_sync(tservice); - } - else { - generate_service_server_sync(tservice); - } -} - -void t_csharp_generator::generate_service_server_sync(t_service* tservice) { - vector functions = tservice->get_functions(); - vector::iterator f_iter; - - string extends = ""; - string extends_processor = ""; - if (tservice->get_extends() != NULL) { - extends = type_name(tservice->get_extends()); - extends_processor = extends + ".Processor, "; - } - - indent(f_service_) << "public class Processor : " << extends_processor << "TProcessor {" << endl; - indent_up(); - - indent(f_service_) << "public Processor(ISync iface)"; - - if (!extends.empty()) { - f_service_ << " : base(iface)"; - } - f_service_ << endl; - scope_up(f_service_); - f_service_ << indent() << "iface_ = iface;" << endl; - - for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) { - f_service_ << indent() << "processMap_[\"" << (*f_iter)->get_name() - << "\"] = " << (*f_iter)->get_name() << "_Process;" << endl; - } - - scope_down(f_service_); - f_service_ << endl; - - if (extends.empty()) { - f_service_ - << indent() - << "protected delegate void ProcessFunction(int seqid, TProtocol iprot, TProtocol oprot);" - << endl; - } - - f_service_ << indent() << "private ISync iface_;" << endl; - - if (extends.empty()) { - f_service_ << indent() << "protected Dictionary processMap_ = new " - "Dictionary();" << endl; - } - - f_service_ << endl; - - if (extends.empty()) { - indent(f_service_) << "public bool Process(TProtocol iprot, TProtocol oprot)" << endl; - } - else { - indent(f_service_) << "public new bool Process(TProtocol iprot, TProtocol oprot)" << endl; - } - scope_up(f_service_); - - f_service_ << indent() << "try" << endl; - scope_up(f_service_); - - f_service_ << indent() << "TMessage msg = iprot.ReadMessageBegin();" << endl; - - f_service_ - << indent() << "ProcessFunction fn;" << endl << indent() - << "processMap_.TryGetValue(msg.Name, out fn);" << endl << indent() << "if (fn == null) {" - << endl << indent() << " TProtocolUtil.Skip(iprot, TType.Struct);" << endl << indent() - << " iprot.ReadMessageEnd();" << endl << indent() - << " TApplicationException x = new TApplicationException " - "(TApplicationException.ExceptionType.UnknownMethod, \"Invalid method name: '\" + " - "msg.Name + \"'\");" << endl << indent() - << " oprot.WriteMessageBegin(new TMessage(msg.Name, TMessageType.Exception, msg.SeqID));" - << endl << indent() << " x.Write(oprot);" << endl << indent() << " oprot.WriteMessageEnd();" - << endl << indent() << " oprot.Transport.Flush();" << endl << indent() << " return true;" - << endl << indent() << "}" << endl << indent() << "fn(msg.SeqID, iprot, oprot);" << endl; - - scope_down(f_service_); - - f_service_ << indent() << "catch (IOException)" << endl; - scope_up(f_service_); - f_service_ << indent() << "return false;" << endl; - scope_down(f_service_); - - f_service_ << indent() << "return true;" << endl; - - scope_down(f_service_); - f_service_ << endl; - - for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) { - generate_process_function(tservice, *f_iter); - } - - indent_down(); - indent(f_service_) << "}" << endl << endl; -} - -void t_csharp_generator::generate_service_server_async(t_service* tservice) { - vector functions = tservice->get_functions(); - vector::iterator f_iter; - - string extends = ""; - string extends_processor = ""; - if (tservice->get_extends() != NULL) { - extends = type_name(tservice->get_extends()); - extends_processor = extends + ".AsyncProcessor, "; - } - - indent(f_service_) << "public class AsyncProcessor : " << extends_processor << "TAsyncProcessor {" << endl; - indent_up(); - - indent(f_service_) << "public AsyncProcessor(IAsync iface)"; - if (!extends.empty()) { - f_service_ << " : base(iface)"; - } - f_service_ << endl; - scope_up(f_service_); - f_service_ << indent() << "iface_ = iface;" << endl; - - for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) { - f_service_ << indent() << "processMap_[\"" << (*f_iter)->get_name() - << "\"] = " << (*f_iter)->get_name() << "_ProcessAsync;" << endl; - } - - scope_down(f_service_); - f_service_ << endl; - - if (extends.empty()) { - f_service_ - << indent() - << "protected delegate Task ProcessFunction(int seqid, TProtocol iprot, TProtocol oprot);" - << endl; - } - - f_service_ << indent() << "private IAsync iface_;" << endl; - - if (extends.empty()) { - f_service_ << indent() << "protected Dictionary processMap_ = new " - "Dictionary();" << endl; - } - - f_service_ << endl; - - if (extends.empty()) { - indent(f_service_) << "public async Task ProcessAsync(TProtocol iprot, TProtocol oprot)" << endl; - } - else { - indent(f_service_) << "public new async Task ProcessAsync(TProtocol iprot, TProtocol oprot)" << endl; - } - scope_up(f_service_); - - f_service_ << indent() << "try" << endl; - scope_up(f_service_); - - f_service_ << indent() << "TMessage msg = iprot.ReadMessageBegin();" << endl; - - f_service_ - << indent() << "ProcessFunction fn;" << endl << indent() - << "processMap_.TryGetValue(msg.Name, out fn);" << endl << indent() << "if (fn == null) {" - << endl << indent() << " TProtocolUtil.Skip(iprot, TType.Struct);" << endl << indent() - << " iprot.ReadMessageEnd();" << endl << indent() - << " TApplicationException x = new TApplicationException " - "(TApplicationException.ExceptionType.UnknownMethod, \"Invalid method name: '\" + " - "msg.Name + \"'\");" << endl << indent() - << " oprot.WriteMessageBegin(new TMessage(msg.Name, TMessageType.Exception, msg.SeqID));" - << endl << indent() << " x.Write(oprot);" << endl << indent() << " oprot.WriteMessageEnd();" - << endl << indent() << " oprot.Transport.Flush();" << endl << indent() << " return true;" - << endl << indent() << "}" << endl << indent() << "await fn(msg.SeqID, iprot, oprot);" << endl; - - scope_down(f_service_); - - f_service_ << indent() << "catch (IOException)" << endl; - scope_up(f_service_); - f_service_ << indent() << "return false;" << endl; - scope_down(f_service_); - - f_service_ << indent() << "return true;" << endl; - - scope_down(f_service_); - f_service_ << endl; - - for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) { - generate_process_function_async(tservice, *f_iter); - } - - indent_down(); - indent(f_service_) << "}" << endl << endl; -} - -void t_csharp_generator::generate_function_helpers(t_function* tfunction) { - if (tfunction->is_oneway()) { - return; - } - - t_struct result(program_, tfunction->get_name() + "_result"); - t_field success(tfunction->get_returntype(), "success", 0); - if (!tfunction->get_returntype()->is_void()) { - result.append(&success); - } - - t_struct* xs = tfunction->get_xceptions(); - const vector& fields = xs->get_members(); - vector::const_iterator f_iter; - for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { - result.append(*f_iter); - } - - generate_csharp_struct_definition(f_service_, &result, false, true, true); -} - -void t_csharp_generator::generate_process_function(t_service* tservice, t_function* tfunction) { - (void)tservice; - indent(f_service_) << "public void " << tfunction->get_name() - << "_Process(int seqid, TProtocol iprot, TProtocol oprot)" << endl; - scope_up(f_service_); - - string argsname = tfunction->get_name() + "_args"; - string resultname = tfunction->get_name() + "_result"; - - f_service_ << indent() << argsname << " args = new " << argsname << "();" << endl - << indent() << "args.Read(iprot);" << endl - << indent() << "iprot.ReadMessageEnd();" << endl; - - t_struct* xs = tfunction->get_xceptions(); - const std::vector& xceptions = xs->get_members(); - vector::const_iterator x_iter; - - if (!tfunction->is_oneway()) { - f_service_ << indent() << resultname << " result = new " << resultname << "();" << endl; - } - - f_service_ << indent() << "try" << endl - << indent() << "{" << endl; - indent_up(); - - if (xceptions.size() > 0) { - f_service_ << indent() << "try" << endl - << indent() << "{" << endl; - indent_up(); - } - - t_struct* arg_struct = tfunction->get_arglist(); - const std::vector& fields = arg_struct->get_members(); - vector::const_iterator f_iter; - - f_service_ << indent(); - if (!tfunction->is_oneway() && !tfunction->get_returntype()->is_void()) { - f_service_ << "result.Success = "; - } - f_service_ << "iface_." << normalize_name(tfunction->get_name()) << "("; - bool first = true; - prepare_member_name_mapping(arg_struct); - for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { - if (first) { - first = false; - } else { - f_service_ << ", "; - } - f_service_ << "args." << prop_name(*f_iter); - if (nullable_ && !type_can_be_null((*f_iter)->get_type())) { - f_service_ << ".Value"; - } - } - cleanup_member_name_mapping(arg_struct); - f_service_ << ");" << endl; - - prepare_member_name_mapping(xs, xs->get_members(), resultname); - if (xceptions.size() > 0) { - indent_down(); - f_service_ << indent() << "}" << endl; - for (x_iter = xceptions.begin(); x_iter != xceptions.end(); ++x_iter) { - f_service_ << indent() << "catch (" << type_name((*x_iter)->get_type(), false, false) << " " - << (*x_iter)->get_name() << ")" << endl - << indent() << "{" << endl; - if (!tfunction->is_oneway()) { - indent_up(); - f_service_ << indent() << "result." << prop_name(*x_iter) << " = " << (*x_iter)->get_name() - << ";" << endl; - indent_down(); - } - f_service_ << indent() << "}" << endl; - } - } - if (!tfunction->is_oneway()) { - f_service_ << indent() << "oprot.WriteMessageBegin(new TMessage(\"" << tfunction->get_name() - << "\", TMessageType.Reply, seqid)); " << endl; - f_service_ << indent() << "result.Write(oprot);" << endl; - } - indent_down(); - - cleanup_member_name_mapping(xs); - - f_service_ << indent() << "}" << endl - << indent() << "catch (TTransportException)" << endl - << indent() << "{" << endl - << indent() << " throw;" << endl - << indent() << "}" << endl - << indent() << "catch (Exception ex)" << endl - << indent() << "{" << endl - << indent() << " Console.Error.WriteLine(\"Error occurred in processor:\");" << endl - << indent() << " Console.Error.WriteLine(ex.ToString());" << endl; - - if (tfunction->is_oneway()) { - f_service_ << indent() << "}" << endl; - } else { - f_service_ << indent() << " TApplicationException x = new TApplicationException" << indent() - << "(TApplicationException.ExceptionType.InternalError,\" Internal error.\");" - << endl - << indent() << " oprot.WriteMessageBegin(new TMessage(\"" << tfunction->get_name() - << "\", TMessageType.Exception, seqid));" << endl - << indent() << " x.Write(oprot);" << endl - << indent() << "}" << endl; - f_service_ << indent() << "oprot.WriteMessageEnd();" << endl - << indent() << "oprot.Transport.Flush();" << endl; - } - - scope_down(f_service_); - - f_service_ << endl; -} - -void t_csharp_generator::generate_process_function_async(t_service* tservice, t_function* tfunction) { - (void)tservice; - indent(f_service_) << "public async Task " << tfunction->get_name() - << "_ProcessAsync(int seqid, TProtocol iprot, TProtocol oprot)" << endl; - scope_up(f_service_); - - string argsname = tfunction->get_name() + "_args"; - string resultname = tfunction->get_name() + "_result"; - - f_service_ << indent() << argsname << " args = new " << argsname << "();" << endl - << indent() << "args.Read(iprot);" << endl - << indent() << "iprot.ReadMessageEnd();" << endl; - - t_struct* xs = tfunction->get_xceptions(); - const std::vector& xceptions = xs->get_members(); - vector::const_iterator x_iter; - - if (!tfunction->is_oneway()) { - f_service_ << indent() << resultname << " result = new " << resultname << "();" << endl; - } - - f_service_ << indent() << "try" << endl - << indent() << "{" << endl; - indent_up(); - - if (xceptions.size() > 0) { - f_service_ << indent() << "try" << endl - << indent() << "{" << endl; - indent_up(); - } - - t_struct* arg_struct = tfunction->get_arglist(); - const std::vector& fields = arg_struct->get_members(); - vector::const_iterator f_iter; - - f_service_ << indent(); - if (!tfunction->is_oneway() && !tfunction->get_returntype()->is_void()) { - f_service_ << "result.Success = "; - } - f_service_ << "await iface_." << normalize_name(tfunction->get_name()) << "Async("; - bool first = true; - prepare_member_name_mapping(arg_struct); - for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { - if (first) { - first = false; - } - else { - f_service_ << ", "; - } - f_service_ << "args." << prop_name(*f_iter); - if (nullable_ && !type_can_be_null((*f_iter)->get_type())) { - f_service_ << ".Value"; - } - } - cleanup_member_name_mapping(arg_struct); - f_service_ << ");" << endl; - - prepare_member_name_mapping(xs, xs->get_members(), resultname); - if (xceptions.size() > 0) { - indent_down(); - f_service_ << indent() << "}" << endl; - for (x_iter = xceptions.begin(); x_iter != xceptions.end(); ++x_iter) { - f_service_ << indent() << "catch (" << type_name((*x_iter)->get_type(), false, false) << " " - << (*x_iter)->get_name() << ")" << endl - << indent() << "{" << endl; - if (!tfunction->is_oneway()) { - indent_up(); - f_service_ << indent() << "result." << prop_name(*x_iter) << " = " << (*x_iter)->get_name() - << ";" << endl; - indent_down(); - } - f_service_ << indent() << "}" << endl; - } - } - if (!tfunction->is_oneway()) { - f_service_ << indent() << "oprot.WriteMessageBegin(new TMessage(\"" << tfunction->get_name() - << "\", TMessageType.Reply, seqid)); " << endl; - f_service_ << indent() << "result.Write(oprot);" << endl; - } - indent_down(); - - cleanup_member_name_mapping(xs); - - f_service_ << indent() << "}" << endl - << indent() << "catch (TTransportException)" << endl - << indent() << "{" << endl - << indent() << " throw;" << endl - << indent() << "}" << endl - << indent() << "catch (Exception ex)" << endl - << indent() << "{" << endl - << indent() << " Console.Error.WriteLine(\"Error occurred in processor:\");" << endl - << indent() << " Console.Error.WriteLine(ex.ToString());" << endl; - - if (tfunction->is_oneway()) { - f_service_ << indent() << "}" << endl; - } - else { - f_service_ << indent() << " TApplicationException x = new TApplicationException" << indent() - << "(TApplicationException.ExceptionType.InternalError,\" Internal error.\");" - << endl - << indent() << " oprot.WriteMessageBegin(new TMessage(\"" << tfunction->get_name() - << "\", TMessageType.Exception, seqid));" << endl - << indent() << " x.Write(oprot);" << endl - << indent() << "}" << endl; - f_service_ << indent() << "oprot.WriteMessageEnd();" << endl - << indent() << "oprot.Transport.Flush();" << endl; - } - - scope_down(f_service_); - - f_service_ << endl; -} - -void t_csharp_generator::generate_csharp_union_reader(std::ostream& out, t_struct* tunion) { - // Thanks to THRIFT-1768, we don't need to check for required fields in the union - const vector& fields = tunion->get_members(); - vector::const_iterator f_iter; - - indent(out) << "public static " << tunion->get_name() << " Read(TProtocol iprot)" << endl; - scope_up(out); - - out << indent() << "iprot.IncrementRecursionDepth();" << endl; - out << indent() << "try" << endl; - scope_up(out); - - indent(out) << tunion->get_name() << " retval;" << endl; - indent(out) << "iprot.ReadStructBegin();" << endl; - indent(out) << "TField field = iprot.ReadFieldBegin();" << endl; - // we cannot have the first field be a stop -- we must have a single field defined - indent(out) << "if (field.Type == TType.Stop)" << endl; - scope_up(out); - indent(out) << "iprot.ReadFieldEnd();" << endl; - indent(out) << "retval = new ___undefined();" << endl; - scope_down(out); - indent(out) << "else" << endl; - scope_up(out); - indent(out) << "switch (field.ID)" << endl; - scope_up(out); - - for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { - indent(out) << "case " << (*f_iter)->get_key() << ":" << endl; - indent_up(); - indent(out) << "if (field.Type == " << type_to_enum((*f_iter)->get_type()) << ") {" << endl; - indent_up(); - - indent(out) << type_name((*f_iter)->get_type()) << " temp;" << endl; - generate_deserialize_field(out, (*f_iter), "temp", true); - indent(out) << "retval = new " << (*f_iter)->get_name() << "(temp);" << endl; - - indent_down(); - out << indent() << "} else { " << endl << indent() << " TProtocolUtil.Skip(iprot, field.Type);" - << endl << indent() << " retval = new ___undefined();" << endl << indent() << "}" << endl - << indent() << "break;" << endl; - indent_down(); - } - - indent(out) << "default: " << endl; - indent_up(); - indent(out) << "TProtocolUtil.Skip(iprot, field.Type);" << endl << indent() - << "retval = new ___undefined();" << endl; - indent(out) << "break;" << endl; - indent_down(); - - scope_down(out); - - indent(out) << "iprot.ReadFieldEnd();" << endl; - - indent(out) << "if (iprot.ReadFieldBegin().Type != TType.Stop)" << endl; - scope_up(out); - indent(out) << "throw new TProtocolException(TProtocolException.INVALID_DATA);" << endl; - scope_down(out); - - // end of else for TStop - scope_down(out); - indent(out) << "iprot.ReadStructEnd();" << endl; - indent(out) << "return retval;" << endl; - indent_down(); - - scope_down(out); - out << indent() << "finally" << endl; - scope_up(out); - out << indent() << "iprot.DecrementRecursionDepth();" << endl; - scope_down(out); - - indent(out) << "}" << endl << endl; -} - -void t_csharp_generator::generate_deserialize_field(ostream& out, - t_field* tfield, - string prefix, - bool is_propertyless) { - t_type* type = tfield->get_type(); - while (type->is_typedef()) { - type = ((t_typedef*)type)->get_type(); - } - - if (type->is_void()) { - throw "CANNOT GENERATE DESERIALIZE CODE FOR void TYPE: " + prefix + tfield->get_name(); - } - - string name = prefix + (is_propertyless ? "" : prop_name(tfield)); - - if (type->is_struct() || type->is_xception()) { - generate_deserialize_struct(out, (t_struct*)type, name); - } else if (type->is_container()) { - generate_deserialize_container(out, type, name); - } else if (type->is_base_type() || type->is_enum()) { - indent(out) << name << " = "; - - if (type->is_enum()) { - out << "(" << type_name(type, false, true) << ")"; - } - - out << "iprot."; - - if (type->is_base_type()) { - t_base_type::t_base tbase = ((t_base_type*)type)->get_base(); - switch (tbase) { - case t_base_type::TYPE_VOID: - throw "compiler error: cannot serialize void field in a struct: " + name; - break; - case t_base_type::TYPE_STRING: - if (type->is_binary()) { - out << "ReadBinary();"; - } else { - out << "ReadString();"; - } - break; - case t_base_type::TYPE_BOOL: - out << "ReadBool();"; - break; - case t_base_type::TYPE_I8: - out << "ReadByte();"; - break; - case t_base_type::TYPE_I16: - out << "ReadI16();"; - break; - case t_base_type::TYPE_I32: - out << "ReadI32();"; - break; - case t_base_type::TYPE_I64: - out << "ReadI64();"; - break; - case t_base_type::TYPE_DOUBLE: - out << "ReadDouble();"; - break; - default: - throw "compiler error: no C# name for base type " + t_base_type::t_base_name(tbase); - } - } else if (type->is_enum()) { - out << "ReadI32();"; - } - out << endl; - } else { - printf("DO NOT KNOW HOW TO DESERIALIZE FIELD '%s' TYPE '%s'\n", - tfield->get_name().c_str(), - type_name(type).c_str()); - } -} - -void t_csharp_generator::generate_deserialize_struct(ostream& out, - t_struct* tstruct, - string prefix) { - if (union_ && tstruct->is_union()) { - out << indent() << prefix << " = " << type_name(tstruct) << ".Read(iprot);" << endl; - } else { - out << indent() << prefix << " = new " << type_name(tstruct) << "();" << endl << indent() - << prefix << ".Read(iprot);" << endl; - } -} - -void t_csharp_generator::generate_deserialize_container(ostream& out, - t_type* ttype, - string prefix) { - scope_up(out); - - string obj; - - if (ttype->is_map()) { - obj = tmp("_map"); - } else if (ttype->is_set()) { - obj = tmp("_set"); - } else if (ttype->is_list()) { - obj = tmp("_list"); - } - - indent(out) << prefix << " = new " << type_name(ttype, false, true) << "();" << endl; - if (ttype->is_map()) { - out << indent() << "TMap " << obj << " = iprot.ReadMapBegin();" << endl; - } else if (ttype->is_set()) { - out << indent() << "TSet " << obj << " = iprot.ReadSetBegin();" << endl; - } else if (ttype->is_list()) { - out << indent() << "TList " << obj << " = iprot.ReadListBegin();" << endl; - } - - string i = tmp("_i"); - indent(out) << "for( int " << i << " = 0; " << i << " < " << obj << ".Count" - << "; " - << "++" << i << ")" << endl; - scope_up(out); - - if (ttype->is_map()) { - generate_deserialize_map_element(out, (t_map*)ttype, prefix); - } else if (ttype->is_set()) { - generate_deserialize_set_element(out, (t_set*)ttype, prefix); - } else if (ttype->is_list()) { - generate_deserialize_list_element(out, (t_list*)ttype, prefix); - } - - scope_down(out); - - if (ttype->is_map()) { - indent(out) << "iprot.ReadMapEnd();" << endl; - } else if (ttype->is_set()) { - indent(out) << "iprot.ReadSetEnd();" << endl; - } else if (ttype->is_list()) { - indent(out) << "iprot.ReadListEnd();" << endl; - } - - scope_down(out); -} - -void t_csharp_generator::generate_deserialize_map_element(ostream& out, - t_map* tmap, - string prefix) { - string key = tmp("_key"); - string val = tmp("_val"); - - t_field fkey(tmap->get_key_type(), key); - t_field fval(tmap->get_val_type(), val); - - indent(out) << declare_field(&fkey) << endl; - indent(out) << declare_field(&fval) << endl; - - generate_deserialize_field(out, &fkey); - generate_deserialize_field(out, &fval); - - indent(out) << prefix << "[" << key << "] = " << val << ";" << endl; -} - -void t_csharp_generator::generate_deserialize_set_element(ostream& out, - t_set* tset, - string prefix) { - string elem = tmp("_elem"); - t_field felem(tset->get_elem_type(), elem); - - indent(out) << declare_field(&felem) << endl; - - generate_deserialize_field(out, &felem); - - indent(out) << prefix << ".Add(" << elem << ");" << endl; -} - -void t_csharp_generator::generate_deserialize_list_element(ostream& out, - t_list* tlist, - string prefix) { - string elem = tmp("_elem"); - t_field felem(tlist->get_elem_type(), elem); - - indent(out) << declare_field(&felem) << endl; - - generate_deserialize_field(out, &felem); - - indent(out) << prefix << ".Add(" << elem << ");" << endl; -} - -void t_csharp_generator::generate_serialize_field(ostream& out, - t_field* tfield, - string prefix, - bool is_element, - bool is_propertyless) { - t_type* type = tfield->get_type(); - while (type->is_typedef()) { - type = ((t_typedef*)type)->get_type(); - } - - string name = prefix + (is_propertyless ? "" : prop_name(tfield)); - - if (type->is_void()) { - throw "CANNOT GENERATE SERIALIZE CODE FOR void TYPE: " + name; - } - - if (type->is_struct() || type->is_xception()) { - generate_serialize_struct(out, (t_struct*)type, name); - } else if (type->is_container()) { - generate_serialize_container(out, type, name); - } else if (type->is_base_type() || type->is_enum()) { - indent(out) << "oprot."; - - string nullable_name = nullable_ && !is_element && !field_is_required(tfield) ? name + ".Value" - : name; - - if (type->is_base_type()) { - t_base_type::t_base tbase = ((t_base_type*)type)->get_base(); - switch (tbase) { - case t_base_type::TYPE_VOID: - throw "compiler error: cannot serialize void field in a struct: " + name; - break; - case t_base_type::TYPE_STRING: - if (type->is_binary()) { - out << "WriteBinary("; - } else { - out << "WriteString("; - } - out << name << ");"; - break; - case t_base_type::TYPE_BOOL: - out << "WriteBool(" << nullable_name << ");"; - break; - case t_base_type::TYPE_I8: - out << "WriteByte(" << nullable_name << ");"; - break; - case t_base_type::TYPE_I16: - out << "WriteI16(" << nullable_name << ");"; - break; - case t_base_type::TYPE_I32: - out << "WriteI32(" << nullable_name << ");"; - break; - case t_base_type::TYPE_I64: - out << "WriteI64(" << nullable_name << ");"; - break; - case t_base_type::TYPE_DOUBLE: - out << "WriteDouble(" << nullable_name << ");"; - break; - default: - throw "compiler error: no C# name for base type " + t_base_type::t_base_name(tbase); - } - } else if (type->is_enum()) { - out << "WriteI32((int)" << nullable_name << ");"; - } - out << endl; - } else { - printf("DO NOT KNOW HOW TO SERIALIZE '%s%s' TYPE '%s'\n", - prefix.c_str(), - tfield->get_name().c_str(), - type_name(type).c_str()); - } -} - -void t_csharp_generator::generate_serialize_struct(ostream& out, - t_struct* tstruct, - string prefix) { - (void)tstruct; - out << indent() << prefix << ".Write(oprot);" << endl; -} - -void t_csharp_generator::generate_serialize_container(ostream& out, t_type* ttype, string prefix) { - scope_up(out); - - if (ttype->is_map()) { - indent(out) << "oprot.WriteMapBegin(new TMap(" << type_to_enum(((t_map*)ttype)->get_key_type()) - << ", " << type_to_enum(((t_map*)ttype)->get_val_type()) << ", " << prefix - << ".Count));" << endl; - } else if (ttype->is_set()) { - indent(out) << "oprot.WriteSetBegin(new TSet(" << type_to_enum(((t_set*)ttype)->get_elem_type()) - << ", " << prefix << ".Count));" << endl; - } else if (ttype->is_list()) { - indent(out) << "oprot.WriteListBegin(new TList(" - << type_to_enum(((t_list*)ttype)->get_elem_type()) << ", " << prefix << ".Count));" - << endl; - } - - string iter = tmp("_iter"); - if (ttype->is_map()) { - indent(out) << "foreach (" << type_name(((t_map*)ttype)->get_key_type()) << " " << iter - << " in " << prefix << ".Keys)"; - } else if (ttype->is_set()) { - indent(out) << "foreach (" << type_name(((t_set*)ttype)->get_elem_type()) << " " << iter - << " in " << prefix << ")"; - } else if (ttype->is_list()) { - indent(out) << "foreach (" << type_name(((t_list*)ttype)->get_elem_type()) << " " << iter - << " in " << prefix << ")"; - } - - out << endl; - scope_up(out); - - if (ttype->is_map()) { - generate_serialize_map_element(out, (t_map*)ttype, iter, prefix); - } else if (ttype->is_set()) { - generate_serialize_set_element(out, (t_set*)ttype, iter); - } else if (ttype->is_list()) { - generate_serialize_list_element(out, (t_list*)ttype, iter); - } - - scope_down(out); - - if (ttype->is_map()) { - indent(out) << "oprot.WriteMapEnd();" << endl; - } else if (ttype->is_set()) { - indent(out) << "oprot.WriteSetEnd();" << endl; - } else if (ttype->is_list()) { - indent(out) << "oprot.WriteListEnd();" << endl; - } - - scope_down(out); -} - -void t_csharp_generator::generate_serialize_map_element(ostream& out, - t_map* tmap, - string iter, - string map) { - t_field kfield(tmap->get_key_type(), iter); - generate_serialize_field(out, &kfield, "", true); - t_field vfield(tmap->get_val_type(), map + "[" + iter + "]"); - generate_serialize_field(out, &vfield, "", true); -} - -void t_csharp_generator::generate_serialize_set_element(ostream& out, t_set* tset, string iter) { - t_field efield(tset->get_elem_type(), iter); - generate_serialize_field(out, &efield, "", true); -} - -void t_csharp_generator::generate_serialize_list_element(ostream& out, - t_list* tlist, - string iter) { - t_field efield(tlist->get_elem_type(), iter); - generate_serialize_field(out, &efield, "", true); -} - -void t_csharp_generator::generate_property(ostream& out, - t_field* tfield, - bool isPublic, - bool generateIsset) { - generate_csharp_property(out, tfield, isPublic, generateIsset, "_"); -} -void t_csharp_generator::generate_csharp_property(ostream& out, - t_field* tfield, - bool isPublic, - bool generateIsset, - std::string fieldPrefix) { - if ((serialize_ || wcf_) && isPublic) { - indent(out) << "[DataMember(Order = 0)]" << endl; - } - bool has_default = field_has_default(tfield); - bool is_required = field_is_required(tfield); - if ((nullable_ && !has_default) || (is_required)) { - indent(out) << (isPublic ? "public " : "private ") - << type_name(tfield->get_type(), false, false, true, is_required) << " " - << prop_name(tfield) << " { get; set; }" << endl; - } else { - indent(out) << (isPublic ? "public " : "private ") - << type_name(tfield->get_type(), false, false, true) << " " << prop_name(tfield) - << endl; - scope_up(out); - indent(out) << "get" << endl; - scope_up(out); - bool use_nullable = false; - if (nullable_) { - t_type* ttype = tfield->get_type(); - while (ttype->is_typedef()) { - ttype = ((t_typedef*)ttype)->get_type(); - } - if (ttype->is_base_type()) { - use_nullable = ((t_base_type*)ttype)->get_base() != t_base_type::TYPE_STRING; - } else if (ttype->is_enum()) { - use_nullable = true; - } - } - indent(out) << "return " << fieldPrefix + tfield->get_name() << ";" << endl; - scope_down(out); - indent(out) << "set" << endl; - scope_up(out); - if (use_nullable) { - if (generateIsset) { - indent(out) << "__isset." << normalize_name(tfield->get_name()) << " = value.HasValue;" - << endl; - } - indent(out) << "if (value.HasValue) this." << fieldPrefix + tfield->get_name() - << " = value.Value;" << endl; - } else { - if (generateIsset) { - indent(out) << "__isset." << normalize_name(tfield->get_name()) << " = true;" << endl; - } - indent(out) << "this." << fieldPrefix + tfield->get_name() << " = value;" << endl; - } - scope_down(out); - scope_down(out); - } - out << endl; -} - -std::string t_csharp_generator::make_valid_csharp_identifier(std::string const& fromName) { - std::string str = fromName; - if (str.empty()) { - return str; - } - - // tests rely on this - assert(('A' < 'Z') && ('a' < 'z') && ('0' < '9')); - - // if the first letter is a number, we add an additional underscore in front of it - char c = str.at(0); - if (('0' <= c) && (c <= '9')) { - str = "_" + str; - } - - // following chars: letter, number or underscore - for (size_t i = 0; i < str.size(); ++i) { - c = str.at(i); - if ((('A' > c) || (c > 'Z')) && (('a' > c) || (c > 'z')) && (('0' > c) || (c > '9')) - && ('_' != c)) { - str.replace(i, 1, "_"); - } - } - - return str; -} - -void t_csharp_generator::cleanup_member_name_mapping(void* scope) { - if( member_mapping_scopes.empty()) { - throw "internal error: cleanup_member_name_mapping() no scope active"; - } - - member_mapping_scope& active = member_mapping_scopes.back(); - if (active.scope_member != scope) { - throw "internal error: cleanup_member_name_mapping() called for wrong struct"; - } - - member_mapping_scopes.pop_back(); -} - -string t_csharp_generator::get_mapped_member_name(string name) { - if( ! member_mapping_scopes.empty()) { - member_mapping_scope& active = member_mapping_scopes.back(); - map::iterator iter = active.mapping_table.find(name); - if (active.mapping_table.end() != iter) { - return iter->second; - } - } - - pverbose("no mapping for member %s\n", name.c_str()); - return name; -} - -void t_csharp_generator::prepare_member_name_mapping(t_struct* tstruct) { - prepare_member_name_mapping(tstruct, tstruct->get_members(), tstruct->get_name()); -} - -void t_csharp_generator::prepare_member_name_mapping(void* scope, - const vector& members, - const string& structname) { - // begin new scope - member_mapping_scope dummy; - dummy.scope_member = 0; - member_mapping_scopes.push_back(dummy); - member_mapping_scope& active = member_mapping_scopes.back(); - active.scope_member = scope; - - // current C# generator policy: - // - prop names are always rendered with an Uppercase first letter - // - struct names are used as given - std::set used_member_names; - vector::const_iterator iter; - - // prevent name conflicts with struct (CS0542 error) - used_member_names.insert(structname); - - // prevent name conflicts with known methods (THRIFT-2942) - used_member_names.insert("Read"); - used_member_names.insert("Write"); - - for (iter = members.begin(); iter != members.end(); ++iter) { - string oldname = (*iter)->get_name(); - string newname = prop_name(*iter, true); - while (true) { - - // new name conflicts with another member - if (used_member_names.find(newname) != used_member_names.end()) { - pverbose("struct %s: member %s conflicts with another member\n", - structname.c_str(), - newname.c_str()); - newname += '_'; - continue; - } - - // add always, this helps us to detect edge cases like - // different spellings ("foo" and "Foo") within the same struct - pverbose("struct %s: member mapping %s => %s\n", - structname.c_str(), - oldname.c_str(), - newname.c_str()); - active.mapping_table[oldname] = newname; - used_member_names.insert(newname); - break; - } - } -} - -std::string t_csharp_generator::prop_name(t_field* tfield, bool suppress_mapping) { - string name(tfield->get_name()); - if (suppress_mapping) { - name[0] = toupper(name[0]); - } else { - name = get_mapped_member_name(name); - } - return name; -} - -string t_csharp_generator::type_name(t_type* ttype, - bool in_container, - bool in_init, - bool in_param, - bool is_required) { - (void)in_init; - while (ttype->is_typedef()) { - ttype = ((t_typedef*)ttype)->get_type(); - } - - if (ttype->is_base_type()) { - return base_type_name((t_base_type*)ttype, in_container, in_param, is_required); - } else if (ttype->is_map()) { - t_map* tmap = (t_map*)ttype; - return "Dictionary<" + type_name(tmap->get_key_type(), true) + ", " - + type_name(tmap->get_val_type(), true) + ">"; - } else if (ttype->is_set()) { - t_set* tset = (t_set*)ttype; - return "THashSet<" + type_name(tset->get_elem_type(), true) + ">"; - } else if (ttype->is_list()) { - t_list* tlist = (t_list*)ttype; - return "List<" + type_name(tlist->get_elem_type(), true) + ">"; - } - - t_program* program = ttype->get_program(); - string postfix = (!is_required && nullable_ && in_param && ttype->is_enum()) ? "?" : ""; - if (program != NULL && program != program_) { - string ns = program->get_namespace("csharp"); - if (!ns.empty()) { - return ns + "." + normalize_name(ttype->get_name()) + postfix; - } - } - - return normalize_name(ttype->get_name()) + postfix; -} - -string t_csharp_generator::base_type_name(t_base_type* tbase, - bool in_container, - bool in_param, - bool is_required) { - (void)in_container; - string postfix = (!is_required && nullable_ && in_param) ? "?" : ""; - switch (tbase->get_base()) { - case t_base_type::TYPE_VOID: - return "void"; - case t_base_type::TYPE_STRING: - if (tbase->is_binary()) { - return "byte[]"; - } else { - return "string"; - } - case t_base_type::TYPE_BOOL: - return "bool" + postfix; - case t_base_type::TYPE_I8: - return "sbyte" + postfix; - case t_base_type::TYPE_I16: - return "short" + postfix; - case t_base_type::TYPE_I32: - return "int" + postfix; - case t_base_type::TYPE_I64: - return "long" + postfix; - case t_base_type::TYPE_DOUBLE: - return "double" + postfix; - default: - throw "compiler error: no C# name for base type " + t_base_type::t_base_name(tbase->get_base()); - } -} - -string t_csharp_generator::declare_field(t_field* tfield, bool init, std::string prefix) { - string result = type_name(tfield->get_type()) + " " + prefix + tfield->get_name(); - if (init) { - t_type* ttype = tfield->get_type(); - while (ttype->is_typedef()) { - ttype = ((t_typedef*)ttype)->get_type(); - } - if (ttype->is_base_type() && field_has_default(tfield)) { - std::ofstream dummy; - result += " = " + render_const_value(dummy, tfield->get_name(), ttype, tfield->get_value()); - } else if (ttype->is_base_type()) { - t_base_type::t_base tbase = ((t_base_type*)ttype)->get_base(); - switch (tbase) { - case t_base_type::TYPE_VOID: - throw "NO T_VOID CONSTRUCT"; - case t_base_type::TYPE_STRING: - result += " = null"; - break; - case t_base_type::TYPE_BOOL: - result += " = false"; - break; - case t_base_type::TYPE_I8: - case t_base_type::TYPE_I16: - case t_base_type::TYPE_I32: - case t_base_type::TYPE_I64: - result += " = 0"; - break; - case t_base_type::TYPE_DOUBLE: - result += " = (double)0"; - break; - } - } else if (ttype->is_enum()) { - result += " = (" + type_name(ttype, false, true) + ")0"; - } else if (ttype->is_container()) { - result += " = new " + type_name(ttype, false, true) + "()"; - } else { - result += " = new " + type_name(ttype, false, true) + "()"; - } - } - return result + ";"; -} - -string t_csharp_generator::function_signature(t_function* tfunction, string prefix) { - t_type* ttype = tfunction->get_returntype(); - return type_name(ttype) + " " + normalize_name(prefix + tfunction->get_name()) + "(" - + argument_list(tfunction->get_arglist()) + ")"; -} - -string t_csharp_generator::function_signature_async_begin(t_function* tfunction, string prefix) { - string comma = (tfunction->get_arglist()->get_members().size() > 0 ? ", " : ""); - return "IAsyncResult " + normalize_name(prefix + tfunction->get_name()) - + "(AsyncCallback callback, object state" + comma + argument_list(tfunction->get_arglist()) - + ")"; -} - -string t_csharp_generator::function_signature_async_end(t_function* tfunction, string prefix) { - t_type* ttype = tfunction->get_returntype(); - return type_name(ttype) + " " + normalize_name(prefix + tfunction->get_name()) - + "(IAsyncResult asyncResult)"; -} - -string t_csharp_generator::function_signature_async(t_function* tfunction, string prefix) { - t_type* ttype = tfunction->get_returntype(); - string task = "Task"; - if (!ttype->is_void()) - task += "<" + type_name(ttype) + ">"; - return task + " " + normalize_name(prefix + tfunction->get_name()) + "Async(" - + argument_list(tfunction->get_arglist()) + ")"; -} - -string t_csharp_generator::argument_list(t_struct* tstruct) { - string result = ""; - const vector& fields = tstruct->get_members(); - vector::const_iterator f_iter; - bool first = true; - for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { - if (first) { - first = false; - } else { - result += ", "; - } - result += type_name((*f_iter)->get_type()) + " " + normalize_name((*f_iter)->get_name()); - } - return result; -} - -string t_csharp_generator::type_to_enum(t_type* type) { - while (type->is_typedef()) { - type = ((t_typedef*)type)->get_type(); - } - - if (type->is_base_type()) { - t_base_type::t_base tbase = ((t_base_type*)type)->get_base(); - switch (tbase) { - case t_base_type::TYPE_VOID: - throw "NO T_VOID CONSTRUCT"; - case t_base_type::TYPE_STRING: - return "TType.String"; - case t_base_type::TYPE_BOOL: - return "TType.Bool"; - case t_base_type::TYPE_I8: - return "TType.Byte"; - case t_base_type::TYPE_I16: - return "TType.I16"; - case t_base_type::TYPE_I32: - return "TType.I32"; - case t_base_type::TYPE_I64: - return "TType.I64"; - case t_base_type::TYPE_DOUBLE: - return "TType.Double"; - } - } else if (type->is_enum()) { - return "TType.I32"; - } else if (type->is_struct() || type->is_xception()) { - return "TType.Struct"; - } else if (type->is_map()) { - return "TType.Map"; - } else if (type->is_set()) { - return "TType.Set"; - } else if (type->is_list()) { - return "TType.List"; - } - - throw "INVALID TYPE IN type_to_enum: " + type->get_name(); -} - -void t_csharp_generator::generate_csharp_docstring_comment(ostream& out, string contents) { - generate_docstring_comment(out, "/// \n", "/// ", contents, "/// \n"); -} - -void t_csharp_generator::generate_csharp_doc(ostream& out, t_field* field) { - if (field->get_type()->is_enum()) { - string combined_message = field->get_doc() + "\nget_type()) + "\"/>"; - generate_csharp_docstring_comment(out, combined_message); - } else { - generate_csharp_doc(out, (t_doc*)field); - } -} - -void t_csharp_generator::generate_csharp_doc(ostream& out, t_doc* tdoc) { - if (tdoc->has_doc()) { - generate_csharp_docstring_comment(out, tdoc->get_doc()); - } -} - -void t_csharp_generator::generate_csharp_doc(ostream& out, t_function* tfunction) { - if (tfunction->has_doc()) { - stringstream ps; - const vector& fields = tfunction->get_arglist()->get_members(); - vector::const_iterator p_iter; - for (p_iter = fields.begin(); p_iter != fields.end(); ++p_iter) { - t_field* p = *p_iter; - ps << "\nget_name() << "\">"; - if (p->has_doc()) { - std::string str = p->get_doc(); - str.erase(std::remove(str.begin(), str.end(), '\n'), - str.end()); // remove the newlines that appear from the parser - ps << str; - } - ps << ""; - } - generate_docstring_comment(out, - "", - "/// ", - "\n" + tfunction->get_doc() + "" + ps.str(), - ""); - } -} - -std::string t_csharp_generator::get_enum_class_name(t_type* type) { - string package = ""; - t_program* program = type->get_program(); - if (program != NULL && program != program_) { - package = program->get_namespace("csharp") + "."; - } - return package + type->get_name(); -} - - -THRIFT_REGISTER_GENERATOR( - csharp, - "C#", - " async: Adds Async support using Task.Run.\n" - " wcf: Adds bindings for WCF to generated classes.\n" - " serial: Add serialization support to generated classes.\n" - " nullable: Use nullable types for properties.\n" - " hashcode: Generate a hashcode and equals implementation for classes.\n" - " union: Use new union typing, which includes a static read function for union " - "types.\n") diff --git a/compiler/cpp/src/thrift/generate/t_d_generator.cc b/compiler/cpp/src/thrift/generate/t_d_generator.cc index df56cfc03d5..afae5b53dc0 100644 --- a/compiler/cpp/src/thrift/generate/t_d_generator.cc +++ b/compiler/cpp/src/thrift/generate/t_d_generator.cc @@ -71,7 +71,15 @@ class t_d_generator : public t_oop_generator { } protected: - virtual void init_generator() { + + // D reserved words are suffixed with an underscore + static string suffix_if_reserved(const string& name) { + const bool isIn = std::binary_search(std::begin(d_reserved_words), std::end(d_reserved_words), name); + string ret = isIn ? name + "_" : name; + return ret; + } + + void init_generator() override { // Make output directory MKDIR(get_out_dir().c_str()); @@ -102,20 +110,20 @@ class t_d_generator : public t_oop_generator { // Include type modules from other imported programs. const vector& includes = program_->get_includes(); - for (size_t i = 0; i < includes.size(); ++i) { - f_types_ << "public import " << render_package(*(includes[i])) << includes[i]->get_name() + for (auto include : includes) { + f_types_ << "public import " << render_package(*include) << include->get_name() << "_types;" << endl; } if (!includes.empty()) f_types_ << endl; } - virtual void close_generator() { + void close_generator() override { // Close output file f_types_.close(); } - virtual void generate_consts(std::vector consts) { + void generate_consts(std::vector consts) override { if (!consts.empty()) { string f_consts_name = package_dir_ + program_name_ + "_constants.d"; ofstream_with_content_based_conditional_update f_consts; @@ -132,12 +140,12 @@ class t_d_generator : public t_oop_generator { vector::iterator c_iter; for (c_iter = consts.begin(); c_iter != consts.end(); ++c_iter) { this->emit_doc(*c_iter, f_consts); - string name = (*c_iter)->get_name(); + string name = suffix_if_reserved((*c_iter)->get_name()); t_type* type = (*c_iter)->get_type(); indent(f_consts) << "immutable(" << render_type_name(type) << ") " << name << ";" << endl; } - f_consts << endl << "static this() {" << endl; + f_consts << endl << "shared static this() {" << endl; indent_up(); bool first = true; @@ -148,7 +156,7 @@ class t_d_generator : public t_oop_generator { f_consts << endl; } t_type* type = (*c_iter)->get_type(); - indent(f_consts) << (*c_iter)->get_name() << " = "; + indent(f_consts) << suffix_if_reserved((*c_iter)->get_name()) << " = "; if (!is_immutable_type(type)) { f_consts << "cast(immutable(" << render_type_name(type) << ")) "; } @@ -159,17 +167,17 @@ class t_d_generator : public t_oop_generator { } } - virtual void generate_typedef(t_typedef* ttypedef) { + void generate_typedef(t_typedef* ttypedef) override { this->emit_doc(ttypedef, f_types_); f_types_ << indent() << "alias " << render_type_name(ttypedef->get_type()) << " " << ttypedef->get_symbolic() << ";" << endl << endl; } - virtual void generate_enum(t_enum* tenum) { + void generate_enum(t_enum* tenum) override { vector constants = tenum->get_constants(); this->emit_doc(tenum, f_types_); - string enum_name = tenum->get_name(); + string enum_name = suffix_if_reserved(tenum->get_name()); f_types_ << indent() << "enum " << enum_name << " {" << endl; indent_up(); @@ -177,7 +185,7 @@ class t_d_generator : public t_oop_generator { vector::const_iterator c_iter; for (c_iter = constants.begin(); c_iter != constants.end(); ++c_iter) { this->emit_doc(*c_iter, f_types_); - indent(f_types_) << (*c_iter)->get_name(); + indent(f_types_) << suffix_if_reserved((*c_iter)->get_name()); f_types_ << " = " << (*c_iter)->get_value() << ","; } @@ -188,39 +196,39 @@ class t_d_generator : public t_oop_generator { f_types_ << endl; } - virtual void generate_struct(t_struct* tstruct) { + void generate_struct(t_struct* tstruct) override { print_struct_definition(f_types_, tstruct, false); } - virtual void generate_xception(t_struct* txception) { + void generate_xception(t_struct* txception) override { print_struct_definition(f_types_, txception, true); } - virtual void generate_service(t_service* tservice) { - string svc_name = tservice->get_name(); + void generate_service(t_service* tservice) override { + string svc_name = suffix_if_reserved(tservice->get_name()); // Service implementation file includes string f_servicename = package_dir_ + svc_name + ".d"; ofstream_with_content_based_conditional_update f_service; f_service.open(f_servicename.c_str()); - f_service << autogen_comment() << "module " << render_package(*program_) << svc_name << ";" + f_service << autogen_comment() << "module " << suffix_if_reserved(render_package(*program_)) << svc_name << ";" << endl << endl; print_default_imports(f_service); - f_service << "import " << render_package(*get_program()) << program_name_ << "_types;" << endl; + f_service << "import " << suffix_if_reserved(render_package(*get_program())) << program_name_ << "_types;" << endl; t_service* extends_service = tservice->get_extends(); - if (extends_service != NULL) { - f_service << "import " << render_package(*(extends_service->get_program())) - << extends_service->get_name() << ";" << endl; + if (extends_service != nullptr) { + f_service << "import " << suffix_if_reserved(render_package(*(extends_service->get_program()))) + << suffix_if_reserved(extends_service->get_name()) << ";" << endl; } f_service << endl; string extends = ""; - if (tservice->get_extends() != NULL) { - extends = " : " + render_type_name(tservice->get_extends()); + if (tservice->get_extends() != nullptr) { + extends = " : " + suffix_if_reserved(render_type_name(tservice->get_extends())); } this->emit_doc(tservice, f_service); @@ -274,7 +282,7 @@ class t_d_generator : public t_oop_generator { meta << ","; } - meta << endl << indent() << "TMethodMeta(`" << (*fn_iter)->get_name() << "`, " << endl; + meta << endl << indent() << "TMethodMeta(`" << suffix_if_reserved((*fn_iter)->get_name()) << "`, " << endl; indent_up(); indent(meta) << "["; @@ -288,10 +296,10 @@ class t_d_generator : public t_oop_generator { meta << ", "; } - meta << "TParamMeta(`" << (*p_iter)->get_name() << "`, " << (*p_iter)->get_key(); + meta << "TParamMeta(`" << suffix_if_reserved((*p_iter)->get_name()) << "`, " << (*p_iter)->get_key(); t_const_value* cv = (*p_iter)->get_value(); - if (cv != NULL) { + if (cv != nullptr) { meta << ", q{" << render_const_value((*p_iter)->get_type(), cv) << "}"; } meta << ")"; @@ -312,8 +320,8 @@ class t_d_generator : public t_oop_generator { meta << ", "; } - meta << "TExceptionMeta(`" << (*ex_iter)->get_name() << "`, " << (*ex_iter)->get_key() - << ", `" << (*ex_iter)->get_type()->get_name() << "`)"; + meta << "TExceptionMeta(`" << suffix_if_reserved((*ex_iter)->get_name()) << "`, " + << (*ex_iter)->get_key() << ", `" << (*ex_iter)->get_type()->get_name() << "`)"; } meta << "]"; @@ -364,7 +372,7 @@ class t_d_generator : public t_oop_generator { * Writes a server skeleton for the passed service to out. */ void print_server_skeleton(ostream& out, t_service* tservice) { - string svc_name = tservice->get_name(); + string svc_name = suffix_if_reserved(tservice->get_name()); out << "/*" << endl << " * This auto-generated skeleton file illustrates how to build a server. If you" << endl @@ -393,7 +401,7 @@ class t_d_generator : public t_oop_generator { indent_up(); out << indent() << "// Your implementation goes here." << endl << indent() << "writeln(\"" - << (*f_iter)->get_name() << " called\");" << endl; + << suffix_if_reserved((*f_iter)->get_name()) << " called\");" << endl; t_type* rt = (*f_iter)->get_returntype(); if (!rt->is_void()) { @@ -428,16 +436,16 @@ class t_d_generator : public t_oop_generator { const vector& members = tstruct->get_members(); if (is_exception) { - indent(out) << "class " << tstruct->get_name() << " : TException {" << endl; + indent(out) << "class " << suffix_if_reserved(tstruct->get_name()) << " : TException {" << endl; } else { - indent(out) << "struct " << tstruct->get_name() << " {" << endl; + indent(out) << "struct " << suffix_if_reserved(tstruct->get_name()) << " {" << endl; } indent_up(); // Declare all fields. vector::const_iterator m_iter; for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) { - indent(out) << render_type_name((*m_iter)->get_type()) << " " << (*m_iter)->get_name() << ";" + indent(out) << render_type_name((*m_iter)->get_type()) << " " << suffix_if_reserved((*m_iter)->get_name()) << ";" << endl; } @@ -462,12 +470,12 @@ class t_d_generator : public t_oop_generator { } out << endl; - indent(out) << "TFieldMeta(`" << (*m_iter)->get_name() << "`, " << (*m_iter)->get_key(); + indent(out) << "TFieldMeta(`" << suffix_if_reserved((*m_iter)->get_name()) << "`, " << (*m_iter)->get_key(); t_const_value* cv = (*m_iter)->get_value(); t_field::e_req req = (*m_iter)->get_req(); out << ", " << render_req(req); - if (cv != NULL) { + if (cv != nullptr) { out << ", q{" << render_const_value((*m_iter)->get_type(), cv) << "}"; } out << ")"; @@ -488,7 +496,7 @@ class t_d_generator : public t_oop_generator { * method. */ void print_function_signature(ostream& out, t_function* fn) { - out << render_type_name(fn->get_returntype()) << " " << fn->get_name() << "("; + out << render_type_name(fn->get_returntype()) << " " << suffix_if_reserved(fn->get_name()) << "("; const vector& fields = fn->get_arglist()->get_members(); vector::const_iterator f_iter; @@ -499,7 +507,7 @@ class t_d_generator : public t_oop_generator { } else { out << ", "; } - out << render_type_name((*f_iter)->get_type(), true) << " " << (*f_iter)->get_name(); + out << render_type_name((*f_iter)->get_type(), true) << " " << suffix_if_reserved((*f_iter)->get_name()); } out << ")"; @@ -560,13 +568,13 @@ class t_d_generator : public t_oop_generator { const map& val = value->get_map(); map::const_iterator v_iter; for (v_iter = val.begin(); v_iter != val.end(); ++v_iter) { - t_type* field_type = NULL; + t_type* field_type = nullptr; for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { if ((*f_iter)->get_name() == v_iter->first->get_string()) { field_type = (*f_iter)->get_type(); } } - if (field_type == NULL) { + if (field_type == nullptr) { throw "Type error: " + type->get_name() + " has no field " + v_iter->first->get_string(); } @@ -737,6 +745,30 @@ class t_d_generator : public t_oop_generator { ofstream_with_content_based_conditional_update f_header_; string package_dir_; + + protected: + static vector d_reserved_words; + +}; + +vector t_d_generator::d_reserved_words = { + // The keywords are extracted from https://dlang.org/spec/lex.html + // and sorted for use with std::binary_search + "__FILE_FULL_PATH__", "__FILE__", "__FUNCTION__", "__LINE__", "__MODULE__", + "__PRETTY_FUNCTION__", "__gshared", "__parameters", "__traits", "__vector", + "abstract", "alias", "align", "asm", "assert", "auto", "body", "bool", + "break", "byte", "case", "cast", "catch", "cdouble", "cent", "cfloat", + "char", "class", "const", "continue", "creal", "dchar", "debug", "default", + "delegate", "delete", "deprecated", "do", "double", "else", "enum", + "export", "extern", "false", "final", "finally", "float", "for", "foreach", + "foreach_reverse", "function", "goto", "idouble", "if", "ifloat", "immutable", + "import", "in", "inout", "int", "interface", "invariant", "ireal", "is", + "lazy", "long", "macro ", "mixin", "module", "new", "nothrow", "null", "out", + "override", "package", "pragma", "private", "protected", "public", "pure", + "real", "ref", "return", "scope", "shared", "short", "static", "struct", + "super", "switch", "synchronized", "template", "this", "throw", "true", "try", + "typeid", "typeof", "ubyte", "ucent", "uint", "ulong", "union", "unittest", + "ushort", "version", "void", "wchar", "while", "with" }; THRIFT_REGISTER_GENERATOR(d, "D", "") diff --git a/compiler/cpp/src/thrift/generate/t_dart_generator.cc b/compiler/cpp/src/thrift/generate/t_dart_generator.cc index 414c333b4b7..65d0f535f34 100644 --- a/compiler/cpp/src/thrift/generate/t_dart_generator.cc +++ b/compiler/cpp/src/thrift/generate/t_dart_generator.cc @@ -121,25 +121,25 @@ class t_dart_generator : public t_oop_generator { * Init and close methods */ - void init_generator(); - void close_generator(); + void init_generator() override; + void close_generator() override; void export_class_to_library(string file_name, string class_name); void generate_dart_library(); void generate_dart_pubspec(); - void generate_consts(std::vector consts); + void generate_consts(std::vector consts) override; /** * Program-level generation functions */ - void generate_typedef(t_typedef* ttypedef); - void generate_enum(t_enum* tenum); - void generate_struct(t_struct* tstruct); - void generate_xception(t_struct* txception); - void generate_service(t_service* tservice); + void generate_typedef(t_typedef* ttypedef) override; + void generate_enum(t_enum* tenum) override; + void generate_struct(t_struct* tstruct) override; + void generate_xception(t_struct* txception) override; + void generate_service(t_service* tservice) override; void print_const_value(std::ostream& out, std::string name, @@ -359,8 +359,8 @@ string t_dart_generator::dart_thrift_imports() { // add imports for included thrift files const vector& includes = program_->get_includes(); - for (size_t i = 0; i < includes.size(); ++i) { - string include_name = find_library_name(includes[i]); + for (auto include : includes) { + string include_name = find_library_name(include); string named_import = "t_" + include_name; if (package_prefix_.empty()) { imports += "import 'package:" + include_name + "/" + include_name + ".dart' as " + named_import + ";" + endl; @@ -423,7 +423,7 @@ void t_dart_generator::generate_dart_pubspec() { indent(f_pubspec) << "environment:" << endl; indent_up(); - indent(f_pubspec) << "sdk: ^1.12.0" << endl; + indent(f_pubspec) << "sdk: '>=1.24.3 <3.0.0'" << endl; indent_down(); f_pubspec << endl; @@ -438,15 +438,15 @@ void t_dart_generator::generate_dart_pubspec() { indent_down(); } else { const vector lines = split(pubspec_lib_, '|'); - for (size_t line_index = 0; line_index < lines.size(); line_index++) { - indent(f_pubspec) << lines[line_index] << endl; + for (const auto & line : lines) { + indent(f_pubspec) << line << endl; } } // add included thrift files as dependencies const vector& includes = program_->get_includes(); - for (size_t i = 0; i < includes.size(); ++i) { - string include_name = find_library_name(includes[i]); + for (auto include : includes) { + string include_name = find_library_name(include); indent(f_pubspec) << include_name << ":" << endl; indent_up(); indent(f_pubspec) << "path: ../" << include_name << endl; @@ -599,13 +599,13 @@ void t_dart_generator::print_const_value(std::ostream& out, out << type_name(type) << " " << name << " = new " << type_name(type) << "()"; indent_up(); for (v_iter = val.begin(); v_iter != val.end(); ++v_iter) { - t_type* field_type = NULL; + t_type* field_type = nullptr; for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { if ((*f_iter)->get_name() == v_iter->first->get_string()) { field_type = (*f_iter)->get_type(); } } - if (field_type == NULL) { + if (field_type == nullptr) { throw "type error: " + type->get_name() + " has no field " + v_iter->first->get_string(); } string val = render_const_value(out, name, field_type, v_iter->second); @@ -827,7 +827,7 @@ void t_dart_generator::generate_dart_struct_definition(ostream& out, scope_up(out); for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) { t_type* t = get_true_type((*m_iter)->get_type()); - if ((*m_iter)->get_value() != NULL) { + if ((*m_iter)->get_value() != nullptr) { print_const_value(out, "this." + get_member_name((*m_iter)->get_name()), t, @@ -1409,7 +1409,7 @@ void t_dart_generator::generate_service(t_service* tservice) { */ void t_dart_generator::generate_service_interface(t_service* tservice) { string extends_iface = ""; - if (tservice->get_extends() != NULL) { + if (tservice->get_extends() != nullptr) { extends_iface = " extends " + get_ttype_class_name(tservice->get_extends()); } @@ -1454,7 +1454,7 @@ void t_dart_generator::generate_service_helpers(t_service* tservice) { void t_dart_generator::generate_service_client(t_service* tservice) { string extends = ""; string extends_client = ""; - if (tservice->get_extends() != NULL) { + if (tservice->get_extends() != nullptr) { extends = get_ttype_class_name(tservice->get_extends()); extends_client = " extends " + extends + "Client"; } @@ -1587,7 +1587,7 @@ void t_dart_generator::generate_service_server(t_service* tservice) { // Extends stuff string extends = ""; string extends_processor = ""; - if (tservice->get_extends() != NULL) { + if (tservice->get_extends() != nullptr) { extends = get_ttype_class_name(tservice->get_extends()); extends_processor = " extends " + extends + "Processor"; } @@ -2193,7 +2193,7 @@ string t_dart_generator::declare_field(t_field* tfield, bool init) { string result = type_name(tfield->get_type()) + " " + field_name; if (init) { t_type* ttype = get_true_type(tfield->get_type()); - if (ttype->is_base_type() && tfield->get_value() != NULL) { + if (ttype->is_base_type() && tfield->get_value() != nullptr) { std:: ofstream dummy; result += " = " + render_const_value(dummy, field_name, ttype, tfield->get_value()); } else if (ttype->is_base_type()) { @@ -2432,9 +2432,7 @@ string t_dart_generator::constant_name(string name) { bool is_first = true; bool was_previous_char_upper = false; - for (string::iterator iter = name.begin(); iter != name.end(); ++iter) { - string::value_type character = (*iter); - + for (char character : name) { bool is_upper = isupper(character); if (is_upper && !is_first && !was_previous_char_upper) { diff --git a/compiler/cpp/src/thrift/generate/t_delphi_generator.cc b/compiler/cpp/src/thrift/generate/t_delphi_generator.cc index 8bd77e84279..d3ad76a32d2 100644 --- a/compiler/cpp/src/thrift/generate/t_delphi_generator.cc +++ b/compiler/cpp/src/thrift/generate/t_delphi_generator.cc @@ -37,6 +37,13 @@ #include "thrift/platform.h" #include "thrift/generate/t_oop_generator.h" +#ifdef _WIN32 +#include +#include +#include +#include +#endif + using std::map; using std::ofstream; using std::ostream; @@ -89,17 +96,17 @@ class t_delphi_generator : public t_oop_generator { escape_['\''] = "''"; } - void init_generator(); - void close_generator(); + void init_generator() override; + void close_generator() override; - void generate_consts(std::vector consts); + void generate_consts(std::vector consts) override; - void generate_typedef(t_typedef* ttypedef); - void generate_enum(t_enum* tenum); - void generate_forward_declaration(t_struct* tstruct); - void generate_struct(t_struct* tstruct); - void generate_xception(t_struct* txception); - void generate_service(t_service* tservice); + void generate_typedef(t_typedef* ttypedef) override; + void generate_enum(t_enum* tenum) override; + void generate_forward_declaration(t_struct* tstruct) override; + void generate_struct(t_struct* tstruct) override; + void generate_xception(t_struct* txception) override; + void generate_service(t_service* tservice) override; void generate_property(ostream& out, t_field* tfield, bool isPublic, bool is_xception); void generate_property_writer_(ostream& out, t_field* tfield, bool isPublic); @@ -108,7 +115,7 @@ class t_delphi_generator : public t_oop_generator { t_field* tfield, bool isPublic, std::string fieldPrefix = ""); - void generate_delphi_isset_reader_definition(ostream& out, t_field* tfield, bool is_xception); + void generate_delphi_isset_reader_writer_definition(ostream& out, t_field* tfield, bool is_xception); void generate_delphi_property_reader_definition(ostream& out, t_field* tfield, bool is_xception_class); @@ -142,7 +149,7 @@ class t_delphi_generator : public t_oop_generator { bool is_union, bool is_xception_factory, std::string xception_factory_name); - void generate_delphi_isset_reader_impl(ostream& out, + void generate_delphi_isset_reader_writer_impl(ostream& out, std::string cls_prefix, std::string name, t_type* type, @@ -152,11 +159,13 @@ class t_delphi_generator : public t_oop_generator { void generate_delphi_struct_writer_impl(ostream& out, std::string cls_prefix, t_struct* tstruct, - bool is_exception); + bool is_exception, + bool is_x_factory); void generate_delphi_struct_result_writer_impl(ostream& out, std::string cls_prefix, t_struct* tstruct, - bool is_exception); + bool is_exception, + bool is_x_factory); void generate_delphi_struct_tostring_impl(ostream& out, std::string cls_prefix, @@ -169,7 +178,8 @@ class t_delphi_generator : public t_oop_generator { void generate_delphi_struct_reader_impl(ostream& out, std::string cls_prefix, t_struct* tstruct, - bool is_exception); + bool is_exception, + bool is_x_factory); void generate_delphi_create_exception_impl(ostream& out, string cls_prefix, t_struct* tstruct, @@ -240,6 +250,7 @@ class t_delphi_generator : public t_oop_generator { void generate_function_helpers(t_function* tfunction); void generate_service_interface(t_service* tservice); void generate_service_interface(t_service* tservice, bool for_async); + void generate_guid(std::ostream& out); void generate_service_helpers(t_service* tservice); void generate_service_client(t_service* tservice); void generate_service_server(t_service* tservice); @@ -319,6 +330,7 @@ class t_delphi_generator : public t_oop_generator { std::string prefix, bool b_no_check_keyword = false); std::string make_valid_delphi_identifier(std::string const& fromName); + std::string make_pascal_string_literal( std::string value); std::string input_arg_prefix(t_type* ttype); std::string base_type_name(t_base_type* tbase); @@ -343,7 +355,7 @@ class t_delphi_generator : public t_oop_generator { void write_struct(std::string line); void write_service(std::string line); - virtual std::string autogen_comment() { + std::string autogen_comment() override { return std::string("(**\n") + " * Autogenerated by Thrift Compiler (" + THRIFT_VERSION + ")\n" + " *\n" + " * DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING\n" + " *)\n"; @@ -670,10 +682,8 @@ void t_delphi_generator::create_keywords() { delphi_reserved_method["instancesize"] = 1; delphi_reserved_method["inheritsfrom"] = 1; delphi_reserved_method["methodaddress"] = 1; - delphi_reserved_method["methodaddress"] = 1; delphi_reserved_method["methodname"] = 1; delphi_reserved_method["fieldaddress"] = 1; - delphi_reserved_method["fieldaddress"] = 1; delphi_reserved_method["getinterface"] = 1; delphi_reserved_method["getinterfaceentry"] = 1; delphi_reserved_method["getinterfacetable"] = 1; @@ -759,9 +769,9 @@ void t_delphi_generator::init_generator() { string unitname, nsname; const vector& includes = program_->get_includes(); - for (size_t i = 0; i < includes.size(); ++i) { - unitname = includes[i]->get_name(); - nsname = includes[i]->get_namespace("delphi"); + for (auto include : includes) { + unitname = include->get_name(); + nsname = include->get_namespace("delphi"); if ("" != nsname) { unitname = normalize_name(nsname); } @@ -788,11 +798,12 @@ void t_delphi_generator::close_generator() { std::string f_name = get_out_dir() + "/" + unitname + ".pas"; ofstream_with_content_based_conditional_update f_all; - f_all.open(f_name.c_str()); + f_all.open(f_name); f_all << autogen_comment() << endl; generate_delphi_doc(f_all, program_); f_all << "unit " << unitname << ";" << endl << endl; + f_all << "{$WARN SYMBOL_DEPRECATED OFF}" << endl << endl; f_all << "interface" << endl << endl; f_all << "uses" << endl; @@ -947,9 +958,9 @@ void t_delphi_generator::generate_typedef(t_typedef* ttypedef) { } bool t_delphi_generator::is_fully_defined_type(t_type* ttype) { - if ((NULL != ttype->get_program()) && (ttype->get_program() != program_)) { + if ((nullptr != ttype->get_program()) && (ttype->get_program() != program_)) { t_scope* scope = ttype->get_program()->scope(); - if (NULL != scope->get_type(ttype->get_name())) { + if (nullptr != scope->get_type(ttype->get_name())) { // printf("type %s found in included scope %s\n", ttype->get_name().c_str(), // ttype->get_program()->get_name().c_str()); return true; @@ -1043,6 +1054,28 @@ void t_delphi_generator::generate_enum(t_enum* tenum) { indent_down(); } +std::string t_delphi_generator::make_pascal_string_literal(std::string value) { + std::stringstream result; + + if (value.length() == 0) { + return ""; + } + + result << "'"; + for (char const &c: value) { + if( (c >= 0) && (c < 32)) { // convert ctrl chars, but leave UTF-8 alone + result << "#" << (int)c; + } else if (c == '\'') { + result << "''"; // duplicate any single quotes we find + } else { + result << c; // anything else "as is" + } + } + result << "'"; + + return result.str(); +} + std::string t_delphi_generator::make_valid_delphi_identifier(std::string const& fromName) { std::string str = fromName; if (str.empty()) { @@ -1221,13 +1254,13 @@ void t_delphi_generator::print_const_def_value(std::ostream& vars, const map& val = value->get_map(); map::const_iterator v_iter; for (v_iter = val.begin(); v_iter != val.end(); ++v_iter) { - t_type* field_type = NULL; + t_type* field_type = nullptr; for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { if ((*f_iter)->get_name() == v_iter->first->get_string()) { field_type = (*f_iter)->get_type(); } } - if (field_type == NULL) { + if (field_type == nullptr) { throw "type error: " + type->get_name() + " has no field " + v_iter->first->get_string(); } string val = render_const_value(vars, out, name, field_type, v_iter->second); @@ -1457,7 +1490,7 @@ void t_delphi_generator::generate_delphi_struct_impl(ostream& out, while (t->is_typedef()) { t = ((t_typedef*)t)->get_type(); } - if ((*m_iter)->get_value() != NULL) { + if ((*m_iter)->get_value() != nullptr) { initialize_field(vars, code, "F" + prop_name((*m_iter)->get_name(), is_exception), @@ -1534,11 +1567,30 @@ void t_delphi_generator::generate_delphi_struct_impl(ostream& out, indent_impl(out) << "begin" << endl; indent_up_impl(); indent_impl(out) << "if F" << exception_factory_name << " = nil" << endl; - indent_impl(out) << "then F" << exception_factory_name << " := T" << exception_factory_name << "Impl.Create;" << endl; - indent_impl(out) << endl; + indent_impl(out) << "then F" << exception_factory_name << " := T" << exception_factory_name << "Impl.Create;" << endl << endl; indent_impl(out) << "result := F" << exception_factory_name << ";" << endl; indent_down_impl(); indent_impl(out) << "end;" << endl << endl; + indent_impl(out) << "function " << cls_prefix << cls_nm << ".QueryInterface(const IID: TGUID; out Obj): HRESULT;" << endl; + indent_impl(out) << "begin" << endl; + indent_up_impl(); + indent_impl(out) << "if GetInterface(IID, Obj)" << endl; + indent_impl(out) << "then result := S_OK" << endl; + indent_impl(out) << "else result := E_NOINTERFACE;" << endl; + indent_down_impl(); + indent_impl(out) << "end;" << endl << endl; + indent_impl(out) << "function " << cls_prefix << cls_nm << "._AddRef: Integer;" << endl; + indent_impl(out) << "begin" << endl; + indent_up_impl(); + indent_impl(out) << "result := -1; // not refcounted" << endl; + indent_down_impl(); + indent_impl(out) << "end;" << endl << endl; + indent_impl(out) << "function " << cls_prefix << cls_nm << "._Release: Integer;" << endl; + indent_impl(out) << "begin" << endl; + indent_up_impl(); + indent_impl(out) << "result := -1; // not refcounted" << endl; + indent_down_impl(); + indent_impl(out) << "end;" << endl << endl; } if (tstruct->is_union()) { @@ -1584,17 +1636,15 @@ void t_delphi_generator::generate_delphi_struct_impl(ostream& out, is_x_factory, exception_factory_name); if ((*m_iter)->get_req() != t_field::T_REQUIRED) { - generate_delphi_isset_reader_impl(out, cls_prefix, cls_nm, t, *m_iter, "F", is_exception); + generate_delphi_isset_reader_writer_impl(out, cls_prefix, cls_nm, t, *m_iter, "F", is_exception); } } - if ((!is_exception) || is_x_factory) { - generate_delphi_struct_reader_impl(out, cls_prefix, tstruct, is_exception); - if (is_result) { - generate_delphi_struct_result_writer_impl(out, cls_prefix, tstruct, is_exception); - } else { - generate_delphi_struct_writer_impl(out, cls_prefix, tstruct, is_exception); - } + generate_delphi_struct_reader_impl(out, cls_prefix, tstruct, is_exception, is_x_factory); + if (is_result) { + generate_delphi_struct_result_writer_impl(out, cls_prefix, tstruct, is_exception, is_x_factory); + } else { + generate_delphi_struct_writer_impl(out, cls_prefix, tstruct, is_exception, is_x_factory); } generate_delphi_struct_tostring_impl(out, cls_prefix, tstruct, is_exception, is_x_factory); @@ -1694,6 +1744,8 @@ void t_delphi_generator::generate_delphi_struct_definition(ostream& out, indent(out) << struct_intf_name << " = interface(IBase)" << endl; indent_up(); + generate_guid(out); + for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) { generate_delphi_property_reader_definition(out, *m_iter, is_exception); generate_delphi_property_writer_definition(out, *m_iter, is_exception); @@ -1716,7 +1768,7 @@ void t_delphi_generator::generate_delphi_struct_definition(ostream& out, out << endl; for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) { if ((*m_iter)->get_req() != t_field::T_REQUIRED) { - generate_delphi_isset_reader_definition(out, *m_iter, is_exception); + generate_delphi_isset_reader_writer_definition(out, *m_iter, is_exception); } } } @@ -1726,7 +1778,7 @@ void t_delphi_generator::generate_delphi_struct_definition(ostream& out, for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) { if ((*m_iter)->get_req() != t_field::T_REQUIRED) { isset_name = "__isset_" + prop_name(*m_iter, is_exception); - indent(out) << "property " << isset_name << ": System.Boolean read Get" << isset_name << ";" + indent(out) << "property " << isset_name << ": System.Boolean read Get" << isset_name << " write Set" << isset_name << ";" << endl; } } @@ -1743,9 +1795,9 @@ void t_delphi_generator::generate_delphi_struct_definition(ostream& out, } out << "class("; if (is_exception && (!is_x_factory)) { - out << "TException"; + out << "TException, IInterface, IBase, ISupportsToString"; } else { - out << "TInterfacedObject, IBase, " << struct_intf_name; + out << "TInterfacedObject, IBase, ISupportsToString, " << struct_intf_name; } out << ")" << endl; @@ -1799,12 +1851,23 @@ void t_delphi_generator::generate_delphi_struct_definition(ostream& out, if ((*m_iter)->get_req() != t_field::T_REQUIRED) { isset_name = "__isset_" + prop_name(*m_iter, is_exception); indent(out) << "function Get" << isset_name << ": System.Boolean;" << endl; + indent(out) << "procedure Set" << isset_name << "( const value : System.Boolean);" << endl; } } } - indent_down(); + if (is_exception && (!is_x_factory)) { + out << endl; + indent_down(); + indent(out) << "strict protected" << endl; + indent_up(); + indent(out) << "function QueryInterface(const IID: TGUID; out Obj): HRESULT; stdcall;" << endl; + indent(out) << "function _AddRef: Integer; stdcall;" << endl; + indent(out) << "function _Release: Integer; stdcall;" << endl; + out << endl; + } + indent_down(); indent(out) << "public" << endl; indent_up(); @@ -1827,12 +1890,10 @@ void t_delphi_generator::generate_delphi_struct_definition(ostream& out, indent(out) << "function " << exception_factory_name << ": " << struct_intf_name << ";" << endl; } - if ((!is_exception) || is_x_factory) { - out << endl; - indent(out) << "// IBase" << endl; - indent(out) << "procedure Read( const iprot: IProtocol);" << endl; - indent(out) << "procedure Write( const oprot: IProtocol);" << endl; - } + out << endl; + indent(out) << "// IBase" << endl; + indent(out) << "procedure Read( const iprot: IProtocol);" << endl; + indent(out) << "procedure Write( const oprot: IProtocol);" << endl; if (is_exception && is_x_factory) { out << endl; @@ -1854,7 +1915,7 @@ void t_delphi_generator::generate_delphi_struct_definition(ostream& out, for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) { if ((*m_iter)->get_req() != t_field::T_REQUIRED) { isset_name = "__isset_" + prop_name(*m_iter, is_exception); - indent(out) << "property " << isset_name << ": System.Boolean read Get" << isset_name << ";" + indent(out) << "property " << isset_name << ": System.Boolean read Get" << isset_name << " write Set" << isset_name << ";" << endl; } } @@ -1898,7 +1959,7 @@ void t_delphi_generator::generate_service_interface(t_service* tservice, bool fo indent_up(); generate_delphi_doc(s_service, tservice); - if (tservice->get_extends() != NULL) { + if (tservice->get_extends() != nullptr) { extends = type_name(tservice->get_extends(), true, true); extends_iface = extends + "." + iface_name; generate_delphi_doc(s_service, tservice); @@ -1908,6 +1969,7 @@ void t_delphi_generator::generate_service_interface(t_service* tservice, bool fo } indent_up(); + generate_guid(s_service); vector functions = tservice->get_functions(); vector::iterator f_iter; for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) { @@ -1920,6 +1982,23 @@ void t_delphi_generator::generate_service_interface(t_service* tservice, bool fo indent_down(); } +void t_delphi_generator::generate_guid(std::ostream& out) { +#ifdef _WIN32 // TODO: add support for non-windows platforms if needed + GUID guid; + if (SUCCEEDED(CoCreateGuid(&guid))) { + OLECHAR guid_chars[40]; + if (StringFromGUID2(guid, &guid_chars[0], sizeof(guid_chars) / sizeof(guid_chars[0])) > 0) { + std::wstring guid_wstr(guid_chars); + std::wstring_convert> convert; + std::string guid_str = convert.to_bytes(guid_wstr); + indent(out) << "['" << guid_str << "']" << endl; + } + } +#else + (void)out; // prevent unused warning on other platforms +#endif +} + void t_delphi_generator::generate_service_helpers(t_service* tservice) { vector functions = tservice->get_functions(); vector::iterator f_iter; @@ -1942,7 +2021,7 @@ void t_delphi_generator::generate_service_client(t_service* tservice) { string implements = async_ ? "Iface, IAsync" : "Iface"; generate_delphi_doc(s_service, tservice); - if (tservice->get_extends() != NULL) { + if (tservice->get_extends() != nullptr) { extends = type_name(tservice->get_extends(), true, true); extends_client = extends + ".TClient"; } @@ -2165,9 +2244,7 @@ void t_delphi_generator::generate_service_client(t_service* tservice) { indent_impl(s_service_impl) << "begin" << endl; indent_up_impl(); indent_impl(s_service_impl) << msgvar << " := iprot_.ReadMessageBegin();" << endl; - indent_impl(s_service_impl) << "if (" << msgvar << ".Type_ = TMessageType.Exception) then" - << endl; - indent_impl(s_service_impl) << "begin" << endl; + indent_impl(s_service_impl) << "if (" << msgvar << ".Type_ = TMessageType.Exception) then begin" << endl; indent_up_impl(); indent_impl(s_service_impl) << appexvar << " := TApplicationException.Read(iprot_);" << endl; indent_impl(s_service_impl) << "iprot_.ReadMessageEnd();" << endl; @@ -2180,8 +2257,7 @@ void t_delphi_generator::generate_service_client(t_service* tservice) { indent_impl(s_service_impl) << "iprot_.ReadMessageEnd();" << endl; if (!(*f_iter)->get_returntype()->is_void()) { - indent_impl(s_service_impl) << "if (" << retvar << ".__isset_success) then" << endl; - indent_impl(s_service_impl) << "begin" << endl; + indent_impl(s_service_impl) << "if (" << retvar << ".__isset_success) then begin" << endl; indent_up_impl(); indent_impl(s_service_impl) << "Result := " << retvar << ".Success;" << endl; t_type* type = (*f_iter)->get_returntype(); @@ -2197,8 +2273,7 @@ void t_delphi_generator::generate_service_client(t_service* tservice) { vector::const_iterator x_iter; for (x_iter = xceptions.begin(); x_iter != xceptions.end(); ++x_iter) { indent_impl(s_service_impl) << "if (" << retvar << ".__isset_" << prop_name(*x_iter) - << ") then" << endl; - indent_impl(s_service_impl) << "begin" << endl; + << ") then begin" << endl; indent_up_impl(); indent_impl(s_service_impl) << exceptvar << " := " << retvar << "." << prop_name(*x_iter) << ".CreateException;" << endl; @@ -2231,7 +2306,7 @@ void t_delphi_generator::generate_service_server(t_service* tservice) { string full_cls = normalize_clsnm(service_name_, "T") + ".TProcessorImpl"; - if (tservice->get_extends() != NULL) { + if (tservice->get_extends() != nullptr) { extends = type_name(tservice->get_extends(), true, true); extends_processor = extends + ".TProcessorImpl"; indent(s_service) << "TProcessorImpl = class(" << extends_processor << ", IProcessor)" << endl; @@ -2248,13 +2323,13 @@ void t_delphi_generator::generate_service_server(t_service* tservice) { indent_impl(s_service_impl) << "constructor " << full_cls << ".Create( iface_: Iface );" << endl; indent_impl(s_service_impl) << "begin" << endl; indent_up_impl(); - if (tservice->get_extends() != NULL) { + if (tservice->get_extends() != nullptr) { indent_impl(s_service_impl) << "inherited Create( iface_);" << endl; } else { indent_impl(s_service_impl) << "inherited Create;" << endl; } indent_impl(s_service_impl) << "Self.iface_ := iface_;" << endl; - if (tservice->get_extends() != NULL) { + if (tservice->get_extends() != nullptr) { indent_impl(s_service_impl) << "ASSERT( processMap_ <> nil); // inherited" << endl; } else { indent_impl(s_service_impl) @@ -2280,7 +2355,7 @@ void t_delphi_generator::generate_service_server(t_service* tservice) { indent(s_service) << "iface_: Iface;" << endl; indent_down(); - if (tservice->get_extends() == NULL) { + if (tservice->get_extends() == nullptr) { indent(s_service) << "protected" << endl; indent_up(); indent(s_service) << "type" << endl; @@ -2326,8 +2401,7 @@ void t_delphi_generator::generate_service_server(t_service* tservice) { indent_impl(s_service_impl) << "msg := iprot.ReadMessageBegin();" << endl; indent_impl(s_service_impl) << "fn := nil;" << endl; indent_impl(s_service_impl) << "if not processMap_.TryGetValue(msg.Name, fn)" << endl; - indent_impl(s_service_impl) << "or not Assigned(fn) then" << endl; - indent_impl(s_service_impl) << "begin" << endl; + indent_impl(s_service_impl) << "or not Assigned(fn) then begin" << endl; indent_up_impl(); indent_impl(s_service_impl) << "TProtocolUtil.Skip(iprot, TType.Struct);" << endl; indent_impl(s_service_impl) << "iprot.ReadMessageEnd();" << endl; @@ -2718,8 +2792,7 @@ void t_delphi_generator::generate_deserialize_container(ostream& out, indent_impl(out) << obj << " := iprot.ReadListBegin();" << endl; } - indent_impl(out) << "for " << counter << " := 0 to " << obj << ".Count - 1 do" << endl; - indent_impl(out) << "begin" << endl; + indent_impl(out) << "for " << counter << " := 0 to " << obj << ".Count - 1 do begin" << endl; indent_up_impl(); if (ttype->is_map()) { generate_deserialize_map_element(out, is_xception, (t_map*)ttype, name, local_vars); @@ -2906,20 +2979,17 @@ void t_delphi_generator::generate_serialize_container(ostream& out, string iter = tmp("_iter"); if (ttype->is_map()) { local_vars << " " << iter << ": " << type_name(((t_map*)ttype)->get_key_type()) << ";" << endl; - indent_impl(out) << "for " << iter << " in " << prefix << ".Keys do" << endl; - indent_impl(out) << "begin" << endl; + indent_impl(out) << "for " << iter << " in " << prefix << ".Keys do begin" << endl; indent_up_impl(); } else if (ttype->is_set()) { local_vars << " " << iter << ": " << type_name(((t_set*)ttype)->get_elem_type()) << ";" << endl; - indent_impl(out) << "for " << iter << " in " << prefix << " do" << endl; - indent_impl(out) << "begin" << endl; + indent_impl(out) << "for " << iter << " in " << prefix << " do begin" << endl; indent_up_impl(); } else if (ttype->is_list()) { local_vars << " " << iter << ": " << type_name(((t_list*)ttype)->get_elem_type()) << ";" << endl; - indent_impl(out) << "for " << iter << " in " << prefix << " do" << endl; - indent_impl(out) << "begin" << endl; + indent_impl(out) << "for " << iter << " in " << prefix << " do begin" << endl; indent_up_impl(); } @@ -3033,7 +3103,7 @@ string t_delphi_generator::type_name(t_type* ttype, if (ttype->is_typedef()) { t_typedef* tdef = (t_typedef*)ttype; if (tdef->is_forward_typedef()) { // forward types according to THRIFT-2421 - if (tdef->get_type() != NULL) { + if (tdef->get_type() != nullptr) { return type_name(tdef->get_type(), b_cls, b_no_postfix, @@ -3215,25 +3285,42 @@ string t_delphi_generator::function_signature(t_function* tfunction, prefix = full_cls + "."; } + string signature = ""; + if( for_async) { if (is_void(ttype)) { - return "function " + prefix + normalize_name(tfunction->get_name(), true, is_xception) + "Async(" - + argument_list(tfunction->get_arglist()) + "): IFuture;"; // no IFuture in Delphi + signature = "function " + prefix + normalize_name(tfunction->get_name(), true, is_xception) + "Async(" + + argument_list(tfunction->get_arglist()) + "): IFuture;"; // no IFuture in Delphi } else { - return "function " + prefix + normalize_name(tfunction->get_name(), true, is_xception) + "Async(" - + argument_list(tfunction->get_arglist()) + "): IFuture<" - + type_name(ttype, false, true, is_xception, true) + ">;"; + signature = "function " + prefix + normalize_name(tfunction->get_name(), true, is_xception) + "Async(" + + argument_list(tfunction->get_arglist()) + "): IFuture<" + + type_name(ttype, false, true, is_xception, true) + ">;"; } } else { if (is_void(ttype)) { - return "procedure " + prefix + normalize_name(tfunction->get_name(), true, is_xception) + "(" - + argument_list(tfunction->get_arglist()) + ");"; + signature = "procedure " + prefix + normalize_name(tfunction->get_name(), true, is_xception) + "(" + + argument_list(tfunction->get_arglist()) + ");"; } else { - return "function " + prefix + normalize_name(tfunction->get_name(), true, is_xception) + "(" - + argument_list(tfunction->get_arglist()) + "): " - + type_name(ttype, false, true, is_xception, true) + ";"; + signature = "function " + prefix + normalize_name(tfunction->get_name(), true, is_xception) + "(" + + argument_list(tfunction->get_arglist()) + "): " + + type_name(ttype, false, true, is_xception, true) + ";"; + } + } + + // deprecated method? only at intf decl! + if( full_cls == "") { + auto iter = tfunction->annotations_.find("deprecated"); + if( tfunction->annotations_.end() != iter) { + signature += " deprecated"; + // empty annotation values end up with "1" somewhere, ignore these as well + if ((iter->second.length() > 0) && (iter->second != "1")) { + signature += " " + make_pascal_string_literal(iter->second); + } + signature += ";"; } } + + return signature; } string t_delphi_generator::argument_list(t_struct* tstruct) { @@ -3414,10 +3501,11 @@ void t_delphi_generator::generate_delphi_property_reader_definition(ostream& out << type_name(ftype, false, true, is_xception, true) << ";" << endl; } -void t_delphi_generator::generate_delphi_isset_reader_definition(ostream& out, +void t_delphi_generator::generate_delphi_isset_reader_writer_definition(ostream& out, t_field* tfield, bool is_xception) { indent(out) << "function Get__isset_" << prop_name(tfield, is_xception) << ": System.Boolean;" << endl; + indent(out) << "procedure Set__isset_" << prop_name(tfield, is_xception) << "( const value : System.Boolean);" << endl; } void t_delphi_generator::generate_delphi_clear_union_value(ostream& out, @@ -3512,7 +3600,7 @@ void t_delphi_generator::generate_delphi_property_reader_impl(ostream& out, indent_impl(out) << "end;" << endl << endl; } -void t_delphi_generator::generate_delphi_isset_reader_impl(ostream& out, +void t_delphi_generator::generate_delphi_isset_reader_writer_impl(ostream& out, std::string cls_prefix, std::string name, t_type* type, @@ -3522,6 +3610,7 @@ void t_delphi_generator::generate_delphi_isset_reader_impl(ostream& out, (void)type; string isset_name = "__isset_" + prop_name(tfield, is_xception); + indent_impl(out) << "function " << cls_prefix << name << "." << "Get" << isset_name << ": System.Boolean;" << endl; indent_impl(out) << "begin" << endl; @@ -3529,6 +3618,14 @@ void t_delphi_generator::generate_delphi_isset_reader_impl(ostream& out, indent_impl(out) << "Result := " << fieldPrefix << isset_name << ";" << endl; indent_down_impl(); indent_impl(out) << "end;" << endl << endl; + + indent_impl(out) << "procedure " << cls_prefix << name << "." + << "Set" << isset_name << "( const value: System.Boolean);" << endl; + indent_impl(out) << "begin" << endl; + indent_up_impl(); + indent_impl(out) << fieldPrefix << isset_name << " := value;" << endl; + indent_down_impl(); + indent_impl(out) << "end;" << endl << endl; } void t_delphi_generator::generate_delphi_create_exception_impl(ostream& out, @@ -3577,7 +3674,8 @@ void t_delphi_generator::generate_delphi_create_exception_impl(ostream& out, void t_delphi_generator::generate_delphi_struct_reader_impl(ostream& out, string cls_prefix, t_struct* tstruct, - bool is_exception) { + bool is_exception, + bool is_x_factory) { ostringstream local_vars; ostringstream code_block; @@ -3606,32 +3704,28 @@ void t_delphi_generator::generate_delphi_struct_reader_impl(ostream& out, indent_impl(code_block) << "try" << endl; indent_up_impl(); - indent_impl(code_block) << "while (true) do" << endl; - indent_impl(code_block) << "begin" << endl; + indent_impl(code_block) << "while (true) do begin" << endl; indent_up_impl(); indent_impl(code_block) << "field_ := iprot.ReadFieldBegin();" << endl; - indent_impl(code_block) << "if (field_.Type_ = TType.Stop) then" << endl; - indent_impl(code_block) << "begin" << endl; - indent_up_impl(); - indent_impl(code_block) << "Break;" << endl; - indent_down_impl(); - indent_impl(code_block) << "end;" << endl; + indent_impl(code_block) << "if (field_.Type_ = TType.Stop) then Break;" << endl; bool first = true; for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { if (first) { + code_block << endl; indent_impl(code_block) << "case field_.ID of" << endl; indent_up_impl(); } first = false; if (f_iter != fields.begin()) { - code_block << ";" << endl; + code_block << endl; } + indent_impl(code_block) << (*f_iter)->get_key() << ": begin" << endl; indent_up_impl(); indent_impl(code_block) << "if (field_.Type_ = " << type_to_enum((*f_iter)->get_type()) @@ -3654,12 +3748,13 @@ void t_delphi_generator::generate_delphi_struct_reader_impl(ostream& out, indent_down_impl(); indent_impl(code_block) << "end;" << endl; indent_down_impl(); - indent_impl(code_block) << "end"; + indent_impl(code_block) << "end;"; } if (!first) { code_block << endl; - indent_impl(code_block) << "else begin" << endl; + indent_down_impl(); + indent_impl(code_block) << "else" << endl; indent_up_impl(); } @@ -3668,8 +3763,6 @@ void t_delphi_generator::generate_delphi_struct_reader_impl(ostream& out, if (!first) { indent_down_impl(); indent_impl(code_block) << "end;" << endl; - indent_down_impl(); - indent_impl(code_block) << "end;" << endl; } indent_impl(code_block) << "iprot.ReadFieldEnd;" << endl; @@ -3686,8 +3779,13 @@ void t_delphi_generator::generate_delphi_struct_reader_impl(ostream& out, indent_impl(code_block) << "end;" << endl; // all required fields have been read? + first = true; for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { if ((*f_iter)->get_req() == t_field::T_REQUIRED) { + if(first) { + code_block << endl; + first = false; + } indent_impl(code_block) << "if not _req_isset_" << prop_name(*f_iter, is_exception) << endl; indent_impl(code_block) << "then raise TProtocolExceptionInvalidData.Create(" @@ -3695,13 +3793,17 @@ void t_delphi_generator::generate_delphi_struct_reader_impl(ostream& out, << endl; } } - + + if( is_exception && (!is_x_factory)) { + code_block << endl; + indent_impl(code_block) << "UpdateMessageProperty;" << endl; + } indent_down_impl(); indent_impl(code_block) << "end;" << endl << endl; string cls_nm; - cls_nm = type_name(tstruct, true, false, is_exception, is_exception); + cls_nm = type_name(tstruct, true, is_exception && (!is_x_factory), is_x_factory, is_x_factory); indent_impl(out) << "procedure " << cls_prefix << cls_nm << ".Read( const iprot: IProtocol);" << endl; @@ -3717,7 +3819,8 @@ void t_delphi_generator::generate_delphi_struct_reader_impl(ostream& out, void t_delphi_generator::generate_delphi_struct_result_writer_impl(ostream& out, string cls_prefix, t_struct* tstruct, - bool is_exception) { + bool is_exception, + bool is_x_factory) { ostringstream local_vars; ostringstream code_block; @@ -3761,7 +3864,7 @@ void t_delphi_generator::generate_delphi_struct_result_writer_impl(ostream& out, string cls_nm; - cls_nm = type_name(tstruct, true, false, is_exception, is_exception); + cls_nm = type_name(tstruct, true, is_exception && (!is_x_factory), is_x_factory, is_x_factory); indent_impl(out) << "procedure " << cls_prefix << cls_nm << ".Write( const oprot: IProtocol);" << endl; @@ -3781,7 +3884,8 @@ void t_delphi_generator::generate_delphi_struct_result_writer_impl(ostream& out, void t_delphi_generator::generate_delphi_struct_writer_impl(ostream& out, string cls_prefix, t_struct* tstruct, - bool is_exception) { + bool is_exception, + bool is_x_factory) { ostringstream local_vars; ostringstream code_block; @@ -3849,7 +3953,7 @@ void t_delphi_generator::generate_delphi_struct_writer_impl(ostream& out, string cls_nm; - cls_nm = type_name(tstruct, true, false, is_exception, is_exception); + cls_nm = type_name(tstruct, true, is_exception && (!is_x_factory), is_x_factory, is_x_factory); indent_impl(out) << "procedure " << cls_prefix << cls_nm << ".Write( const oprot: IProtocol);" << endl; @@ -3951,8 +4055,10 @@ void t_delphi_generator::generate_delphi_struct_tostring_impl(ostream& out, << ".Append('') else " << tmp_sb << ".Append( Self." << prop_name((*f_iter), is_exception) << ".ToString());" << endl; } else if (ttype->is_enum()) { - indent_impl(out) << tmp_sb << ".Append(System.Integer( Self." << prop_name((*f_iter), is_exception) - << "));" << endl; + indent_impl(out) << tmp_sb << ".Append(EnumUtils<" + << type_name(ttype, false, true, false, false) + << ">.ToString( System.Ord( Self." + << prop_name((*f_iter), is_exception) << ")));" << endl; } else { indent_impl(out) << tmp_sb << ".Append( Self." << prop_name((*f_iter), is_exception) << ");" << endl; diff --git a/compiler/cpp/src/thrift/generate/t_erl_generator.cc b/compiler/cpp/src/thrift/generate/t_erl_generator.cc index 587133f8cb2..c96c1b2d539 100644 --- a/compiler/cpp/src/thrift/generate/t_erl_generator.cc +++ b/compiler/cpp/src/thrift/generate/t_erl_generator.cc @@ -83,19 +83,19 @@ class t_erl_generator : public t_generator { * Init and close methods */ - void init_generator(); - void close_generator(); + void init_generator() override; + void close_generator() override; /** * Program-level generation functions */ - void generate_typedef(t_typedef* ttypedef); - void generate_enum(t_enum* tenum); - void generate_const(t_const* tconst); - void generate_struct(t_struct* tstruct); - void generate_xception(t_struct* txception); - void generate_service(t_service* tservice); + void generate_typedef(t_typedef* ttypedef) override; + void generate_enum(t_enum* tenum) override; + void generate_const(t_const* tconst) override; + void generate_struct(t_struct* tstruct) override; + void generate_xception(t_struct* txception) override; + void generate_service(t_service* tservice) override; void generate_member_type(std::ostream& out, t_type* type); void generate_member_value(std::ostream& out, t_type* type, t_const_value* value); @@ -295,7 +295,8 @@ void t_erl_generator::init_generator() { * Boilerplate at beginning and end of header files */ void t_erl_generator::hrl_header(ostream& out, string name) { - out << "-ifndef(_" << name << "_included)." << endl << "-define(_" << name << "_included, yeah)." + out << erl_autogen_comment() << endl + << "-ifndef(_" << name << "_included)." << endl << "-define(_" << name << "_included, yeah)." << endl; } @@ -310,8 +311,8 @@ void t_erl_generator::hrl_footer(ostream& out, string name) { string t_erl_generator::render_includes() { const vector& includes = program_->get_includes(); string result = ""; - for (size_t i = 0; i < includes.size(); ++i) { - result += "-include(\"" + make_safe_for_module_name(includes[i]->get_name()) + for (auto include : includes) { + result += "-include(\"" + make_safe_for_module_name(include->get_name()) + "_types.hrl\").\n"; } if (includes.size() > 0) { @@ -415,7 +416,6 @@ const std::string emit_double_as_string(const double value) { } void t_erl_generator::generate_type_metadata(std::string function_name, vector names) { - vector::iterator s_iter; size_t num_structs = names.size(); indent(f_types_file_) << function_name << "() ->\n"; @@ -625,13 +625,13 @@ string t_erl_generator::render_const_value(t_type* type, t_const_value* value) { bool first = true; for (v_iter = val.begin(); v_iter != val.end(); ++v_iter) { - t_type* field_type = NULL; + t_type* field_type = nullptr; for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { if ((*f_iter)->get_name() == v_iter->first->get_string()) { field_type = (*f_iter)->get_type(); } } - if (field_type == NULL) { + if (field_type == nullptr) { throw "type error: " + type->get_name() + " has no field " + v_iter->first->get_string(); } @@ -909,7 +909,7 @@ void t_erl_generator::generate_service(t_service* tservice) { hrl_header(f_service_hrl_, service_name_); - if (tservice->get_extends() != NULL) { + if (tservice->get_extends() != nullptr) { f_service_hrl_ << "-include(\"" << make_safe_for_module_name(tservice->get_extends()->get_name()) << "_thrift.hrl\"). % inherit " << endl; @@ -947,7 +947,6 @@ void t_erl_generator::generate_service(t_service* tservice) { void t_erl_generator::generate_service_metadata(t_service* tservice) { export_string("function_names", 0); vector functions = tservice->get_functions(); - vector::iterator f_iter; size_t num_functions = functions.size(); indent(f_service_) << "function_names() -> " << endl; @@ -1014,7 +1013,7 @@ void t_erl_generator::generate_service_interface(t_service* tservice) { } // Inheritance - pass unknown functions to base class - if (tservice->get_extends() != NULL) { + if (tservice->get_extends() != nullptr) { indent(f_service_) << "function_info(Function, InfoType) ->" << endl; indent_up(); indent(f_service_) << make_safe_for_module_name(tservice->get_extends()->get_name()) @@ -1278,6 +1277,6 @@ std::string t_erl_generator::type_module(t_type* ttype) { THRIFT_REGISTER_GENERATOR( erl, "Erlang", - " legacynames: Output files retain naming conventions of Thrift 0.9.1 and earlier.\n" - " maps: Generate maps instead of dicts.\n" - " otp16: Generate non-namespaced dict and set instead of dict:dict and sets:set.\n") + " legacynames: Output files retain naming conventions of Thrift 0.9.1 and earlier.\n" + " maps: Generate maps instead of dicts.\n" + " otp16: Generate non-namespaced dict and set instead of dict:dict and sets:set.\n") diff --git a/compiler/cpp/src/thrift/generate/t_generator.cc b/compiler/cpp/src/thrift/generate/t_generator.cc index ca3f5dd682b..3059fb1412b 100644 --- a/compiler/cpp/src/thrift/generate/t_generator.cc +++ b/compiler/cpp/src/thrift/generate/t_generator.cc @@ -234,8 +234,15 @@ t_generator* t_generator_registry::get_generator(t_program* program, gen_map_t& the_map = get_generator_map(); gen_map_t::iterator iter = the_map.find(language); + if ((language == "csharp") || (language == "netcore")) { + failure("The '%s' target is no longer available. Use 'netstd' instead.", language.c_str()); + } + else if (language == "as3") { + pwarning(1, "The '%s' target is deprecated and will be removed in future Thrift versions.", language.c_str()); + } + if (iter == the_map.end()) { - return NULL; + return nullptr; } return iter->second->get_generator(program, parsed_options, options); diff --git a/compiler/cpp/src/thrift/generate/t_generator.h b/compiler/cpp/src/thrift/generate/t_generator.h index cb9d076b50a..f82463d0c14 100644 --- a/compiler/cpp/src/thrift/generate/t_generator.h +++ b/compiler/cpp/src/thrift/generate/t_generator.h @@ -42,8 +42,9 @@ */ class t_generator { public: - t_generator(t_program* program) - : keywords_(lang_keywords()){ + t_generator(t_program* program) { + update_keywords(); + tmp_ = 0; indent_ = 0; program_ = program; @@ -99,16 +100,24 @@ class t_generator { /** * Check if all identifiers are valid for the target language + * See update_keywords() */ virtual void validate_input() const; protected: virtual std::set lang_keywords() const; + /** + * Call this from constructor if you implement lang_keywords() + */ + void update_keywords() { + keywords_ = lang_keywords(); + } + /** * A list of reserved words that cannot be used as identifiers. */ - const std::set keywords_; + std::set keywords_; virtual void validate_id(const std::string& id) const; diff --git a/compiler/cpp/src/thrift/generate/t_generator_registry.h b/compiler/cpp/src/thrift/generate/t_generator_registry.h index 1f02167bc11..d27f7108201 100644 --- a/compiler/cpp/src/thrift/generate/t_generator_registry.h +++ b/compiler/cpp/src/thrift/generate/t_generator_registry.h @@ -35,7 +35,7 @@ class t_generator_factory { const std::string& long_name, const std::string& documentation); - virtual ~t_generator_factory() {} + virtual ~t_generator_factory() = default; virtual t_generator* get_generator( // The program to generate. @@ -65,13 +65,13 @@ class t_generator_factory_impl : public t_generator_factory { const std::string& documentation) : t_generator_factory(short_name, long_name, documentation) {} - virtual t_generator* get_generator(t_program* program, + t_generator* get_generator(t_program* program, const std::map& parsed_options, - const std::string& option_string) { + const std::string& option_string) override { return new generator(program, parsed_options, option_string); } - virtual bool is_valid_namespace(const std::string& sub_namespace) { + bool is_valid_namespace(const std::string& sub_namespace) override { return generator::is_valid_namespace(sub_namespace); } }; diff --git a/compiler/cpp/src/thrift/generate/t_go_generator.cc b/compiler/cpp/src/thrift/generate/t_go_generator.cc index 5ada4fa813f..d9f9d51c5af 100644 --- a/compiler/cpp/src/thrift/generate/t_go_generator.cc +++ b/compiler/cpp/src/thrift/generate/t_go_generator.cc @@ -29,6 +29,7 @@ #include #include #include +#include #include #include @@ -105,19 +106,19 @@ class t_go_generator : public t_generator { * Init and close methods */ - void init_generator(); - void close_generator(); + void init_generator() override; + void close_generator() override; /** * Program-level generation functions */ - void generate_typedef(t_typedef* ttypedef); - void generate_enum(t_enum* tenum); - void generate_const(t_const* tconst); - void generate_struct(t_struct* tstruct); - void generate_xception(t_struct* txception); - void generate_service(t_service* tservice); + void generate_typedef(t_typedef* ttypedef) override; + void generate_enum(t_enum* tenum) override; + void generate_const(t_const* tconst) override; + void generate_struct(t_struct* tstruct) override; + void generate_xception(t_struct* txception) override; + void generate_service(t_service* tservice) override; std::string render_const_value(t_type* type, t_const_value* value, const string& name, bool opt = false); @@ -151,6 +152,7 @@ class t_go_generator : public t_generator { const string& tstruct_name, bool is_result = false, bool uses_countsetfields = false); + void generate_go_struct_equals(std::ostream& out, t_struct* tstruct, const string& tstruct_name); void generate_go_function_helpers(t_function* tfunction); void get_publicized_name_and_def_value(t_field* tfield, string* OUT_pub_name, @@ -228,6 +230,12 @@ class t_go_generator : public t_generator { void generate_serialize_list_element(std::ostream& out, t_list* tlist, std::string iter); + void generate_go_equals(std::ostream& out, t_type* ttype, string tgt, string src); + + void generate_go_equals_struct(std::ostream& out, t_type* ttype, string tgt, string src); + + void generate_go_equals_container(std::ostream& out, t_type* ttype, string tgt, string src); + void generate_go_docstring(std::ostream& out, t_struct* tstruct); void generate_go_docstring(std::ostream& out, t_function* tfunction); @@ -239,6 +247,8 @@ class t_go_generator : public t_generator { void generate_go_docstring(std::ostream& out, t_doc* tdoc); + void parse_go_tags(map* tags, const string in); + /** * Helper rendering functions */ @@ -249,6 +259,8 @@ class t_go_generator : public t_generator { std::string go_imports_end(); std::string render_includes(bool consts); std::string render_included_programs(string& unused_protection); + std::string render_program_import(const t_program* program, string& unused_protection); + std::string render_system_packages(std::vector &system_packages); std::string render_import_protection(); std::string render_fastbinary_includes(); std::string declare_argument(t_field* tfield); @@ -298,14 +310,18 @@ class t_go_generator : public t_generator { std::string package_name_; std::string package_dir_; + std::unordered_map package_identifiers_; + std::set package_identifiers_set_; std::string read_method_name_; std::string write_method_name_; + std::string equals_method_name_; std::set commonInitialisms; std::string camelcase(const std::string& value) const; void fix_common_initialism(std::string& value, int i) const; std::string publicize(const std::string& value, bool is_args_or_result = false) const; + std::string publicize(const std::string& value, bool is_args_or_result, const std::string& service_name) const; std::string privatize(const std::string& value) const; std::string new_prefix(const std::string& value) const; static std::string variable_name_to_go_name(const std::string& value); @@ -459,7 +475,7 @@ void t_go_generator::fix_common_initialism(std::string& value, int i) const { } } -std::string t_go_generator::publicize(const std::string& value, bool is_args_or_result) const { +std::string t_go_generator::publicize(const std::string& value, bool is_args_or_result, const std::string& service_name) const { if (value.size() <= 0) { return value; } @@ -501,12 +517,16 @@ std::string t_go_generator::publicize(const std::string& value, bool is_args_or_ // Avoid naming collisions with other services if (is_args_or_result) { - prefix += publicize(service_name_); + prefix += publicize(service_name); } return prefix + value2; } +std::string t_go_generator::publicize(const std::string& value, bool is_args_or_result) const { + return publicize(value, is_args_or_result, service_name_); +} + std::string t_go_generator::new_prefix(const std::string& value) const { if (value.size() <= 0) { return value; @@ -712,6 +732,7 @@ void t_go_generator::init_generator() { read_method_name_ = "Read"; write_method_name_ = "Write"; } + equals_method_name_ = "Equals"; while (true) { // TODO: Do better error checking here. @@ -743,10 +764,10 @@ void t_go_generator::init_generator() { // Make output files f_types_name_ = package_dir_ + "/" + program_name_ + ".go"; - f_types_.open(f_types_name_.c_str()); + f_types_.open(f_types_name_); f_consts_name_ = package_dir_ + "/" + program_name_ + "-consts.go"; - f_consts_.open(f_consts_name_.c_str()); + f_consts_.open(f_consts_name_); // Print header f_types_ << go_autogen_comment() << go_package() << render_includes(false); @@ -763,33 +784,86 @@ void t_go_generator::init_generator() { f_unused_prot_.close(); } - -string t_go_generator::render_included_programs(string& unused_protection) { +string t_go_generator::render_included_programs(string& unused_prot) { const vector& includes = program_->get_includes(); string result = ""; - - unused_protection = ""; - string local_namespace = program_->get_namespace("go"); - for (size_t i = 0; i < includes.size(); ++i) { - if (!local_namespace.empty() && local_namespace == includes[i]->get_namespace("go")) { + std::set included; + for (auto include : includes) { + if (!local_namespace.empty() && local_namespace == include->get_namespace("go")) { continue; } - string go_module = get_real_go_module(includes[i]); - size_t found = 0; - for (size_t j = 0; j < go_module.size(); j++) { - // Import statement uses slashes ('/') in namespace - if (go_module[j] == '.') { - go_module[j] = '/'; - found = j + 1; - } + if (!included.insert(include->get_namespace("go")).second) { + continue; } - result += "\t\"" + gen_package_prefix_ + go_module + "\"\n"; - unused_protection += "var _ = " + go_module.substr(found) + ".GoUnusedProtection__\n"; + result += render_program_import(include, unused_prot); } + return result; +} + +string t_go_generator::render_program_import(const t_program* program, string& unused_protection) { + string result = ""; + + string go_module = get_real_go_module(program); + string go_path = go_module; + size_t found = 0; + for (size_t j = 0; j < go_module.size(); j++) { + // Import statement uses slashes ('/') in namespace + if (go_module[j] == '.') { + go_path[j] = '/'; + found = j + 1; + } + } + + auto it = package_identifiers_.find(go_module); + auto last_component = go_module.substr(found); + if (it == package_identifiers_.end()) { + auto value = last_component; + // This final path component has already been used, let's construct a more unique alias + if (package_identifiers_set_.find(value) != package_identifiers_set_.end()) { + // TODO: This would produce more readable code if it appended trailing go_module + // path components to generate a more readable name unique identifier (e.g. use + // packageacommon as the alias for packagea/common instead of common=). But just + // appending an integer is much simpler code + value = tmp(value); + } + package_identifiers_set_.insert(value); + it = package_identifiers_.emplace(go_module, std::move(value)).first; + } + auto const& package_identifier = it->second; + result += "\t"; + // if the package_identifier is different than final path component we need an alias + if (last_component.compare(package_identifier) != 0) { + result += package_identifier + " "; + } + string s; + + for (auto const& e : package_identifiers_set_) + { + s += e; + s += ','; + } + + s.pop_back(); + + result += "\"" + gen_package_prefix_ + go_path + "\"\n"; + unused_protection += "var _ = " + package_identifier + ".GoUnusedProtection__\n"; + return result; +} + +string t_go_generator::render_system_packages(std::vector& system_packages) { + string result = ""; + for (vector::iterator iter = system_packages.begin(); iter != system_packages.end(); ++iter) { + string package = *iter; + result += "\t\""+ package +"\"\n"; + + // Reserve these package names in case the collide with imported Thrift packages + package_identifiers_set_.insert(package); + package_identifiers_.emplace(package, package); + } return result; } @@ -801,32 +875,14 @@ string t_go_generator::render_includes(bool consts) { const vector& includes = program_->get_includes(); string result = ""; string unused_prot = ""; - - string local_namespace = program_->get_namespace("go"); - for (size_t i = 0; i < includes.size(); ++i) { - if (!local_namespace.empty() && local_namespace == includes[i]->get_namespace("go")) { - continue; - } - - string go_module = get_real_go_module(includes[i]); - size_t found = 0; - for (size_t j = 0; j < go_module.size(); j++) { - // Import statement uses slashes ('/') in namespace - if (go_module[j] == '.') { - go_module[j] = '/'; - found = j + 1; - } - } - - result += "\t\"" + gen_package_prefix_ + go_module + "\"\n"; - unused_prot += "var _ = " + go_module.substr(found) + ".GoUnusedProtection__\n"; - } + result += go_imports_begin(consts); + result += render_included_programs(unused_prot); if (includes.size() > 0) { result += "\n"; } - return go_imports_begin(consts) + result + go_imports_end() + unused_prot; + return result + go_imports_end() + unused_prot; } string t_go_generator::render_import_protection() { @@ -841,13 +897,13 @@ string t_go_generator::render_fastbinary_includes() { } /** - * Autogen'd comment + * Autogen'd comment. The different text is necessary due to + * https://github.com/golang/go/issues/13560#issuecomment-288457920 */ string t_go_generator::go_autogen_comment() { return std::string() + - "// Autogenerated by Thrift Compiler (" + THRIFT_VERSION + ")\n" - "// DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING\n\n"; + "// Code generated by Thrift Compiler (" + THRIFT_VERSION + "). DO NOT EDIT.\n\n"; } /** @@ -862,22 +918,18 @@ string t_go_generator::go_package() { * If consts include the additional imports. */ string t_go_generator::go_imports_begin(bool consts) { - string extra; - + std::vector system_packages; + system_packages.push_back("bytes"); + system_packages.push_back("context"); // If not writing constants, and there are enums, need extra imports. if (!consts && get_program()->get_enums().size() > 0) { - extra += - "\t\"database/sql/driver\"\n" - "\t\"errors\"\n"; + system_packages.push_back("database/sql/driver"); + system_packages.push_back("errors"); } - return string( - "import (\n" - "\t\"bytes\"\n" - "\t\"context\"\n" - "\t\"reflect\"\n" - + extra + - "\t\"fmt\"\n" - "\t\"" + gen_thrift_import_ + "\"\n"); + system_packages.push_back("fmt"); + system_packages.push_back("time"); + system_packages.push_back(gen_thrift_import_); + return "import(\n" + render_system_packages(system_packages); } /** @@ -893,7 +945,7 @@ string t_go_generator::go_imports_end() { "var _ = thrift.ZERO\n" "var _ = fmt.Printf\n" "var _ = context.Background\n" - "var _ = reflect.DeepEqual\n" + "var _ = time.Now\n" "var _ = bytes.Equal\n\n"); } @@ -1150,7 +1202,7 @@ string t_go_generator::render_const_value(t_type* type, t_const_value* value, co map::const_iterator v_iter; for (v_iter = val.begin(); v_iter != val.end(); ++v_iter) { - t_type* field_type = NULL; + t_type* field_type = nullptr; bool is_optional = false; for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { if ((*f_iter)->get_name() == v_iter->first->get_string()) { @@ -1159,7 +1211,7 @@ string t_go_generator::render_const_value(t_type* type, t_const_value* value, co } } - if (field_type == NULL) { + if (field_type == nullptr) { throw "type error: " + type->get_name() + " has no field " + v_iter->first->get_string(); } out << endl << indent() << publicize(v_iter->first->get_string()) << ": " @@ -1255,15 +1307,14 @@ void t_go_generator::generate_go_struct_initializer(ostream& out, bool is_args_or_result) { out << publicize(type_name(tstruct), is_args_or_result) << "{"; const vector& members = tstruct->get_members(); - for (vector::const_iterator m_iter = members.begin(); m_iter != members.end(); - ++m_iter) { - bool pointer_field = is_pointer_field(*m_iter); + for (auto member : members) { + bool pointer_field = is_pointer_field(member); string publicized_name; t_const_value* def_value; - get_publicized_name_and_def_value(*m_iter, &publicized_name, &def_value); - if (!pointer_field && def_value != NULL && !omit_initialization(*m_iter)) { + get_publicized_name_and_def_value(member, &publicized_name, &def_value); + if (!pointer_field && def_value != nullptr && !omit_initialization(member)) { out << endl << indent() << publicized_name << ": " - << render_field_initial_value(*m_iter, (*m_iter)->get_name(), pointer_field) << "," + << render_field_initial_value(member, member->get_name(), pointer_field) << "," << endl; } } @@ -1337,18 +1388,35 @@ void t_go_generator::generate_go_struct_definition(ostream& out, t_type* fieldType = (*m_iter)->get_type(); string goType = type_to_go_type_with_opt(fieldType, is_pointer_field(*m_iter)); - string gotag = "db:\"" + escape_string((*m_iter)->get_name()) + "\" "; - if ((*m_iter)->get_req() == t_field::T_OPTIONAL) { - gotag += "json:\"" + escape_string((*m_iter)->get_name()) + ",omitempty\""; + + maptags; + tags["db"]=escape_string((*m_iter)->get_name()); + + // Only add the `omitempty` tag if this field is optional and has no default value. + // Otherwise a proper value like `false` for a bool field will be ommitted from + // the JSON output since Go Marshal won't output `zero values`. + bool has_default = (*m_iter)->get_value(); + bool is_optional = (*m_iter)->get_req() == t_field::T_OPTIONAL; + if (is_optional && !has_default) { + tags["json"]=escape_string((*m_iter)->get_name())+",omitempty"; } else { - gotag += "json:\"" + escape_string((*m_iter)->get_name()) + "\""; + tags["json"]=escape_string((*m_iter)->get_name()); } - // Check for user override of db and json tags using "go.tag" + // Check for user defined tags and them if there are any. User defined tags + // can override the above db and json tags. std::map::iterator it = (*m_iter)->annotations_.find("go.tag"); if (it != (*m_iter)->annotations_.end()) { - gotag = it->second; + parse_go_tags(&tags, it->second); } + + string gotag; + for (auto it = tags.begin(); it != tags.end(); ++it) { + gotag += it->first + ":\"" + it->second + "\" "; + } + // Trailing whitespace + gotag.resize(gotag.size()-1); + indent(out) << publicize((*m_iter)->get_name()) << " " << goType << " `thrift:\"" << escape_string((*m_iter)->get_name()) << "," << sorted_keys_pos; if ((*m_iter)->get_req() == t_field::T_REQUIRED) { @@ -1382,7 +1450,7 @@ void t_go_generator::generate_go_struct_definition(ostream& out, string def_var_name = tstruct_name + "_" + publicized_name + "_DEFAULT"; if ((*m_iter)->get_req() == t_field::T_OPTIONAL || is_pointer_field(*m_iter)) { out << indent() << "var " << def_var_name << " " << goType; - if (def_value != NULL) { + if (def_value != nullptr) { out << " = " << render_const_value(fieldType, def_value, (*m_iter)->get_name()); } out << endl; @@ -1421,6 +1489,9 @@ void t_go_generator::generate_go_struct_definition(ostream& out, generate_isset_helpers(out, tstruct, tstruct_name, is_result); generate_go_struct_reader(out, tstruct, tstruct_name, is_result); generate_go_struct_writer(out, tstruct, tstruct_name, is_result, num_setable > 0); + if (!is_result && !is_args) { + generate_go_struct_equals(out, tstruct, tstruct_name); + } out << indent() << "func (p *" << tstruct_name << ") String() string {" << endl; out << indent() << " if p == nil {" << endl; @@ -1432,8 +1503,19 @@ void t_go_generator::generate_go_struct_definition(ostream& out, if (is_exception) { out << indent() << "func (p *" << tstruct_name << ") Error() string {" << endl; - out << indent() << " return p.String()" << endl; + indent_up(); + out << indent() << "return p.String()" << endl; + indent_down(); out << indent() << "}" << endl << endl; + + out << indent() << "func (" << tstruct_name << ") TExceptionType() thrift.TExceptionType {" << endl; + indent_up(); + out << indent() << "return thrift.TExceptionTypeCompiled" << endl; + indent_down(); + out << indent() << "}" << endl << endl; + + out << indent() << "var _ thrift.TException = (*" << tstruct_name << ")(nil)" + << endl << endl; } } @@ -1526,10 +1608,10 @@ void t_go_generator::generate_go_struct_reader(ostream& out, const vector& fields = tstruct->get_members(); vector::const_iterator f_iter; string escaped_tstruct_name(escape_string(tstruct->get_name())); - out << indent() << "func (p *" << tstruct_name << ") " << read_method_name_ << "(iprot thrift.TProtocol) error {" + out << indent() << "func (p *" << tstruct_name << ") " << read_method_name_ << "(ctx context.Context, iprot thrift.TProtocol) error {" << endl; indent_up(); - out << indent() << "if _, err := iprot.ReadStructBegin(); err != nil {" << endl; + out << indent() << "if _, err := iprot.ReadStructBegin(ctx); err != nil {" << endl; out << indent() << " return thrift.PrependError(fmt.Sprintf(\"%T read error: \", p), err)" << endl; out << indent() << "}" << endl << endl; @@ -1547,7 +1629,7 @@ void t_go_generator::generate_go_struct_reader(ostream& out, indent(out) << "for {" << endl; indent_up(); // Read beginning field marker - out << indent() << "_, fieldTypeId, fieldId, err := iprot.ReadFieldBegin()" << endl; + out << indent() << "_, fieldTypeId, fieldId, err := iprot.ReadFieldBegin(ctx)" << endl; out << indent() << "if err != nil {" << endl; out << indent() << " return thrift.PrependError(fmt.Sprintf(" "\"%T field %d read error: \", p, fieldId), err)" << endl; @@ -1587,7 +1669,7 @@ void t_go_generator::generate_go_struct_reader(ostream& out, } out << indent() << "if fieldTypeId == " << thriftFieldTypeId << " {" << endl; - out << indent() << " if err := p." << field_method_prefix << field_method_suffix << "(iprot); err != nil {" + out << indent() << " if err := p." << field_method_prefix << field_method_suffix << "(ctx, iprot); err != nil {" << endl; out << indent() << " return err" << endl; out << indent() << " }" << endl; @@ -1599,7 +1681,7 @@ void t_go_generator::generate_go_struct_reader(ostream& out, } out << indent() << "} else {" << endl; - out << indent() << " if err := iprot.Skip(fieldTypeId); err != nil {" << endl; + out << indent() << " if err := iprot.Skip(ctx, fieldTypeId); err != nil {" << endl; out << indent() << " return err" << endl; out << indent() << " }" << endl; out << indent() << "}" << endl; @@ -1615,7 +1697,7 @@ void t_go_generator::generate_go_struct_reader(ostream& out, } // Skip unknown fields in either case - out << indent() << "if err := iprot.Skip(fieldTypeId); err != nil {" << endl; + out << indent() << "if err := iprot.Skip(ctx, fieldTypeId); err != nil {" << endl; out << indent() << " return err" << endl; out << indent() << "}" << endl; @@ -1626,12 +1708,12 @@ void t_go_generator::generate_go_struct_reader(ostream& out, } // Read field end marker - out << indent() << "if err := iprot.ReadFieldEnd(); err != nil {" << endl; + out << indent() << "if err := iprot.ReadFieldEnd(ctx); err != nil {" << endl; out << indent() << " return err" << endl; out << indent() << "}" << endl; indent_down(); out << indent() << "}" << endl; - out << indent() << "if err := iprot.ReadStructEnd(); err != nil {" << endl; + out << indent() << "if err := iprot.ReadStructEnd(ctx); err != nil {" << endl; out << indent() << " return thrift.PrependError(fmt.Sprintf(" "\"%T read struct end error: \", p), err)" << endl; out << indent() << "}" << endl; @@ -1664,7 +1746,7 @@ void t_go_generator::generate_go_struct_reader(ostream& out, } out << indent() << "func (p *" << tstruct_name << ") " << field_method_prefix << field_method_suffix - << "(iprot thrift.TProtocol) error {" << endl; + << "(ctx context.Context, iprot thrift.TProtocol) error {" << endl; indent_up(); generate_deserialize_field(out, *f_iter, false, "p."); indent_down(); @@ -1682,7 +1764,7 @@ void t_go_generator::generate_go_struct_writer(ostream& out, string name(tstruct->get_name()); const vector& fields = tstruct->get_sorted_members(); vector::const_iterator f_iter; - indent(out) << "func (p *" << tstruct_name << ") " << write_method_name_ << "(oprot thrift.TProtocol) error {" << endl; + indent(out) << "func (p *" << tstruct_name << ") " << write_method_name_ << "(ctx context.Context, oprot thrift.TProtocol) error {" << endl; indent_up(); if (tstruct->is_union() && uses_countsetfields) { std::string tstruct_name(publicize(tstruct->get_name())); @@ -1691,7 +1773,7 @@ void t_go_generator::generate_go_struct_writer(ostream& out, << " return fmt.Errorf(\"%T write union: exactly one field must be set (%d set).\", p, c)" << endl << indent() << "}" << endl; } - out << indent() << "if err := oprot.WriteStructBegin(\"" << name << "\"); err != nil {" << endl; + out << indent() << "if err := oprot.WriteStructBegin(ctx, \"" << name << "\"); err != nil {" << endl; out << indent() << " return thrift.PrependError(fmt.Sprintf(" "\"%T write struct begin error: \", p), err) }" << endl; @@ -1717,16 +1799,16 @@ void t_go_generator::generate_go_struct_writer(ostream& out, } out << indent() << "if err := p." << field_method_prefix << field_method_suffix - << "(oprot); err != nil { return err }" << endl; + << "(ctx, oprot); err != nil { return err }" << endl; } indent_down(); out << indent() << "}" << endl; // Write the struct map - out << indent() << "if err := oprot.WriteFieldStop(); err != nil {" << endl; + out << indent() << "if err := oprot.WriteFieldStop(ctx); err != nil {" << endl; out << indent() << " return thrift.PrependError(\"write field stop error: \", err) }" << endl; - out << indent() << "if err := oprot.WriteStructEnd(); err != nil {" << endl; + out << indent() << "if err := oprot.WriteStructEnd(ctx); err != nil {" << endl; out << indent() << " return thrift.PrependError(\"write struct stop error: \", err) }" << endl; out << indent() << "return nil" << endl; indent_down(); @@ -1747,7 +1829,7 @@ void t_go_generator::generate_go_struct_writer(ostream& out, } out << indent() << "func (p *" << tstruct_name << ") " << field_method_prefix << field_method_suffix - << "(oprot thrift.TProtocol) (err error) {" << endl; + << "(ctx context.Context, oprot thrift.TProtocol) (err error) {" << endl; indent_up(); if (field_required == t_field::T_OPTIONAL) { @@ -1755,7 +1837,7 @@ void t_go_generator::generate_go_struct_writer(ostream& out, indent_up(); } - out << indent() << "if err := oprot.WriteFieldBegin(\"" << escape_field_name << "\", " + out << indent() << "if err := oprot.WriteFieldBegin(ctx, \"" << escape_field_name << "\", " << type_to_enum((*f_iter)->get_type()) << ", " << field_id << "); err != nil {" << endl; out << indent() << " return thrift.PrependError(fmt.Sprintf(\"%T write field begin error " << field_id << ":" << escape_field_name << ": \", p), err) }" << endl; @@ -1764,7 +1846,7 @@ void t_go_generator::generate_go_struct_writer(ostream& out, generate_serialize_field(out, *f_iter, "p."); // Write field closer - out << indent() << "if err := oprot.WriteFieldEnd(); err != nil {" << endl; + out << indent() << "if err := oprot.WriteFieldEnd(ctx); err != nil {" << endl; out << indent() << " return thrift.PrependError(fmt.Sprintf(\"%T write field end error " << field_id << ":" << escape_field_name << ": \", p), err) }" << endl; @@ -1779,6 +1861,61 @@ void t_go_generator::generate_go_struct_writer(ostream& out, } } +void t_go_generator::generate_go_struct_equals(ostream& out, + t_struct* tstruct, + const string& tstruct_name) { + string name(tstruct->get_name()); + const vector& fields = tstruct->get_sorted_members(); + vector::const_iterator f_iter; + indent(out) << "func (p *" << tstruct_name << ") " << equals_method_name_ << "(other *" + << tstruct_name << ") bool {" << endl; + indent_up(); + + string field_name; + string publicize_field_name; + out << indent() << "if p == other {" << endl; + indent_up(); + out << indent() << "return true" << endl; + indent_down(); + out << indent() << "} else if p == nil || other == nil {" << endl; + indent_up(); + out << indent() << "return false" << endl; + indent_down(); + out << indent() << "}" << endl; + + for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { + field_name = (*f_iter)->get_name(); + t_type* field_type = (*f_iter)->get_type(); + publicize_field_name = publicize(field_name); + string goType = type_to_go_type_with_opt(field_type, is_pointer_field(*f_iter)); + + string tgt = "p." + publicize_field_name; + string src = "other." + publicize_field_name; + t_type* ttype = field_type->get_true_type(); + // Compare field contents + if (is_pointer_field(*f_iter) + && (ttype->is_base_type() || ttype->is_enum() || ttype->is_container())) { + string tgtv = "(*" + tgt + ")"; + string srcv = "(*" + src + ")"; + out << indent() << "if " << tgt << " != " << src << " {" << endl; + indent_up(); + out << indent() << "if " << tgt << " == nil || " << src << " == nil {" << endl; + indent_up(); + out << indent() << "return false" << endl; + indent_down(); + out << indent() << "}" << endl; + generate_go_equals(out, field_type, tgtv, srcv); + indent_down(); + out << indent() << "}" << endl; + } else { + generate_go_equals(out, field_type, tgt, src); + } + } + out << indent() << "return true" << endl; + indent_down(); + out << indent() << "}" << endl << endl; +} + /** * Generates a thrift service. * @@ -1854,7 +1991,7 @@ void t_go_generator::generate_service_interface(t_service* tservice) { string serviceName(publicize(tservice->get_name())); string interfaceName = serviceName; - if (tservice->get_extends() != NULL) { + if (tservice->get_extends() != nullptr) { extends = type_name(tservice->get_extends()); size_t index = extends.rfind("."); @@ -1897,7 +2034,7 @@ void t_go_generator::generate_service_client(t_service* tservice) { string extends_client_new = ""; string serviceName(publicize(tservice->get_name())); - if (tservice->get_extends() != NULL) { + if (tservice->get_extends() != nullptr) { extends = type_name(tservice->get_extends()); size_t index = extends.rfind("."); @@ -1922,6 +2059,7 @@ void t_go_generator::generate_service_client(t_service* tservice) { f_types_ << indent() << "*" << extends_client << endl; } else { f_types_ << indent() << "c thrift.TClient" << endl; + f_types_ << indent() << "meta thrift.ResponseMeta" << endl; } indent_down(); @@ -1991,7 +2129,19 @@ void t_go_generator::generate_service_client(t_service* tservice) { indent_up(); f_types_ << indent() << "return p.c" << endl; indent_down(); - f_types_ << indent() << "}" << endl; + f_types_ << indent() << "}" << endl << endl; + + f_types_ << indent() << "func (p *" << serviceName << "Client) LastResponseMeta_() thrift.ResponseMeta {" << endl; + indent_up(); + f_types_ << indent() << "return p.meta" << endl; + indent_down(); + f_types_ << indent() << "}" << endl << endl; + + f_types_ << indent() << "func (p *" << serviceName << "Client) SetLastResponseMeta_(meta thrift.ResponseMeta) {" << endl; + indent_up(); + f_types_ << indent() << "p.meta = meta" << endl; + indent_down(); + f_types_ << indent() << "}" << endl << endl; } // Generate client method implementations @@ -2023,8 +2173,11 @@ void t_go_generator::generate_service_client(t_service* tservice) { std::string resultName = tmp("_result"); std::string resultType = publicize(method + "_result", true); f_types_ << indent() << "var " << resultName << " " << resultType << endl; - f_types_ << indent() << "if err = p.Client_().Call(ctx, \"" - << method << "\", &" << argsName << ", &" << resultName << "); err != nil {" << endl; + f_types_ << indent() << "var meta thrift.ResponseMeta" << endl; + f_types_ << indent() << "meta, err = p.Client_().Call(ctx, \"" + << method << "\", &" << argsName << ", &" << resultName << ")" << endl; + f_types_ << indent() << "p.SetLastResponseMeta_(meta)" << endl; + f_types_ << indent() << "if err != nil {" << endl; indent_up(); f_types_ << indent() << "return" << endl; @@ -2063,9 +2216,13 @@ void t_go_generator::generate_service_client(t_service* tservice) { f_types_ << indent() << "return nil" << endl; } } else { + // Since we don't have response meta for oneway calls, overwrite it with + // an empty one to avoid users getting the meta from last call and + // mistaken it as from the oneway call. + f_types_ << indent() << "p.SetLastResponseMeta_(thrift.ResponseMeta{})" << endl; // TODO: would be nice to not to duplicate the call generation - f_types_ << indent() << "if err := p.Client_().Call(ctx, \"" - << method << "\", &"<< argsName << ", nil); err != nil {" << endl; + f_types_ << indent() << "if _, err := p.Client_().Call(ctx, \"" + << method << "\", &"<< argsName << ", nil); err != nil {" << endl; indent_up(); f_types_ << indent() << "return err" << endl; @@ -2085,13 +2242,26 @@ void t_go_generator::generate_service_client(t_service* tservice) { * @param tservice The service to generate a remote for. */ void t_go_generator::generate_service_remote(t_service* tservice) { - vector functions = tservice->get_functions(); - t_service* parent = tservice->get_extends(); + vector functions; + std::unordered_map func_to_service; - // collect inherited functions - while (parent != NULL) { + // collect all functions including inherited functions + t_service* parent = tservice; + while (parent != nullptr) { vector p_functions = parent->get_functions(); functions.insert(functions.end(), p_functions.begin(), p_functions.end()); + + // We need to maintain a map of functions names to the name of their parent. + // This is because functions may come from a parent service, and if we need + // to create the arguments struct (e.g. `NewParentServiceNameFuncNameArgs()`) + // we need to make sure to specify the correct service name. + for (vector::iterator f_iter = p_functions.begin(); f_iter != p_functions.end(); ++f_iter) { + auto it = func_to_service.find((*f_iter)->get_name()); + if (it == func_to_service.end()) { + func_to_service.emplace((*f_iter)->get_name(), parent->get_name()); + } + } + parent = parent->get_extends(); } @@ -2108,35 +2278,27 @@ void t_go_generator::generate_service_remote(t_service* tservice) { + underscore(service_name_) + "-remote.go"; ofstream_with_content_based_conditional_update f_remote; f_remote.open(f_remote_name.c_str()); - string service_module = get_real_go_module(program_); - string::size_type loc; - - while ((loc = service_module.find(".")) != string::npos) { - service_module.replace(loc, 1, 1, '/'); - } - if (!gen_package_prefix_.empty()) { - service_module = gen_package_prefix_ + service_module; - } string unused_protection; - string ctxPackage = "context"; + std::vector system_packages; + system_packages.push_back("context"); + system_packages.push_back("flag"); + system_packages.push_back("fmt"); + system_packages.push_back("math"); + system_packages.push_back("net"); + system_packages.push_back("net/url"); + system_packages.push_back("os"); + system_packages.push_back("strconv"); + system_packages.push_back("strings"); f_remote << go_autogen_comment(); f_remote << indent() << "package main" << endl << endl; f_remote << indent() << "import (" << endl; - f_remote << indent() << " \"" << ctxPackage << "\"" << endl; - f_remote << indent() << " \"flag\"" << endl; - f_remote << indent() << " \"fmt\"" << endl; - f_remote << indent() << " \"math\"" << endl; - f_remote << indent() << " \"net\"" << endl; - f_remote << indent() << " \"net/url\"" << endl; - f_remote << indent() << " \"os\"" << endl; - f_remote << indent() << " \"strconv\"" << endl; - f_remote << indent() << " \"strings\"" << endl; - f_remote << indent() << " \"" + gen_thrift_import_ + "\"" << endl; + f_remote << render_system_packages(system_packages); + f_remote << indent() << "\t\"" + gen_thrift_import_ + "\"" << endl; f_remote << indent() << render_included_programs(unused_protection); - f_remote << indent() << " \"" << service_module << "\"" << endl; + f_remote << render_program_import(program_, unused_protection); f_remote << indent() << ")" << endl; f_remote << indent() << endl; f_remote << indent() << unused_protection; // filled in render_included_programs() @@ -2148,12 +2310,13 @@ void t_go_generator::generate_service_remote(t_service* tservice) { f_remote << indent() << " flag.PrintDefaults()" << endl; f_remote << indent() << " fmt.Fprintln(os.Stderr, \"\\nFunctions:\")" << endl; + string package_name_aliased = package_identifiers_[get_real_go_module(program_)]; + for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) { f_remote << " fmt.Fprintln(os.Stderr, \" " << (*f_iter)->get_returntype()->get_name() << " " << (*f_iter)->get_name() << "("; t_struct* arg_struct = (*f_iter)->get_arglist(); const std::vector& args = arg_struct->get_members(); - vector::const_iterator a_iter; std::vector::size_type num_args = args.size(); bool first = true; @@ -2295,7 +2458,7 @@ void t_go_generator::generate_service_remote(t_service* tservice) { f_remote << indent() << "}" << endl; f_remote << indent() << "iprot := protocolFactory.GetProtocol(trans)" << endl; f_remote << indent() << "oprot := protocolFactory.GetProtocol(trans)" << endl; - f_remote << indent() << "client := " << package_name_ << ".New" << publicize(service_name_) + f_remote << indent() << "client := " << package_name_aliased << ".New" << publicize(service_name_) << "Client(thrift.NewTStandardClient(iprot, oprot))" << endl; f_remote << indent() << "if err := trans.Open(); err != nil {" << endl; f_remote << indent() << " fmt.Fprintln(os.Stderr, \"Error opening socket to \", " @@ -2308,11 +2471,10 @@ void t_go_generator::generate_service_remote(t_service* tservice) { for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) { t_struct* arg_struct = (*f_iter)->get_arglist(); const std::vector& args = arg_struct->get_members(); - vector::const_iterator a_iter; std::vector::size_type num_args = args.size(); string funcName((*f_iter)->get_name()); string pubName(publicize(funcName)); - string argumentsName(publicize(funcName + "_args", true)); + string argumentsName(publicize(funcName + "_args", true, func_to_service[funcName])); f_remote << indent() << "case \"" << escape_string(funcName) << "\":" << endl; indent_up(); f_remote << indent() << "if flag.NArg() - 1 != " << num_args << " {" << endl; @@ -2333,7 +2495,7 @@ void t_go_generator::generate_service_remote(t_service* tservice) { f_remote << indent() << " Usage()" << endl; f_remote << indent() << " return" << endl; f_remote << indent() << "}" << endl; - f_remote << indent() << "argvalue" << i << " := " << package_name_ << "." + f_remote << indent() << "argvalue" << i << " := " << package_name_aliased << "." << publicize(the_type->get_name()) << "(tmp" << i << ")" << endl; } else if (the_type2->is_base_type()) { t_base_type::t_base e = ((t_base_type*)the_type2)->get_base(); @@ -2421,7 +2583,7 @@ void t_go_generator::generate_service_remote(t_service* tservice) { std::string tstruct_name(publicize(the_type->get_name())); std::string tstruct_module( module_name(the_type)); if(tstruct_module.empty()) { - tstruct_module = package_name_; + tstruct_module = package_name_aliased; } f_remote << indent() << arg << " := flag.Arg(" << flagArg << ")" << endl; @@ -2439,7 +2601,7 @@ void t_go_generator::generate_service_remote(t_service* tservice) { << endl; f_remote << indent() << "argvalue" << i << " := " << tstruct_module << ".New" << tstruct_name << "()" << endl; - f_remote << indent() << err2 << " := argvalue" << i << "." << read_method_name_ << "(" << jsProt << ")" << endl; + f_remote << indent() << err2 << " := argvalue" << i << "." << read_method_name_ << "(context.Background(), " << jsProt << ")" << endl; f_remote << indent() << "if " << err2 << " != nil {" << endl; f_remote << indent() << " Usage()" << endl; f_remote << indent() << " return" << endl; @@ -2465,9 +2627,9 @@ void t_go_generator::generate_service_remote(t_service* tservice) { f_remote << indent() << factory << " := thrift.NewTJSONProtocolFactory()" << endl; f_remote << indent() << jsProt << " := " << factory << ".GetProtocol(" << mbTrans << ")" << endl; - f_remote << indent() << "containerStruct" << i << " := " << package_name_ << ".New" + f_remote << indent() << "containerStruct" << i << " := " << package_name_aliased << ".New" << argumentsName << "()" << endl; - f_remote << indent() << err2 << " := containerStruct" << i << ".ReadField" << (i + 1) << "(" + f_remote << indent() << err2 << " := containerStruct" << i << ".ReadField" << (i + 1) << "(context.Background(), " << jsProt << ")" << endl; f_remote << indent() << "if " << err2 << " != nil {" << endl; f_remote << indent() << " Usage()" << endl; @@ -2480,9 +2642,9 @@ void t_go_generator::generate_service_remote(t_service* tservice) { } if (the_type->is_typedef()) { - std::string typedef_module( module_name(the_type)); + std::string typedef_module(module_name(the_type)); if(typedef_module.empty()) { - typedef_module = package_name_; + typedef_module = package_name_aliased; } f_remote << indent() << "value" << i << " := " << typedef_module << "." << publicize(the_type->get_name()) << "(argvalue" << i << ")" << endl; @@ -2575,7 +2737,7 @@ void t_go_generator::generate_service_server(t_service* tservice) { string extends_processor_new = ""; string serviceName(publicize(tservice->get_name())); - if (tservice->get_extends() != NULL) { + if (tservice->get_extends() != nullptr) { extends = type_name(tservice->get_extends()); size_t index = extends.rfind("."); @@ -2634,19 +2796,19 @@ void t_go_generator::generate_service_server(t_service* tservice) { f_types_ << indent() << "func (p *" << serviceName << "Processor) Process(ctx context.Context, iprot, oprot thrift.TProtocol) (success bool, err " "thrift.TException) {" << endl; - f_types_ << indent() << " name, _, seqId, err := iprot.ReadMessageBegin()" << endl; - f_types_ << indent() << " if err != nil { return false, err }" << endl; + f_types_ << indent() << " name, _, seqId, err2 := iprot.ReadMessageBegin(ctx)" << endl; + f_types_ << indent() << " if err2 != nil { return false, thrift.WrapTException(err2) }" << endl; f_types_ << indent() << " if processor, ok := p.GetProcessorFunction(name); ok {" << endl; f_types_ << indent() << " return processor.Process(ctx, seqId, iprot, oprot)" << endl; f_types_ << indent() << " }" << endl; - f_types_ << indent() << " iprot.Skip(thrift.STRUCT)" << endl; - f_types_ << indent() << " iprot.ReadMessageEnd()" << endl; + f_types_ << indent() << " iprot.Skip(ctx, thrift.STRUCT)" << endl; + f_types_ << indent() << " iprot.ReadMessageEnd(ctx)" << endl; f_types_ << indent() << " " << x << " := thrift.NewTApplicationException(thrift.UNKNOWN_METHOD, \"Unknown function " "\" + name)" << endl; - f_types_ << indent() << " oprot.WriteMessageBegin(name, thrift.EXCEPTION, seqId)" << endl; - f_types_ << indent() << " " << x << ".Write(oprot)" << endl; - f_types_ << indent() << " oprot.WriteMessageEnd()" << endl; + f_types_ << indent() << " oprot.WriteMessageBegin(ctx, name, thrift.EXCEPTION, seqId)" << endl; + f_types_ << indent() << " " << x << ".Write(ctx, oprot)" << endl; + f_types_ << indent() << " oprot.WriteMessageEnd(ctx)" << endl; f_types_ << indent() << " oprot.Flush(ctx)" << endl; f_types_ << indent() << " return false, " << x << endl; f_types_ << indent() << "" << endl; @@ -2693,7 +2855,6 @@ void t_go_generator::generate_process_function(t_service* tservice, t_function* // t_struct* xs = tfunction->get_xceptions(); // const std::vector& xceptions = xs->get_members(); - vector::const_iterator x_iter; f_types_ << indent() << "type " << processorName << " struct {" << endl; f_types_ << indent() << " handler " << publicize(tservice->get_name()) << endl; f_types_ << indent() << "}" << endl << endl; @@ -2702,31 +2863,82 @@ void t_go_generator::generate_process_function(t_service* tservice, t_function* "thrift.TException) {" << endl; indent_up(); f_types_ << indent() << "args := " << argsname << "{}" << endl; - f_types_ << indent() << "if err = args." << read_method_name_ << "(iprot); err != nil {" << endl; - f_types_ << indent() << " iprot.ReadMessageEnd()" << endl; + f_types_ << indent() << "var err2 error" << endl; + f_types_ << indent() << "if err2 = args." << read_method_name_ << "(ctx, iprot); err2 != nil {" << endl; + f_types_ << indent() << " iprot.ReadMessageEnd(ctx)" << endl; if (!tfunction->is_oneway()) { f_types_ << indent() - << " x := thrift.NewTApplicationException(thrift.PROTOCOL_ERROR, err.Error())" + << " x := thrift.NewTApplicationException(thrift.PROTOCOL_ERROR, err2.Error())" << endl; - f_types_ << indent() << " oprot.WriteMessageBegin(\"" << escape_string(tfunction->get_name()) + f_types_ << indent() << " oprot.WriteMessageBegin(ctx, \"" << escape_string(tfunction->get_name()) << "\", thrift.EXCEPTION, seqId)" << endl; - f_types_ << indent() << " x.Write(oprot)" << endl; - f_types_ << indent() << " oprot.WriteMessageEnd()" << endl; + f_types_ << indent() << " x.Write(ctx, oprot)" << endl; + f_types_ << indent() << " oprot.WriteMessageEnd(ctx)" << endl; f_types_ << indent() << " oprot.Flush(ctx)" << endl; } - f_types_ << indent() << " return false, err" << endl; - f_types_ << indent() << "}" << endl << endl; - f_types_ << indent() << "iprot.ReadMessageEnd()" << endl; + f_types_ << indent() << " return false, thrift.WrapTException(err2)" << endl; + f_types_ << indent() << "}" << endl; + f_types_ << indent() << "iprot.ReadMessageEnd(ctx)" << endl << endl; + + // Even though we never create the goroutine in oneway handlers, + // always have (nop) tickerCancel defined makes the writing part of code + // generating easier and less error-prone. + f_types_ << indent() << "tickerCancel := func() {}" << endl; + // Only create the goroutine for non-oneways. + if (!tfunction->is_oneway()) { + f_types_ << indent() << "// Start a goroutine to do server side connectivity check." << endl; + f_types_ << indent() << "if thrift.ServerConnectivityCheckInterval > 0 {" << endl; + + indent_up(); + f_types_ << indent() << "var cancel context.CancelFunc" << endl; + f_types_ << indent() << "ctx, cancel = context.WithCancel(ctx)" << endl; + f_types_ << indent() << "defer cancel()" << endl; + f_types_ << indent() << "var tickerCtx context.Context" << endl; + f_types_ << indent() << "tickerCtx, tickerCancel = context.WithCancel(context.Background())" << endl; + f_types_ << indent() << "defer tickerCancel()" << endl; + f_types_ << indent() << "go func(ctx context.Context, cancel context.CancelFunc) {" << endl; + + indent_up(); + f_types_ << indent() << "ticker := time.NewTicker(thrift.ServerConnectivityCheckInterval)" << endl; + f_types_ << indent() << "defer ticker.Stop()" << endl; + f_types_ << indent() << "for {" << endl; + + indent_up(); + f_types_ << indent() << "select {" << endl; + f_types_ << indent() << "case <-ctx.Done():" << endl; + indent_up(); + f_types_ << indent() << "return" << endl; + indent_down(); + f_types_ << indent() << "case <-ticker.C:" << endl; + + indent_up(); + f_types_ << indent() << "if !iprot.Transport().IsOpen() {" << endl; + indent_up(); + f_types_ << indent() << "cancel()" << endl; + f_types_ << indent() << "return" << endl; + indent_down(); + f_types_ << indent() << "}" << endl; + indent_down(); + f_types_ << indent() << "}" << endl; + indent_down(); + f_types_ << indent() << "}" << endl; + indent_down(); + f_types_ << indent() << "}(tickerCtx, cancel)" << endl; + indent_down(); + f_types_ << indent() << "}" << endl << endl; + } else { + // Make sure we don't get the defined but unused compiling error. + f_types_ << indent() << "_ = tickerCancel" << endl << endl; + } if (!tfunction->is_oneway()) { f_types_ << indent() << "result := " << resultname << "{}" << endl; } bool need_reference = type_need_reference(tfunction->get_returntype()); if (!tfunction->is_oneway() && !tfunction->get_returntype()->is_void()) { - f_types_ << "var retval " << type_to_go_type(tfunction->get_returntype()) << endl; + f_types_ << indent() << "var retval " << type_to_go_type(tfunction->get_returntype()) << endl; } - f_types_ << indent() << "var err2 error" << endl; f_types_ << indent() << "if "; if (!tfunction->is_oneway()) { @@ -2755,6 +2967,7 @@ void t_go_generator::generate_process_function(t_service* tservice, t_function* } f_types_ << "); err2 != nil {" << endl; + f_types_ << indent() << " tickerCancel()" << endl; t_struct* exceptions = tfunction->get_xceptions(); const vector& x_fields = exceptions->get_members(); @@ -2773,17 +2986,22 @@ void t_go_generator::generate_process_function(t_service* tservice, t_function* } if (!tfunction->is_oneway()) { + // Avoid writing the error to the wire if it's ErrAbandonRequest + f_types_ << indent() << " if err2 == thrift.ErrAbandonRequest {" << endl; + f_types_ << indent() << " return false, thrift.WrapTException(err2)" << endl; + f_types_ << indent() << " }" << endl; + f_types_ << indent() << " x := thrift.NewTApplicationException(thrift.INTERNAL_ERROR, " "\"Internal error processing " << escape_string(tfunction->get_name()) << ": \" + err2.Error())" << endl; - f_types_ << indent() << " oprot.WriteMessageBegin(\"" << escape_string(tfunction->get_name()) + f_types_ << indent() << " oprot.WriteMessageBegin(ctx, \"" << escape_string(tfunction->get_name()) << "\", thrift.EXCEPTION, seqId)" << endl; - f_types_ << indent() << " x.Write(oprot)" << endl; - f_types_ << indent() << " oprot.WriteMessageEnd()" << endl; + f_types_ << indent() << " x.Write(ctx, oprot)" << endl; + f_types_ << indent() << " oprot.WriteMessageEnd(ctx)" << endl; f_types_ << indent() << " oprot.Flush(ctx)" << endl; } - f_types_ << indent() << " return true, err2" << endl; + f_types_ << indent() << " return true, thrift.WrapTException(err2)" << endl; if (!x_fields.empty()) { f_types_ << indent() << "}" << endl; @@ -2801,24 +3019,25 @@ void t_go_generator::generate_process_function(t_service* tservice, t_function* } f_types_ << "retval" << endl; indent_down(); - f_types_ << "}" << endl; + f_types_ << indent() << "}" << endl; } else { f_types_ << endl; } - f_types_ << indent() << "if err2 = oprot.WriteMessageBegin(\"" + f_types_ << indent() << "tickerCancel()" << endl; + f_types_ << indent() << "if err2 = oprot.WriteMessageBegin(ctx, \"" << escape_string(tfunction->get_name()) << "\", thrift.REPLY, seqId); err2 != nil {" << endl; - f_types_ << indent() << " err = err2" << endl; + f_types_ << indent() << " err = thrift.WrapTException(err2)" << endl; f_types_ << indent() << "}" << endl; - f_types_ << indent() << "if err2 = result." << write_method_name_ << "(oprot); err == nil && err2 != nil {" << endl; - f_types_ << indent() << " err = err2" << endl; + f_types_ << indent() << "if err2 = result." << write_method_name_ << "(ctx, oprot); err == nil && err2 != nil {" << endl; + f_types_ << indent() << " err = thrift.WrapTException(err2)" << endl; f_types_ << indent() << "}" << endl; - f_types_ << indent() << "if err2 = oprot.WriteMessageEnd(); err == nil && err2 != nil {" + f_types_ << indent() << "if err2 = oprot.WriteMessageEnd(ctx); err == nil && err2 != nil {" << endl; - f_types_ << indent() << " err = err2" << endl; + f_types_ << indent() << " err = thrift.WrapTException(err2)" << endl; f_types_ << indent() << "}" << endl; f_types_ << indent() << "if err2 = oprot.Flush(ctx); err == nil && err2 != nil {" << endl; - f_types_ << indent() << " err = err2" << endl; + f_types_ << indent() << " err = thrift.WrapTException(err2)" << endl; f_types_ << indent() << "}" << endl; f_types_ << indent() << "if err != nil {" << endl; f_types_ << indent() << " return" << endl; @@ -2826,6 +3045,7 @@ void t_go_generator::generate_process_function(t_service* tservice, t_function* f_types_ << indent() << "return true, err" << endl; } else { f_types_ << endl; + f_types_ << indent() << "tickerCancel()" << endl; f_types_ << indent() << "return true, nil" << endl; } indent_down(); @@ -2882,42 +3102,42 @@ void t_go_generator::generate_deserialize_field(ostream& out, case t_base_type::TYPE_STRING: if (type->is_binary() && !inkey) { - out << "ReadBinary()"; + out << "ReadBinary(ctx)"; } else { - out << "ReadString()"; + out << "ReadString(ctx)"; } break; case t_base_type::TYPE_BOOL: - out << "ReadBool()"; + out << "ReadBool(ctx)"; break; case t_base_type::TYPE_I8: - out << "ReadByte()"; + out << "ReadByte(ctx)"; break; case t_base_type::TYPE_I16: - out << "ReadI16()"; + out << "ReadI16(ctx)"; break; case t_base_type::TYPE_I32: - out << "ReadI32()"; + out << "ReadI32(ctx)"; break; case t_base_type::TYPE_I64: - out << "ReadI64()"; + out << "ReadI64(ctx)"; break; case t_base_type::TYPE_DOUBLE: - out << "ReadDouble()"; + out << "ReadDouble(ctx)"; break; default: throw "compiler error: no Go name for base type " + t_base_type::t_base_name(tbase); } } else if (type->is_enum()) { - out << "ReadI32()"; + out << "ReadI32(ctx)"; } out << "; err != nil {" << endl; @@ -2960,7 +3180,7 @@ void t_go_generator::generate_deserialize_struct(ostream& out, out << indent() << prefix << eq << (pointer_field ? "&" : ""); generate_go_struct_initializer(out, tstruct); - out << indent() << "if err := " << prefix << "." << read_method_name_ << "(iprot); err != nil {" << endl; + out << indent() << "if err := " << prefix << "." << read_method_name_ << "(ctx, iprot); err != nil {" << endl; out << indent() << " return thrift.PrependError(fmt.Sprintf(\"%T error reading struct: \", " << prefix << "), err)" << endl; out << indent() << "}" << endl; @@ -2984,21 +3204,21 @@ void t_go_generator::generate_deserialize_container(ostream& out, // Declare variables, read header if (ttype->is_map()) { - out << indent() << "_, _, size, err := iprot.ReadMapBegin()" << endl; + out << indent() << "_, _, size, err := iprot.ReadMapBegin(ctx)" << endl; out << indent() << "if err != nil {" << endl; out << indent() << " return thrift.PrependError(\"error reading map begin: \", err)" << endl; out << indent() << "}" << endl; out << indent() << "tMap := make(" << type_to_go_type(orig_type) << ", size)" << endl; out << indent() << prefix << eq << " " << (pointer_field ? "&" : "") << "tMap" << endl; } else if (ttype->is_set()) { - out << indent() << "_, size, err := iprot.ReadSetBegin()" << endl; + out << indent() << "_, size, err := iprot.ReadSetBegin(ctx)" << endl; out << indent() << "if err != nil {" << endl; out << indent() << " return thrift.PrependError(\"error reading set begin: \", err)" << endl; out << indent() << "}" << endl; out << indent() << "tSet := make(" << type_to_go_type(orig_type) << ", 0, size)" << endl; out << indent() << prefix << eq << " " << (pointer_field ? "&" : "") << "tSet" << endl; } else if (ttype->is_list()) { - out << indent() << "_, size, err := iprot.ReadListBegin()" << endl; + out << indent() << "_, size, err := iprot.ReadListBegin(ctx)" << endl; out << indent() << "if err != nil {" << endl; out << indent() << " return thrift.PrependError(\"error reading list begin: \", err)" << endl; out << indent() << "}" << endl; @@ -3029,15 +3249,15 @@ void t_go_generator::generate_deserialize_container(ostream& out, // Read container end if (ttype->is_map()) { - out << indent() << "if err := iprot.ReadMapEnd(); err != nil {" << endl; + out << indent() << "if err := iprot.ReadMapEnd(ctx); err != nil {" << endl; out << indent() << " return thrift.PrependError(\"error reading map end: \", err)" << endl; out << indent() << "}" << endl; } else if (ttype->is_set()) { - out << indent() << "if err := iprot.ReadSetEnd(); err != nil {" << endl; + out << indent() << "if err := iprot.ReadSetEnd(ctx); err != nil {" << endl; out << indent() << " return thrift.PrependError(\"error reading set end: \", err)" << endl; out << indent() << "}" << endl; } else if (ttype->is_list()) { - out << indent() << "if err := iprot.ReadListEnd(); err != nil {" << endl; + out << indent() << "if err := iprot.ReadListEnd(ctx); err != nil {" << endl; out << indent() << " return thrift.PrependError(\"error reading list end: \", err)" << endl; out << indent() << "}" << endl; } @@ -3131,42 +3351,42 @@ void t_go_generator::generate_serialize_field(ostream& out, case t_base_type::TYPE_STRING: if (type->is_binary() && !inkey) { - out << "WriteBinary(" << name << ")"; + out << "WriteBinary(ctx, " << name << ")"; } else { - out << "WriteString(string(" << name << "))"; + out << "WriteString(ctx, string(" << name << "))"; } break; case t_base_type::TYPE_BOOL: - out << "WriteBool(bool(" << name << "))"; + out << "WriteBool(ctx, bool(" << name << "))"; break; case t_base_type::TYPE_I8: - out << "WriteByte(int8(" << name << "))"; + out << "WriteByte(ctx, int8(" << name << "))"; break; case t_base_type::TYPE_I16: - out << "WriteI16(int16(" << name << "))"; + out << "WriteI16(ctx, int16(" << name << "))"; break; case t_base_type::TYPE_I32: - out << "WriteI32(int32(" << name << "))"; + out << "WriteI32(ctx, int32(" << name << "))"; break; case t_base_type::TYPE_I64: - out << "WriteI64(int64(" << name << "))"; + out << "WriteI64(ctx, int64(" << name << "))"; break; case t_base_type::TYPE_DOUBLE: - out << "WriteDouble(float64(" << name << "))"; + out << "WriteDouble(ctx, float64(" << name << "))"; break; default: throw "compiler error: no Go name for base type " + t_base_type::t_base_name(tbase); } } else if (type->is_enum()) { - out << "WriteI32(int32(" << name << "))"; + out << "WriteI32(ctx, int32(" << name << "))"; } out << "; err != nil {" << endl; @@ -3187,7 +3407,7 @@ void t_go_generator::generate_serialize_field(ostream& out, */ void t_go_generator::generate_serialize_struct(ostream& out, t_struct* tstruct, string prefix) { (void)tstruct; - out << indent() << "if err := " << prefix << "." << write_method_name_ << "(oprot); err != nil {" << endl; + out << indent() << "if err := " << prefix << "." << write_method_name_ << "(ctx, oprot); err != nil {" << endl; out << indent() << " return thrift.PrependError(fmt.Sprintf(\"%T error writing struct: \", " << prefix << "), err)" << endl; out << indent() << "}" << endl; @@ -3201,20 +3421,20 @@ void t_go_generator::generate_serialize_container(ostream& out, prefix = "*" + prefix; } if (ttype->is_map()) { - out << indent() << "if err := oprot.WriteMapBegin(" + out << indent() << "if err := oprot.WriteMapBegin(ctx, " << type_to_enum(((t_map*)ttype)->get_key_type()) << ", " << type_to_enum(((t_map*)ttype)->get_val_type()) << ", " << "len(" << prefix << ")); err != nil {" << endl; out << indent() << " return thrift.PrependError(\"error writing map begin: \", err)" << endl; out << indent() << "}" << endl; } else if (ttype->is_set()) { - out << indent() << "if err := oprot.WriteSetBegin(" + out << indent() << "if err := oprot.WriteSetBegin(ctx, " << type_to_enum(((t_set*)ttype)->get_elem_type()) << ", " << "len(" << prefix << ")); err != nil {" << endl; out << indent() << " return thrift.PrependError(\"error writing set begin: \", err)" << endl; out << indent() << "}" << endl; } else if (ttype->is_list()) { - out << indent() << "if err := oprot.WriteListBegin(" + out << indent() << "if err := oprot.WriteListBegin(ctx, " << type_to_enum(((t_list*)ttype)->get_elem_type()) << ", " << "len(" << prefix << ")); err != nil {" << endl; out << indent() << " return thrift.PrependError(\"error writing list begin: \", err)" << endl; @@ -3234,11 +3454,30 @@ void t_go_generator::generate_serialize_container(ostream& out, } else if (ttype->is_set()) { t_set* tset = (t_set*)ttype; out << indent() << "for i := 0; iget_elem_type()); + out << indent() << "if func(tgt, src " << goType << ") bool {" << endl; + indent_up(); + generate_go_equals(out, tset->get_elem_type(), "tgt", "src"); + out << indent() << "return true" << endl; + indent_down(); + out << indent() << "}(" << wrapped_prefix << "[i], " << wrapped_prefix << "[j]) {" << endl; + indent_up(); + out << indent() + << "return thrift.PrependError(\"\", fmt.Errorf(\"%T error writing set field: slice is not " + "unique\", " + << wrapped_prefix << "))" << endl; + indent_down(); + out << indent() << "}" << endl; + indent_down(); + out << indent() << "}" << endl; + indent_down(); out << indent() << "}" << endl; out << indent() << "for _, v := range " << prefix << " {" << endl; indent_up(); @@ -3256,15 +3495,15 @@ void t_go_generator::generate_serialize_container(ostream& out, } if (ttype->is_map()) { - out << indent() << "if err := oprot.WriteMapEnd(); err != nil {" << endl; + out << indent() << "if err := oprot.WriteMapEnd(ctx); err != nil {" << endl; out << indent() << " return thrift.PrependError(\"error writing map end: \", err)" << endl; out << indent() << "}" << endl; } else if (ttype->is_set()) { - out << indent() << "if err := oprot.WriteSetEnd(); err != nil {" << endl; + out << indent() << "if err := oprot.WriteSetEnd(ctx); err != nil {" << endl; out << indent() << " return thrift.PrependError(\"error writing set end: \", err)" << endl; out << indent() << "}" << endl; } else if (ttype->is_list()) { - out << indent() << "if err := oprot.WriteListEnd(); err != nil {" << endl; + out << indent() << "if err := oprot.WriteListEnd(ctx); err != nil {" << endl; out << indent() << " return thrift.PrependError(\"error writing list end: \", err)" << endl; out << indent() << "}" << endl; } @@ -3304,6 +3543,111 @@ void t_go_generator::generate_serialize_list_element(ostream& out, t_list* tlist generate_serialize_field(out, &efield, prefix); } +/** + * Compares any type + */ +void t_go_generator::generate_go_equals(ostream& out, t_type* ori_type, string tgt, string src) { + + t_type* ttype = get_true_type(ori_type); + // Do nothing for void types + if (ttype->is_void()) { + throw "compiler error: cannot generate equals for void type: " + tgt; + } + + if (ttype->is_struct() || ttype->is_xception()) { + generate_go_equals_struct(out, ttype, tgt, src); + } else if (ttype->is_container()) { + generate_go_equals_container(out, ttype, tgt, src); + } else if (ttype->is_base_type() || ttype->is_enum()) { + out << indent() << "if "; + if (ttype->is_base_type()) { + t_base_type::t_base tbase = ((t_base_type*)ttype)->get_base(); + switch (tbase) { + case t_base_type::TYPE_VOID: + throw "compiler error: cannot equals void: " + tgt; + break; + + case t_base_type::TYPE_STRING: + if (ttype->is_binary()) { + out << "bytes.Compare(" << tgt << ", " << src << ") != 0"; + } else { + out << tgt << " != " << src; + } + break; + + case t_base_type::TYPE_BOOL: + case t_base_type::TYPE_I8: + case t_base_type::TYPE_I16: + case t_base_type::TYPE_I32: + case t_base_type::TYPE_I64: + case t_base_type::TYPE_DOUBLE: + out << tgt << " != " << src; + break; + + default: + throw "compiler error: no Go name for base type " + t_base_type::t_base_name(tbase); + } + } else if (ttype->is_enum()) { + out << tgt << " != " << src; + } + + out << " { return false }" << endl; + } else { + throw "compiler error: Invalid type in generate_go_equals '" + ttype->get_name() + "' for '" + + tgt + "'"; + } +} + +/** + * Compares the members of a struct + */ +void t_go_generator::generate_go_equals_struct(ostream& out, + t_type* ttype, + string tgt, + string src) { + (void)ttype; + out << indent() << "if !" << tgt << "." << equals_method_name_ << "(" << src + << ") { return false }" << endl; +} + +/** + * Compares any container type + */ +void t_go_generator::generate_go_equals_container(ostream& out, + t_type* ttype, + string tgt, + string src) { + out << indent() << "if len(" << tgt << ") != len(" << src << ") { return false }" << endl; + if (ttype->is_map()) { + t_map* tmap = (t_map*)ttype; + out << indent() << "for k, _tgt := range " << tgt << " {" << endl; + indent_up(); + string element_source = tmp("_src"); + out << indent() << element_source << " := " << src << "[k]" << endl; + generate_go_equals(out, tmap->get_val_type(), "_tgt", element_source); + indent_down(); + indent(out) << "}" << endl; + } else if (ttype->is_list() || ttype->is_set()) { + t_type* elem; + if (ttype->is_list()) { + t_list* temp = (t_list*)ttype; + elem = temp->get_elem_type(); + } else { + t_set* temp = (t_set*)ttype; + elem = temp->get_elem_type(); + } + out << indent() << "for i, _tgt := range " << tgt << " {" << endl; + indent_up(); + string element_source = tmp("_src"); + out << indent() << element_source << " := " << src << "[i]" << endl; + generate_go_equals(out, elem, "_tgt", element_source); + indent_down(); + indent(out) << "}" << endl; + } else { + throw "INVALID TYPE IN generate_go_equals_container '" + ttype->get_name(); + } +} + /** * Generates the docstring for a given struct. */ @@ -3379,7 +3723,7 @@ string t_go_generator::declare_argument(t_field* tfield) { std::ostringstream result; result << publicize(tfield->get_name()) << "="; - if (tfield->get_value() != NULL) { + if (tfield->get_value() != nullptr) { result << "thrift_spec[" << tfield->get_key() << "][4]"; } else { result << "nil"; @@ -3391,7 +3735,7 @@ string t_go_generator::declare_argument(t_field* tfield) { /** * Renders a struct field initial value. * - * @param tfield The field, which must have `tfield->get_value() != NULL` + * @param tfield The field, which must have `tfield->get_value() != nullptr` */ string t_go_generator::render_field_initial_value(t_field* tfield, const string& name, @@ -3493,17 +3837,12 @@ string t_go_generator::type_name(t_type* ttype) { string t_go_generator::module_name(t_type* ttype) { t_program* program = ttype->get_program(); - if (program != NULL && program != program_) { + if (program != nullptr && program != program_) { if (program->get_namespace("go").empty() || program_->get_namespace("go").empty() || program->get_namespace("go") != program_->get_namespace("go")) { string module(get_real_go_module(program)); - // for namespaced includes, only keep part after dot. - size_t dot = module.rfind('.'); - if (dot != string::npos) { - module = module.substr(dot + 1); - } - return module; + return package_identifiers_[module]; } } @@ -3686,6 +4025,48 @@ string t_go_generator::type_to_spec_args(t_type* ttype) { throw "INVALID TYPE IN type_to_spec_args: " + ttype->get_name(); } +// parses a string of struct tags into key/value pairs and writes them to the given map +void t_go_generator::parse_go_tags(map* tags, const string in) { + string key; + string value; + + size_t mode=0; // 0/1/2 for key/value/whitespace + size_t index=0; + for (auto it=in.begin(); itget_name() << ">"; f_out_ << (*arg_iter)->get_name(); - if ((*arg_iter)->get_value() != NULL) { + if ((*arg_iter)->get_value() != nullptr) { f_out_ << " = "; print_const_value((*arg_iter)->get_type(), (*arg_iter)->get_value()); } diff --git a/compiler/cpp/src/thrift/generate/t_haxe_generator.cc b/compiler/cpp/src/thrift/generate/t_haxe_generator.cc index 3b88c4da79b..7f1bb6f758b 100644 --- a/compiler/cpp/src/thrift/generate/t_haxe_generator.cc +++ b/compiler/cpp/src/thrift/generate/t_haxe_generator.cc @@ -74,20 +74,20 @@ class t_haxe_generator : public t_oop_generator { * Init and close methods */ - void init_generator(); - void close_generator(); + void init_generator() override; + void close_generator() override; - void generate_consts(std::vector consts); + void generate_consts(std::vector consts) override; /** * Program-level generation functions */ - void generate_typedef(t_typedef* ttypedef); - void generate_enum(t_enum* tenum); - void generate_struct(t_struct* tstruct); - void generate_xception(t_struct* txception); - void generate_service(t_service* tservice); + void generate_typedef(t_typedef* ttypedef) override; + void generate_enum(t_enum* tenum) override; + void generate_struct(t_struct* tstruct) override; + void generate_xception(t_struct* txception) override; + void generate_service(t_service* tservice) override; void print_const_value(std::ostream& out, std::string name, @@ -190,7 +190,7 @@ class t_haxe_generator : public t_oop_generator { std::string function_signature_normal(t_function* tfunction); std::string argument_list(t_struct* tstruct); std::string type_to_enum(t_type* ttype); - std::string get_enum_class_name(t_type* type); + std::string get_enum_class_name(t_type* type) override; string generate_service_method_onsuccess(t_function* tfunction, bool as_type, bool omit_name); void generate_service_method_signature_callback(t_function* tfunction, bool is_interface); void generate_service_method_signature_normal(t_function* tfunction, bool is_interface); @@ -319,7 +319,7 @@ string t_haxe_generator::haxe_thrift_gen_imports(t_struct* tstruct, string& impo // For each type check if it is from a different namespace for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) { t_program* program = (*m_iter)->get_type()->get_program(); - if (program != NULL && program != program_) { + if (program != nullptr && program != program_) { string package = program->get_namespace("haxe"); if (!package.empty()) { if (imports.find(package + "." + (*m_iter)->get_type()->get_name()) == string::npos) { @@ -344,7 +344,7 @@ string t_haxe_generator::haxe_thrift_gen_imports(t_service* tservice) { // For each type check if it is from a different namespace for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) { t_program* program = (*f_iter)->get_returntype()->get_program(); - if (program != NULL && program != program_) { + if (program != nullptr && program != program_) { string package = program->get_namespace("haxe"); if (!package.empty()) { if (imports.find(package + "." + (*f_iter)->get_returntype()->get_name()) == string::npos) { @@ -514,13 +514,13 @@ void t_haxe_generator::print_const_value(std::ostream& out, indent_up(); } for (v_iter = val.begin(); v_iter != val.end(); ++v_iter) { - t_type* field_type = NULL; + t_type* field_type = nullptr; for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { if ((*f_iter)->get_name() == v_iter->first->get_string()) { field_type = (*f_iter)->get_type(); } } - if (field_type == NULL) { + if (field_type == nullptr) { throw "type error: " + type->get_name() + " has no field " + v_iter->first->get_string(); } string val = render_const_value(out, name, field_type, v_iter->second); @@ -788,7 +788,7 @@ void t_haxe_generator::generate_haxe_struct_definition(ostream& out, indent(out) << "super();" << endl; } for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) { - if ((*m_iter)->get_value() != NULL) { + if ((*m_iter)->get_value() != nullptr) { indent(out) << "this." << (*m_iter)->get_name() << " = " << (*m_iter)->get_value()->get_integer() << ";" << endl; } @@ -1483,7 +1483,7 @@ void t_haxe_generator::generate_service(t_service* tservice) { f_service_ << endl << haxe_type_imports() << haxe_thrift_imports() << haxe_thrift_gen_imports(tservice); - if (tservice->get_extends() != NULL) { + if (tservice->get_extends() != nullptr) { t_type* parent = tservice->get_extends(); string parent_namespace = parent->get_program()->get_namespace("haxe"); if (!parent_namespace.empty() && parent_namespace != package_name_) { @@ -1504,7 +1504,7 @@ void t_haxe_generator::generate_service(t_service* tservice) { f_service_ << autogen_comment() << haxe_package() << ";" << endl << endl << haxe_type_imports() << haxe_thrift_imports() << haxe_thrift_gen_imports(tservice) << endl; - if (tservice->get_extends() != NULL) { + if (tservice->get_extends() != nullptr) { t_type* parent = tservice->get_extends(); string parent_namespace = parent->get_program()->get_namespace("haxe"); if (!parent_namespace.empty() && parent_namespace != package_name_) { @@ -1629,7 +1629,7 @@ void t_haxe_generator::generate_service_method_signature_callback(t_function* tf */ void t_haxe_generator::generate_service_interface(t_service* tservice) { string extends_iface = ""; - if (tservice->get_extends() != NULL) { + if (tservice->get_extends() != nullptr) { extends_iface = " extends " + tservice->get_extends()->get_name(); } @@ -1674,7 +1674,7 @@ void t_haxe_generator::generate_service_helpers(t_service* tservice) { void t_haxe_generator::generate_service_client(t_service* tservice) { string extends = ""; string extends_client = ""; - if (tservice->get_extends() != NULL) { + if (tservice->get_extends() != nullptr) { extends = get_cap_name(tservice->get_extends()->get_name()); extends_client = " extends " + extends + "Impl"; } @@ -1891,7 +1891,7 @@ void t_haxe_generator::generate_service_server(t_service* tservice) { // Extends stuff string extends = ""; string extends_processor = ""; - if (tservice->get_extends() != NULL) { + if (tservice->get_extends() != nullptr) { extends = get_cap_name(type_name(tservice->get_extends())); extends_processor = " extends " + extends + "Processor"; } @@ -1932,7 +1932,7 @@ void t_haxe_generator::generate_service_server(t_service* tservice) { // Generate the server implementation string override = ""; - if (tservice->get_extends() != NULL) { + if (tservice->get_extends() != nullptr) { override = "override "; } indent(f_service_) << override @@ -2606,7 +2606,7 @@ string t_haxe_generator::type_name(t_type* ttype, bool in_container, bool in_ini // Check for namespacing t_program* program = ttype->get_program(); - if (program != NULL && program != program_) { + if (program != nullptr && program != program_) { string package = program->get_namespace("haxe"); if (!package.empty()) { return package + "." + ttype->get_name(); @@ -2660,7 +2660,7 @@ string t_haxe_generator::declare_field(t_field* tfield, bool init) { string result = "var " + tfield->get_name() + " : " + type_name(tfield->get_type()); if (init) { t_type* ttype = get_true_type(tfield->get_type()); - if (ttype->is_base_type() && tfield->get_value() != NULL) { + if (ttype->is_base_type() && tfield->get_value() != nullptr) { std::ofstream dummy; result += " = " + render_const_value(dummy, tfield->get_name(), ttype, tfield->get_value()); } else if (ttype->is_base_type()) { @@ -2884,9 +2884,7 @@ string t_haxe_generator::constant_name(string name) { bool is_first = true; bool was_previous_char_upper = false; - for (string::iterator iter = name.begin(); iter != name.end(); ++iter) { - string::value_type character = (*iter); - + for (char character : name) { bool is_upper = isupper(character); if (is_upper && !is_first && !was_previous_char_upper) { @@ -2968,7 +2966,7 @@ void t_haxe_generator::generate_isset_set(ostream& out, t_field* field) { std::string t_haxe_generator::get_enum_class_name(t_type* type) { string package = ""; t_program* program = type->get_program(); - if (program != NULL /*&& program != program_*/) { + if (program != nullptr /*&& program != program_*/) { package = program->get_namespace("haxe") + "."; } return package + type->get_name(); diff --git a/compiler/cpp/src/thrift/generate/t_hs_generator.cc b/compiler/cpp/src/thrift/generate/t_hs_generator.cc index ce7cd0c76c9..d314b8f2c21 100644 --- a/compiler/cpp/src/thrift/generate/t_hs_generator.cc +++ b/compiler/cpp/src/thrift/generate/t_hs_generator.cc @@ -66,18 +66,18 @@ class t_hs_generator : public t_oop_generator { * Init and close methods */ - void init_generator(); - void close_generator(); + void init_generator() override; + void close_generator() override; /** * Program-level generation functions */ - void generate_typedef(t_typedef* ttypedef); - void generate_enum(t_enum* tenum); - void generate_const(t_const* tconst); - void generate_struct(t_struct* tstruct); - void generate_xception(t_struct* txception); - void generate_service(t_service* tservice); + void generate_typedef(t_typedef* ttypedef) override; + void generate_enum(t_enum* tenum) override; + void generate_const(t_const* tconst) override; + void generate_struct(t_struct* tstruct) override; + void generate_xception(t_struct* txception) override; + void generate_service(t_service* tservice) override; string render_const_value(t_type* type, t_const_value* value); @@ -261,8 +261,8 @@ string t_hs_generator::hs_imports() { "import qualified Thrift.Arbitraries as T\n" "\n"); - for (size_t i = 0; i < includes.size(); ++i) - result += "import qualified " + capitalize(includes[i]->get_name()) + "_Types\n"; + for (auto include : includes) + result += "import qualified " + capitalize(include->get_name()) + "_Types\n"; if (includes.size() > 0) result += "\n"; @@ -305,7 +305,7 @@ void t_hs_generator::generate_enum(t_enum* tenum) { bool first = true; for (c_iter = constants.begin(); c_iter != constants.end(); ++c_iter) { - string name = capitalize((*c_iter)->get_name()); + string name = capitalize(tenum->get_name()) + "_" + capitalize((*c_iter)->get_name()); f_types_ << (first ? "" : "|"); f_types_ << name; first = false; @@ -321,7 +321,7 @@ void t_hs_generator::generate_enum(t_enum* tenum) { indent_up(); for (c_iter = constants.begin(); c_iter != constants.end(); ++c_iter) { int value = (*c_iter)->get_value(); - string name = capitalize((*c_iter)->get_name()); + string name = capitalize(tenum->get_name()) + "_" + capitalize((*c_iter)->get_name()); indent(f_types_) << name << " -> " << value << endl; } indent_down(); @@ -329,7 +329,7 @@ void t_hs_generator::generate_enum(t_enum* tenum) { indent_up(); for (c_iter = constants.begin(); c_iter != constants.end(); ++c_iter) { int value = (*c_iter)->get_value(); - string name = capitalize((*c_iter)->get_name()); + string name = capitalize(tenum->get_name()) + "_" + capitalize((*c_iter)->get_name()); indent(f_types_) << value << " -> " << name << endl; } indent(f_types_) << "_ -> X.throw T.ThriftException" << endl; @@ -367,7 +367,7 @@ void t_hs_generator::generate_const(t_const* tconst) { * validate_types method in main.cc */ string t_hs_generator::render_const_value(t_type* type, t_const_value* value) { - if (value == NULL) + if (value == nullptr) return type_to_default(type); type = get_true_type(type); @@ -407,14 +407,13 @@ string t_hs_generator::render_const_value(t_type* type, t_const_value* value) { } else if (type->is_enum()) { t_enum* tenum = (t_enum*)type; vector constants = tenum->get_constants(); - for (vector::iterator c_iter = constants.begin(); c_iter != constants.end(); - ++c_iter) { - int val = (*c_iter)->get_value(); + for (auto & constant : constants) { + int val = constant->get_value(); if (val == value->get_integer()) { t_program* prog = type->get_program(); - if (prog != NULL && prog != program_) + if (prog != nullptr && prog != program_) out << capitalize(prog->get_name()) << "_Types."; - out << capitalize((*c_iter)->get_name()); + out << capitalize(constant->get_name()); break; } } @@ -427,21 +426,18 @@ string t_hs_generator::render_const_value(t_type* type, t_const_value* value) { const map& val = value->get_map(); bool first = true; - for (map::const_iterator v_iter = val.begin(); - v_iter != val.end(); - ++v_iter) { - t_field* field = NULL; + for (auto v_iter : val) { + t_field* field = nullptr; - for (vector::const_iterator f_iter = fields.begin(); f_iter != fields.end(); - ++f_iter) - if ((*f_iter)->get_name() == v_iter->first->get_string()) - field = (*f_iter); + for (auto f_iter : fields) + if (f_iter->get_name() == v_iter.first->get_string()) + field = f_iter; - if (field == NULL) - throw "type error: " + cname + " has no field " + v_iter->first->get_string(); + if (field == nullptr) + throw "type error: " + cname + " has no field " + v_iter.first->get_string(); - string fname = v_iter->first->get_string(); - string const_value = render_const_value(field->get_type(), v_iter->second); + string fname = v_iter.first->get_string(); + string const_value = render_const_value(field->get_type(), v_iter.second); out << (first ? "" : ", "); out << field_name(cname, fname) << " = "; @@ -543,21 +539,20 @@ void t_hs_generator::generate_hs_struct_definition(ostream& out, if (members.size() > 0) { indent_up(); bool first = true; - for (vector::const_iterator m_iter = members.begin(); m_iter != members.end(); - ++m_iter) { + for (auto member : members) { if (first) { indent(out) << "{ "; first = false; } else { indent(out) << ", "; } - string mname = (*m_iter)->get_name(); + string mname = member->get_name(); out << field_name(tname, mname) << " :: "; - if ((*m_iter)->get_req() == t_field::T_OPTIONAL - || ((t_type*)(*m_iter)->get_type())->is_xception()) { + if (member->get_req() == t_field::T_OPTIONAL + || ((t_type*)member->get_type())->is_xception()) { out << "P.Maybe "; } - out << render_hs_type((*m_iter)->get_type(), true) << endl; + out << render_hs_type(member->get_type(), true) << endl; } indent(out) << "}"; indent_down(); @@ -571,9 +566,8 @@ void t_hs_generator::generate_hs_struct_definition(ostream& out, indent(out) << "instance H.Hashable " << tname << " where" << endl; indent_up(); indent(out) << "hashWithSalt salt record = salt"; - for (vector::const_iterator m_iter = members.begin(); m_iter != members.end(); - ++m_iter) { - string mname = (*m_iter)->get_name(); + for (auto member : members) { + string mname = member->get_name(); indent(out) << " `H.hashWithSalt` " << field_name(tname, mname) << " record"; } indent(out) << endl; @@ -590,7 +584,6 @@ void t_hs_generator::generate_hs_struct_arbitrary(ostream& out, t_struct* tstruc string tname = type_name(tstruct); string name = tstruct->get_name(); const vector& members = tstruct->get_members(); - vector::const_iterator m_iter; indent(out) << "instance QC.Arbitrary " << tname << " where " << endl; indent_up(); @@ -601,8 +594,7 @@ void t_hs_generator::generate_hs_struct_arbitrary(ostream& out, t_struct* tstruc indent_up(); indent_up(); bool first = true; - for (vector::const_iterator m_iter = members.begin(); m_iter != members.end(); - ++m_iter) { + for (auto member : members) { if (first) { first = false; out << " "; @@ -610,8 +602,8 @@ void t_hs_generator::generate_hs_struct_arbitrary(ostream& out, t_struct* tstruc indent(out) << "`M.ap`"; } out << "("; - if ((*m_iter)->get_req() == t_field::T_OPTIONAL - || ((t_type*)(*m_iter)->get_type())->is_xception()) { + if (member->get_req() == t_field::T_OPTIONAL + || ((t_type*)member->get_type())->is_xception()) { out << "M.liftM P.Just "; } out << "QC.arbitrary)" << endl; @@ -626,15 +618,14 @@ void t_hs_generator::generate_hs_struct_arbitrary(ostream& out, t_struct* tstruc indent(out) << " | P.otherwise = M.catMaybes" << endl; indent_up(); first = true; - for (vector::const_iterator m_iter = members.begin(); m_iter != members.end(); - ++m_iter) { + for (auto member : members) { if (first) { first = false; indent(out) << "[ "; } else { indent(out) << ", "; } - string fname = field_name(tname, (*m_iter)->get_name()); + string fname = field_name(tname, member->get_name()); out << "if obj == default_" << tname; out << "{" << fname << " = " << fname << " obj} "; out << "then P.Nothing "; @@ -654,7 +645,6 @@ void t_hs_generator::generate_hs_struct_arbitrary(ostream& out, t_struct* tstruc */ void t_hs_generator::generate_hs_struct_reader(ostream& out, t_struct* tstruct) { const vector& fields = tstruct->get_members(); - vector::const_iterator f_iter; string sname = type_name(tstruct); string id = tmp("_id"); @@ -667,10 +657,10 @@ void t_hs_generator::generate_hs_struct_reader(ostream& out, t_struct* tstruct) bool first = true; // Generate deserialization code for known cases - for (vector::const_iterator f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { - int32_t key = (*f_iter)->get_key(); - string etype = type_to_enum((*f_iter)->get_type()); - string fname = (*f_iter)->get_name(); + for (auto field : fields) { + int32_t key = field->get_key(); + string etype = type_to_enum(field->get_type()); + string fname = field->get_name(); if (first) { first = false; @@ -682,11 +672,11 @@ void t_hs_generator::generate_hs_struct_reader(ostream& out, t_struct* tstruct) indent(out) << field_name(sname, fname) << " = "; out << "P.maybe ("; - if ((*f_iter)->get_req() == t_field::T_REQUIRED) { + if (field->get_req() == t_field::T_REQUIRED) { out << "P.error \"Missing required field: " << fname << "\""; } else { - if (((*f_iter)->get_req() == t_field::T_OPTIONAL - || ((t_type*)(*f_iter)->get_type())->is_xception()) && (*f_iter)->get_value() == NULL) { + if ((field->get_req() == t_field::T_OPTIONAL + || ((t_type*)field->get_type())->is_xception()) && field->get_value() == nullptr) { out << "P.Nothing"; } else { out << field_name(sname, fname) << " default_" << sname; @@ -695,10 +685,10 @@ void t_hs_generator::generate_hs_struct_reader(ostream& out, t_struct* tstruct) out << ") "; out << "(\\(_," << val << ") -> "; - if ((*f_iter)->get_req() == t_field::T_OPTIONAL - || ((t_type*)(*f_iter)->get_type())->is_xception()) + if (field->get_req() == t_field::T_OPTIONAL + || ((t_type*)field->get_type())->is_xception()) out << "P.Just "; - generate_deserialize_field(out, *f_iter, val); + generate_deserialize_field(out, field, val); out << ")"; out << " (Map.lookup (" << key << ") fields)"; } @@ -725,7 +715,6 @@ void t_hs_generator::generate_hs_struct_reader(ostream& out, t_struct* tstruct) void t_hs_generator::generate_hs_struct_writer(ostream& out, t_struct* tstruct) { string name = type_name(tstruct); const vector& fields = tstruct->get_sorted_members(); - vector::const_iterator f_iter; string str = tmp("_str"); string f = tmp("_f"); string v = tmp("_v"); @@ -736,8 +725,8 @@ void t_hs_generator::generate_hs_struct_writer(ostream& out, t_struct* tstruct) // Get Exceptions bool hasExn = false; - for (vector::const_iterator f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { - if (((t_type*)(*f_iter)->get_type())->is_xception()) { + for (auto field : fields) { + if (((t_type*)field->get_type())->is_xception()) { hasExn = true; break; } @@ -748,19 +737,18 @@ void t_hs_generator::generate_hs_struct_writer(ostream& out, t_struct* tstruct) out << endl; indent(out) << "(let exns = M.catMaybes "; indent_up(); - for (vector::const_iterator f_iter = fields.begin(); f_iter != fields.end(); - ++f_iter) { - if (((t_type*)(*f_iter)->get_type())->is_xception()) { + for (auto field : fields) { + if (((t_type*)field->get_type())->is_xception()) { if (isfirst) { out << "[ "; isfirst = false; } else { out << ", "; } - string mname = (*f_iter)->get_name(); - int32_t key = (*f_iter)->get_key(); + string mname = field->get_name(); + int32_t key = field->get_key(); out << "(\\" << v << " -> (" << key << ", (\"" << mname << "\","; - generate_serialize_type(out, (*f_iter)->get_type(), v); + generate_serialize_type(out, field->get_type(), v); out << "))) <$> " << field_name(name, mname) << " record"; } } @@ -777,7 +765,7 @@ void t_hs_generator::generate_hs_struct_writer(ostream& out, t_struct* tstruct) out << "M.catMaybes" << endl; // Get the Rest isfirst = true; - for (vector::const_iterator f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { + for (auto field : fields) { // Write field header if (isfirst) { indent(out) << "[ "; @@ -785,19 +773,19 @@ void t_hs_generator::generate_hs_struct_writer(ostream& out, t_struct* tstruct) } else { indent(out) << ", "; } - string mname = (*f_iter)->get_name(); - int32_t key = (*f_iter)->get_key(); + string mname = field->get_name(); + int32_t key = field->get_key(); out << "(\\"; out << v << " -> "; - if ((*f_iter)->get_req() != t_field::T_OPTIONAL - && !((t_type*)(*f_iter)->get_type())->is_xception()) { + if (field->get_req() != t_field::T_OPTIONAL + && !((t_type*)field->get_type())->is_xception()) { out << "P.Just "; } out << "(" << key << ", (\"" << mname << "\","; - generate_serialize_type(out, (*f_iter)->get_type(), v); + generate_serialize_type(out, field->get_type(), v); out << "))) "; - if ((*f_iter)->get_req() != t_field::T_OPTIONAL - && !((t_type*)(*f_iter)->get_type())->is_xception()) { + if (field->get_req() != t_field::T_OPTIONAL + && !((t_type*)field->get_type())->is_xception()) { out << "$"; } else { out << "<$>"; @@ -909,19 +897,18 @@ void t_hs_generator::generate_hs_function_helpers(t_function* tfunction) { void t_hs_generator::generate_hs_typemap(ostream& out, t_struct* tstruct) { string name = type_name(tstruct); const vector& fields = tstruct->get_sorted_members(); - vector::const_iterator f_iter; indent(out) << "typemap_" << name << " :: T.TypeMap" << endl; indent(out) << "typemap_" << name << " = Map.fromList ["; bool first = true; - for (vector::const_iterator f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { - string mname = (*f_iter)->get_name(); + for (auto field : fields) { + string mname = field->get_name(); if (!first) { out << ","; } - t_type* type = get_true_type((*f_iter)->get_type()); - int32_t key = (*f_iter)->get_key(); + t_type* type = get_true_type(field->get_type()); + int32_t key = field->get_key(); out << "(" << key << ",(\"" << mname << "\"," << type_to_enum(type) << "))"; first = false; } @@ -941,20 +928,20 @@ void t_hs_generator::generate_hs_default(ostream& out, t_struct* tstruct) { indent(out) << fname << " = " << name << "{" << endl; indent_up(); bool first = true; - for (vector::const_iterator f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { - string mname = (*f_iter)->get_name(); + for (auto field : fields) { + string mname = field->get_name(); if (first) { first = false; } else { out << "," << endl; } - t_type* type = get_true_type((*f_iter)->get_type()); - t_const_value* value = (*f_iter)->get_value(); + t_type* type = get_true_type(field->get_type()); + t_const_value* value = field->get_value(); indent(out) << field_name(name, mname) << " = "; - if ((*f_iter)->get_req() == t_field::T_OPTIONAL - || ((t_type*)(*f_iter)->get_type())->is_xception()) { - if (value == NULL) { + if (field->get_req() == t_field::T_OPTIONAL + || ((t_type*)field->get_type())->is_xception()) { + if (value == nullptr) { out << "P.Nothing"; } else { out << "P.Just " << render_const_value(type, value); @@ -986,7 +973,7 @@ void t_hs_generator::generate_service_interface(t_service* tservice) { f_iface_ << endl; string sname = capitalize(service_name_); - if (tservice->get_extends() != NULL) { + if (tservice->get_extends() != nullptr) { string extends = type_name(tservice->get_extends()); indent(f_iface_) << "import " << extends << "_Iface" << endl; @@ -1039,7 +1026,7 @@ void t_hs_generator::generate_service_client(t_service* tservice) { string sname = capitalize(service_name_); indent(f_client_) << "module " << sname << "_Client(" << exports << ") where" << endl; - if (tservice->get_extends() != NULL) { + if (tservice->get_extends() != nullptr) { extends = type_name(tservice->get_extends()); indent(f_client_) << "import " << extends << "_Client" << endl; } @@ -1091,13 +1078,12 @@ void t_hs_generator::generate_service_client(t_service* tservice) { indent(f_client_) << "write_" << argsname << " op (" << argsname << "{"; bool first = true; - for (vector::const_iterator fld_iter = fields.begin(); fld_iter != fields.end(); - ++fld_iter) { - string fieldname = (*fld_iter)->get_name(); + for (auto field : fields) { + string fieldname = field->get_name(); f_client_ << (first ? "" : ","); f_client_ << field_name(argsname, fieldname) << "="; - if ((*fld_iter)->get_req() == t_field::T_OPTIONAL - || ((t_type*)(*fld_iter)->get_type())->is_xception()) + if (field->get_req() == t_field::T_OPTIONAL + || ((t_type*)field->get_type())->is_xception()) f_client_ << "P.Just "; f_client_ << "arg_" << fieldname; first = false; @@ -1127,10 +1113,9 @@ void t_hs_generator::generate_service_client(t_service* tservice) { t_struct* xs = (*f_iter)->get_xceptions(); const vector& xceptions = xs->get_members(); - for (vector::const_iterator x_iter = xceptions.begin(); x_iter != xceptions.end(); - ++x_iter) { + for (auto xception : xceptions) { indent(f_client_) << "P.maybe (P.return ()) X.throw (" - << field_name(resultname, (*x_iter)->get_name()) << " res)" << endl; + << field_name(resultname, xception->get_name()) << " res)" << endl; } if (!(*f_iter)->get_returntype()->is_void()) @@ -1171,7 +1156,7 @@ void t_hs_generator::generate_service_server(t_service* tservice) { } indent(f_service_) << "_ -> "; - if (tservice->get_extends() != NULL) { + if (tservice->get_extends() != nullptr) { f_service_ << type_name(tservice->get_extends()) << ".proc_ handler (iprot,oprot) (name,typ,seqid)" << endl; @@ -1503,11 +1488,11 @@ string t_hs_generator::function_type(t_function* tfunc, bool options, bool io, b string result = ""; const vector& fields = tfunc->get_arglist()->get_members(); - for (vector::const_iterator f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { - if ((*f_iter)->get_req() == t_field::T_OPTIONAL - || ((t_type*)(*f_iter)->get_type())->is_xception()) + for (auto field : fields) { + if (field->get_req() == t_field::T_OPTIONAL + || ((t_type*)field->get_type())->is_xception()) result += "P.Maybe "; - result += render_hs_type((*f_iter)->get_type(), options); + result += render_hs_type(field->get_type(), options); result += " -> "; } @@ -1525,7 +1510,7 @@ string t_hs_generator::type_name(t_type* ttype, string function_prefix) { string prefix = ""; t_program* program = ttype->get_program(); - if (program != NULL && program != program_) + if (program != nullptr && program != program_) if (!ttype->is_service()) prefix = capitalize(program->get_name()) + "_Types."; diff --git a/compiler/cpp/src/thrift/generate/t_html_generator.cc b/compiler/cpp/src/thrift/generate/t_html_generator.cc index 8dfa3897ebf..3bff2c95af0 100644 --- a/compiler/cpp/src/thrift/generate/t_html_generator.cc +++ b/compiler/cpp/src/thrift/generate/t_html_generator.cc @@ -82,7 +82,7 @@ class t_html_generator : public t_generator { init_allowed__markup(); } - void generate_program(); + void generate_program() override; void generate_program_toc(); void generate_program_toc_row(t_program* tprog); void generate_program_toc_rows(t_program* tprog, std::vector& finished); @@ -101,12 +101,12 @@ class t_html_generator : public t_generator { * Program-level generation functions */ - void generate_typedef(t_typedef* ttypedef); - void generate_enum(t_enum* tenum); - void generate_const(t_const* tconst); - void generate_struct(t_struct* tstruct); - void generate_service(t_service* tservice); - void generate_xception(t_struct* txception); + void generate_typedef(t_typedef* ttypedef) override; + void generate_enum(t_enum* tenum) override; + void generate_const(t_const* tconst) override; + void generate_struct(t_struct* tstruct) override; + void generate_service(t_service* tservice) override; + void generate_xception(t_struct* txception) override; void print_doc(t_doc* tdoc); int print_type(t_type* ttype); @@ -127,10 +127,10 @@ class t_html_generator : public t_generator { */ void t_html_generator::generate_program_toc() { f_out_ << "" - << "" << endl; + "table-condensed\">" + << "" << endl; generate_program_toc_row(program_); - f_out_ << "
ModuleServicesData typesConstants
ModuleServicesData typesConstants
" << endl; + f_out_ << "" << endl; } /** @@ -140,16 +140,16 @@ void t_html_generator::generate_program_toc() { */ void t_html_generator::generate_program_toc_rows(t_program* tprog, std::vector& finished) { - for (vector::iterator iter = finished.begin(); iter != finished.end(); iter++) { - if (tprog->get_path() == (*iter)->get_path()) { + for (auto & iter : finished) { + if (tprog->get_path() == iter->get_path()) { return; } } finished.push_back(tprog); generate_program_toc_row(tprog); vector includes = tprog->get_includes(); - for (vector::iterator iter = includes.begin(); iter != includes.end(); iter++) { - generate_program_toc_rows(*iter, finished); + for (auto & include : includes) { + generate_program_toc_rows(include, finished); } } @@ -176,9 +176,8 @@ void t_html_generator::generate_program_toc_row(t_program* tprog) { + "\">" + fn_name + ""; fn_html.insert(pair(fn_name, html)); } - for (map::iterator html_iter = fn_html.begin(); html_iter != fn_html.end(); - html_iter++) { - f_out_ << html_iter->second << endl; + for (auto & html_iter : fn_html) { + f_out_ << html_iter.second << endl; } f_out_ << "" << endl; } @@ -220,9 +219,8 @@ void t_html_generator::generate_program_toc_row(t_program* tprog) { data_types.insert(pair(name, html)); } } - for (map::iterator dt_iter = data_types.begin(); dt_iter != data_types.end(); - dt_iter++) { - f_out_ << dt_iter->second << "
" << endl; + for (auto & data_type : data_types) { + f_out_ << data_type.second << "
" << endl; } f_out_ << "" << endl << ""; if (!tprog->get_consts().empty()) { @@ -235,12 +233,11 @@ void t_html_generator::generate_program_toc_row(t_program* tprog) { + ""; const_html.insert(pair(name, html)); } - for (map::iterator con_iter = const_html.begin(); con_iter != const_html.end(); - con_iter++) { - f_out_ << con_iter->second << "
" << endl; + for (auto & con_iter : const_html) { + f_out_ << con_iter.second << "
" << endl; } } - f_out_ << "" << endl << ""; + f_out_ << "" << endl << ""; } /** @@ -253,9 +250,8 @@ void t_html_generator::generate_program() { current_file_ = program_->get_name() + ".html"; string fname = get_out_dir() + current_file_; f_out_.open(fname.c_str()); - f_out_ << "" << endl; - f_out_ << "" << endl; + f_out_ << "" << endl; + f_out_ << "" << endl; f_out_ << "" << endl; f_out_ << "" << endl; generate_style_tag(); @@ -271,9 +267,9 @@ void t_html_generator::generate_program() { f_out_ << "

Constants

" << endl; vector consts = program_->get_consts(); f_out_ << ""; - f_out_ << "" << endl; + f_out_ << "" << endl; generate_consts(consts); - f_out_ << "
ConstantTypeValue
ConstantTypeValue
"; + f_out_ << ""; } if (!program_->get_enums().empty()) { @@ -335,16 +331,16 @@ void t_html_generator::generate_index() { current_file_ = "index.html"; string index_fname = get_out_dir() + current_file_; f_out_.open(index_fname.c_str()); - f_out_ << "" << endl; + f_out_ << "" << endl << "" << endl; generate_style_tag(); f_out_ << "All Thrift declarations" << endl << "
" << endl << "

All Thrift declarations

" << endl; f_out_ << "" - << "" << endl; + "table-condensed\">" + << "" << endl; vector programs; generate_program_toc_rows(program_, programs); - f_out_ << "
ModuleServicesData typesConstants
ModuleServicesData typesConstants
" << endl; + f_out_ << "" << endl; f_out_ << "
" << endl; f_out_.close(); } @@ -573,8 +569,8 @@ std::string t_html_generator::escape_html_tags(std::string const& str) { if (first_white != string::npos) { tag_key.erase(first_white); } - for (std::string::size_type i = 0; i < tag_key.length(); ++i) { - tag_key[i] = tolower(tag_key[i]); + for (char & i : tag_key) { + i = tolower(i); } if (allowed_markup.find(tag_key) != allowed_markup.end()) { result << "<" << tag_content << ">"; @@ -780,13 +776,13 @@ void t_html_generator::print_const_value(t_type* type, t_const_value* tvalue) { const map& val = tvalue->get_map(); map::const_iterator v_iter; for (v_iter = val.begin(); v_iter != val.end(); ++v_iter) { - t_type* field_type = NULL; + t_type* field_type = nullptr; for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { if ((*f_iter)->get_name() == v_iter->first->get_string()) { field_type = (*f_iter)->get_type(); } } - if (field_type == NULL) { + if (field_type == nullptr) { throw "type error: " + truetype->get_name() + " has no field " + v_iter->first->get_string(); } @@ -861,14 +857,14 @@ void t_html_generator::print_fn_args_doc(t_function* tfunction) { f_out_ << "

get_name() << "\">Parameters

" << endl; f_out_ << ""; - f_out_ << ""; + f_out_ << ""; for (; arg_iter != args.end(); arg_iter++) { f_out_ << "" << endl; } - f_out_ << "
NameDescription
NameDescription
" << (*arg_iter)->get_name(); f_out_ << ""; f_out_ << escape_html((*arg_iter)->get_doc()); f_out_ << "
"; + f_out_ << ""; } } @@ -885,14 +881,14 @@ void t_html_generator::print_fn_args_doc(t_function* tfunction) { f_out_ << "

get_name() << "\">Exceptions

" << endl; f_out_ << ""; - f_out_ << ""; + f_out_ << ""; for (; ex_iter != excepts.end(); ex_iter++) { f_out_ << "" << endl; } - f_out_ << "
TypeDescription
TypeDescription
" << (*ex_iter)->get_type()->get_name(); f_out_ << ""; f_out_ << escape_html((*ex_iter)->get_doc()); f_out_ << "
"; + f_out_ << ""; } } } @@ -975,8 +971,8 @@ void t_html_generator::generate_struct(t_struct* tstruct) { vector members = tstruct->get_members(); vector::iterator mem_iter = members.begin(); f_out_ << ""; - f_out_ << "" << endl; + f_out_ << "" << endl; for (; mem_iter != members.end(); mem_iter++) { f_out_ << "" << endl; } - f_out_ << "
KeyFieldTypeDescriptionRequirednessDefault value
KeyFieldTypeDescriptionRequirednessDefault value
" << (*mem_iter)->get_key() << ""; f_out_ << (*mem_iter)->get_name(); @@ -994,14 +990,14 @@ void t_html_generator::generate_struct(t_struct* tstruct) { } f_out_ << ""; t_const_value* default_val = (*mem_iter)->get_value(); - if (default_val != NULL) { + if (default_val != nullptr) { f_out_ << ""; print_const_value((*mem_iter)->get_type(), default_val); f_out_ << ""; } f_out_ << "

"; + f_out_ << "
"; print_doc(tstruct); f_out_ << ""; } @@ -1053,7 +1049,7 @@ void t_html_generator::generate_service(t_service* tservice) { first = false; print_type((*arg_iter)->get_type()); f_out_ << " " << (*arg_iter)->get_name(); - if ((*arg_iter)->get_value() != NULL) { + if ((*arg_iter)->get_value() != nullptr) { f_out_ << " = "; print_const_value((*arg_iter)->get_type(), (*arg_iter)->get_value()); } diff --git a/compiler/cpp/src/thrift/generate/t_java_generator.cc b/compiler/cpp/src/thrift/generate/t_java_generator.cc index 2c845512ccb..ceabe6687f5 100644 --- a/compiler/cpp/src/thrift/generate/t_java_generator.cc +++ b/compiler/cpp/src/thrift/generate/t_java_generator.cc @@ -122,21 +122,21 @@ class t_java_generator : public t_oop_generator { * Init and close methods */ - void init_generator(); - void close_generator(); + void init_generator() override; + void close_generator() override; - void generate_consts(std::vector consts); + void generate_consts(std::vector consts) override; /** * Program-level generation functions */ - void generate_typedef(t_typedef* ttypedef); - void generate_enum(t_enum* tenum); - void generate_struct(t_struct* tstruct); + void generate_typedef(t_typedef* ttypedef) override; + void generate_enum(t_enum* tenum) override; + void generate_struct(t_struct* tstruct) override; void generate_union(t_struct* tunion); - void generate_xception(t_struct* txception); - void generate_service(t_service* tservice); + void generate_xception(t_struct* txception) override; + void generate_service(t_service* tservice) override; void print_const_value(std::ostream& out, std::string name, @@ -305,7 +305,7 @@ class t_java_generator : public t_oop_generator { t_type* type); enum isset_type { ISSET_NONE, ISSET_PRIMITIVE, ISSET_BITSET }; - isset_type needs_isset(t_struct* tstruct, std::string* outPrimitiveType = NULL); + isset_type needs_isset(t_struct* tstruct, std::string* outPrimitiveType = nullptr); /** * Helper rendering functions @@ -648,13 +648,13 @@ void t_java_generator::print_const_value(std::ostream& out, indent_up(); } for (v_iter = val.begin(); v_iter != val.end(); ++v_iter) { - t_type* field_type = NULL; + t_type* field_type = nullptr; for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { if ((*f_iter)->get_name() == v_iter->first->get_string()) { field_type = (*f_iter)->get_type(); } } - if (field_type == NULL) { + if (field_type == nullptr) { throw "type error: " + type->get_name() + " has no field " + v_iter->first->get_string(); } string val = render_const_value(out, field_type, v_iter->second); @@ -731,7 +731,11 @@ string t_java_generator::render_const_value(ostream& out, t_type* type, t_const_ t_base_type::t_base tbase = ((t_base_type*)type)->get_base(); switch (tbase) { case t_base_type::TYPE_STRING: - render << '"' << get_escaped_string(value) << '"'; + if (((t_base_type*)type)->is_binary()) { + render << "java.nio.ByteBuffer.wrap(\"" << get_escaped_string(value) << "\".getBytes())"; + } else { + render << '"' << get_escaped_string(value) << '"'; + } break; case t_base_type::TYPE_BOOL: render << ((value->get_integer() > 0) ? "true" : "false"); @@ -907,7 +911,7 @@ void t_java_generator::generate_union_constructor(ostream& out, t_struct* tstruc bool default_value = false; for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) { t_type* type = get_true_type((*m_iter)->get_type()); - if ((*m_iter)->get_value() != NULL) { + if ((*m_iter)->get_value() != nullptr) { indent(out) << "super(_Fields." << constant_name((*m_iter)->get_name()) << ", " << render_const_value(out, type, (*m_iter)->get_value()) << ");" << endl; default_value = true; @@ -1056,11 +1060,15 @@ void t_java_generator::generate_union_getters_and_setters(ostream& out, t_struct } indent(out) << "public void set" << get_cap_name(field->get_name()) << "(" << type_name(field->get_type()) << " value) {" << endl; + + indent(out) << " setField_ = _Fields." << constant_name(field->get_name()) << ";" << endl; + if (type_can_be_null(field->get_type())) { - indent(out) << " if (value == null) throw new java.lang.NullPointerException();" << endl; + indent(out) << " value_ = java.util.Objects.requireNonNull(value,\"" << "_Fields." << constant_name(field->get_name()) << "\");" << endl; + } else { + indent(out) << " value_ = value;" << endl; } - indent(out) << " setField_ = _Fields." << constant_name(field->get_name()) << ";" << endl; - indent(out) << " value_ = value;" << endl; + indent(out) << "}" << endl; } } @@ -1538,7 +1546,7 @@ void t_java_generator::generate_java_struct_definition(ostream& out, indent_up(); for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) { t_type* t = get_true_type((*m_iter)->get_type()); - if ((*m_iter)->get_value() != NULL) { + if ((*m_iter)->get_value() != nullptr) { print_const_value(out, "this." + (*m_iter)->get_name(), t, @@ -1904,8 +1912,7 @@ void t_java_generator::generate_java_struct_equality(ostream& out, t_struct* tst out << indent() << "@Override" << endl << indent() << "public boolean equals(java.lang.Object that) {" << endl; indent_up(); - out << indent() << "if (that == null)" << endl << indent() << " return false;" << endl - << indent() << "if (that instanceof " << tstruct->get_name() << ")" << endl << indent() + out << indent() << "if (that instanceof " << tstruct->get_name() << ")" << endl << indent() << " return this.equals((" << tstruct->get_name() << ")that);" << endl << indent() << "return false;" << endl; scope_down(out); @@ -2048,8 +2055,8 @@ void t_java_generator::generate_java_struct_compare_to(ostream& out, t_struct* t vector::const_iterator m_iter; for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) { t_field* field = *m_iter; - indent(out) << "lastComparison = java.lang.Boolean.valueOf(" << generate_isset_check(field) - << ").compareTo(other." << generate_isset_check(field) << ");" << endl; + indent(out) << "lastComparison = java.lang.Boolean.compare(" << generate_isset_check(field) + << ", other." << generate_isset_check(field) << ");" << endl; indent(out) << "if (lastComparison != 0) {" << endl; indent(out) << " return lastComparison;" << endl; indent(out) << "}" << endl; @@ -2519,7 +2526,7 @@ void t_java_generator::generate_java_bean_boilerplate(ostream& out, t_struct* ts }else{ indent(out) << " : java.nio.ByteBuffer.wrap(" << field_name << ".clone());" << endl; } - + if (!bean_style_) { indent(out) << " return this;" << endl; } @@ -2875,7 +2882,7 @@ void t_java_generator::generate_service(t_service* tservice) { void t_java_generator::generate_service_interface(t_service* tservice) { string extends = ""; string extends_iface = ""; - if (tservice->get_extends() != NULL) { + if (tservice->get_extends() != nullptr) { extends = type_name(tservice->get_extends()); extends_iface = " extends " + extends + ".Iface"; } @@ -2896,7 +2903,7 @@ void t_java_generator::generate_service_interface(t_service* tservice) { void t_java_generator::generate_service_async_interface(t_service* tservice) { string extends = ""; string extends_iface = ""; - if (tservice->get_extends() != NULL) { + if (tservice->get_extends() != nullptr) { extends = type_name(tservice->get_extends()); extends_iface = " extends " + extends + " .AsyncIface"; } @@ -2936,7 +2943,7 @@ void t_java_generator::generate_service_helpers(t_service* tservice) { void t_java_generator::generate_service_client(t_service* tservice) { string extends = ""; string extends_client = ""; - if (tservice->get_extends() == NULL) { + if (tservice->get_extends() == nullptr) { extends_client = "org.apache.thrift.TServiceClient"; } else { extends = type_name(tservice->get_extends()); @@ -3099,7 +3106,7 @@ void t_java_generator::generate_service_client(t_service* tservice) { void t_java_generator::generate_service_async_client(t_service* tservice) { string extends = "org.apache.thrift.async.TAsyncClient"; string extends_client = ""; - if (tservice->get_extends() != NULL) { + if (tservice->get_extends() != nullptr) { extends = type_name(tservice->get_extends()) + ".AsyncClient"; } @@ -3275,7 +3282,7 @@ void t_java_generator::generate_service_server(t_service* tservice) { // Extends stuff string extends = ""; string extends_processor = ""; - if (tservice->get_extends() == NULL) { + if (tservice->get_extends() == nullptr) { extends_processor = "org.apache.thrift.TBaseProcessor"; } else { extends = type_name(tservice->get_extends()); @@ -3338,7 +3345,7 @@ void t_java_generator::generate_service_async_server(t_service* tservice) { // Extends stuff string extends = ""; string extends_processor = ""; - if (tservice->get_extends() == NULL) { + if (tservice->get_extends() == nullptr) { extends_processor = "org.apache.thrift.TBaseAsyncProcessor"; } else { extends = type_name(tservice->get_extends()); @@ -3854,19 +3861,18 @@ void t_java_generator::generate_deserialize_container(ostream& out, // Declare variables, read header if (ttype->is_map()) { indent(out) << "org.apache.thrift.protocol.TMap " << obj - << " = new org.apache.thrift.protocol.TMap(" + << " = iprot.readMapBegin(" << type_to_enum(((t_map*)ttype)->get_key_type()) << ", " - << type_to_enum(((t_map*)ttype)->get_val_type()) << ", " - << "iprot.readI32());" << endl; + << type_to_enum(((t_map*)ttype)->get_val_type()) << "); "<< endl; } else if (ttype->is_set()) { indent(out) << "org.apache.thrift.protocol.TSet " << obj - << " = new org.apache.thrift.protocol.TSet(" - << type_to_enum(((t_set*)ttype)->get_elem_type()) << ", iprot.readI32());" + << " = iprot.readSetBegin(" + << type_to_enum(((t_set*)ttype)->get_elem_type()) << ");" << endl; } else if (ttype->is_list()) { indent(out) << "org.apache.thrift.protocol.TList " << obj - << " = new org.apache.thrift.protocol.TList(" - << type_to_enum(((t_list*)ttype)->get_elem_type()) << ", iprot.readI32());" + << " = iprot.readListBegin(" + << type_to_enum(((t_list*)ttype)->get_elem_type()) << ");" << endl; } } @@ -4292,7 +4298,7 @@ string t_java_generator::type_name(t_type* ttype, // Check for namespacing t_program* program = ttype->get_program(); - if ((program != NULL) && ((program != program_) || force_namespace)) { + if ((program != nullptr) && ((program != program_) || force_namespace)) { string package = program->get_namespace("java"); if (!package.empty()) { return package + "." + ttype->get_name(); @@ -4352,7 +4358,7 @@ string t_java_generator::declare_field(t_field* tfield, bool init, bool comment) } result += type_name(tfield->get_type()) + " " + tfield->get_name(); if (init) { - if (ttype->is_base_type() && tfield->get_value() != NULL) { + if (ttype->is_base_type() && tfield->get_value() != nullptr) { std::ofstream dummy; result += " = " + render_const_value(dummy, ttype, tfield->get_value()); } else if (ttype->is_base_type()) { @@ -4652,9 +4658,7 @@ string t_java_generator::constant_name(string name) { bool is_first = true; bool was_previous_char_upper = false; - for (string::iterator iter = name.begin(); iter != name.end(); ++iter) { - string::value_type character = (*iter); - + for (char character : name) { bool is_upper = isupper(character); if (is_upper && !is_first && !was_previous_char_upper) { @@ -4993,7 +4997,7 @@ t_java_generator::isset_type t_java_generator::needs_isset(t_struct* tstruct, if (count == 0) { return ISSET_NONE; } else if (count <= 64) { - if (outPrimitiveType != NULL) { + if (outPrimitiveType != nullptr) { if (count <= 8) *outPrimitiveType = "byte"; else if (count <= 16) @@ -5023,7 +5027,7 @@ void t_java_generator::generate_java_struct_clear(std::ostream& out, t_struct* t t_field* field = *m_iter; t_type* t = get_true_type(field->get_type()); - if (field->get_value() != NULL) { + if (field->get_value() != nullptr) { print_const_value(out, "this." + field->get_name(), t, field->get_value(), true, true); continue; } @@ -5415,7 +5419,7 @@ void t_java_generator::generate_java_scheme_lookup(ostream& out) { } void t_java_generator::generate_javax_generated_annotation(ostream& out) { - time_t seconds = time(NULL); + time_t seconds = time(nullptr); struct tm* now = localtime(&seconds); indent(out) << "@javax.annotation.Generated(value = \"" << autogen_summary() << "\""; if (undated_generated_annotations_) { @@ -5440,7 +5444,7 @@ THRIFT_REGISTER_GENERATOR( "above).\n" " option_type: Wrap optional fields in an Option type.\n" " rethrow_unhandled_exceptions:\n" - " Enable rethrow of unhandled exceptions and let them propagate futher." + " Enable rethrow of unhandled exceptions and let them propagate further." " (Default behavior is to catch and log it.)\n" " java5: Generate Java 1.5 compliant code (includes android_legacy flag).\n" " reuse-objects: Data objects will not be allocated, but existing instances will be used " diff --git a/compiler/cpp/src/thrift/generate/t_javame_generator.cc b/compiler/cpp/src/thrift/generate/t_javame_generator.cc index fa743ca3d38..b37a43bd072 100644 --- a/compiler/cpp/src/thrift/generate/t_javame_generator.cc +++ b/compiler/cpp/src/thrift/generate/t_javame_generator.cc @@ -65,21 +65,21 @@ class t_javame_generator : public t_oop_generator { * Init and close methods */ - void init_generator(); - void close_generator(); + void init_generator() override; + void close_generator() override; - void generate_consts(std::vector consts); + void generate_consts(std::vector consts) override; /** * Program-level generation functions */ - void generate_typedef(t_typedef* ttypedef); - void generate_enum(t_enum* tenum); - void generate_struct(t_struct* tstruct); + void generate_typedef(t_typedef* ttypedef) override; + void generate_enum(t_enum* tenum) override; + void generate_struct(t_struct* tstruct) override; void generate_union(t_struct* tunion); - void generate_xception(t_struct* txception); - void generate_service(t_service* tservice); + void generate_xception(t_struct* txception) override; + void generate_service(t_service* tservice) override; void print_const_value(std::ostream& out, std::string name, @@ -185,13 +185,13 @@ class t_javame_generator : public t_oop_generator { void generate_serialize_list_element(std::ostream& out, t_list* tlist, std::string iter); - void generate_java_doc(std::ostream& out, t_field* field); + void generate_java_doc(std::ostream& out, t_field* field) override; - void generate_java_doc(std::ostream& out, t_doc* tdoc); + void generate_java_doc(std::ostream& out, t_doc* tdoc) override; - void generate_java_doc(std::ostream& out, t_function* tdoc); + void generate_java_doc(std::ostream& out, t_function* tdoc) override; - void generate_java_docstring_comment(std::ostream& out, string contents); + void generate_java_docstring_comment(std::ostream& out, string contents) override; void generate_deep_copy_container(std::ostream& out, std::string source_name_p1, @@ -221,7 +221,7 @@ class t_javame_generator : public t_oop_generator { std::string function_signature(t_function* tfunction, std::string prefix = ""); std::string argument_list(t_struct* tstruct, bool include_types = true); std::string type_to_enum(t_type* ttype); - std::string get_enum_class_name(t_type* type); + std::string get_enum_class_name(t_type* type) override; void generate_struct_desc(ostream& out, t_struct* tstruct); void generate_field_descs(ostream& out, t_struct* tstruct); std::string box_type(t_type* type, string value); @@ -462,13 +462,13 @@ void t_javame_generator::print_const_value(std::ostream& out, indent_up(); } for (v_iter = val.begin(); v_iter != val.end(); ++v_iter) { - t_type* field_type = NULL; + t_type* field_type = nullptr; for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { if ((*f_iter)->get_name() == v_iter->first->get_string()) { field_type = (*f_iter)->get_type(); } } - if (field_type == NULL) { + if (field_type == nullptr) { throw "type error: " + type->get_name() + " has no field " + v_iter->first->get_string(); } string val = render_const_value(out, name, field_type, v_iter->second); @@ -1060,7 +1060,7 @@ void t_javame_generator::generate_java_struct_definition(ostream& out, indent_up(); for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) { t_type* t = get_true_type((*m_iter)->get_type()); - if ((*m_iter)->get_value() != NULL) { + if ((*m_iter)->get_value() != nullptr) { print_const_value(out, "this." + (*m_iter)->get_name(), t, @@ -1898,7 +1898,7 @@ void t_javame_generator::generate_primitive_service_interface(t_service* tservic f_iface.open(f_interface_name.c_str()); string extends_iface = ""; - if (tservice->get_extends() != NULL) { + if (tservice->get_extends() != nullptr) { extends_iface = " extends " + type_name(tservice->get_extends()) + "Iface"; } @@ -1923,7 +1923,7 @@ void t_javame_generator::generate_primitive_service_interface(t_service* tservic void t_javame_generator::generate_service_interface(t_service* tservice) { string extends = ""; string extends_iface = ""; - if (tservice->get_extends() != NULL) { + if (tservice->get_extends() != nullptr) { extends = type_name(tservice->get_extends()); extends_iface = " extends " + extends + ".Iface"; } @@ -1964,7 +1964,7 @@ void t_javame_generator::generate_service_helpers(t_service* tservice) { void t_javame_generator::generate_service_client(t_service* tservice) { string extends = ""; string extends_client = ""; - if (tservice->get_extends() != NULL) { + if (tservice->get_extends() != nullptr) { extends = type_name(tservice->get_extends()); extends_client = " extends " + extends + ".Client"; } @@ -2144,7 +2144,7 @@ void t_javame_generator::generate_service_server(t_service* tservice) { // Extends stuff string extends = ""; string extends_processor = ""; - if (tservice->get_extends() != NULL) { + if (tservice->get_extends() != nullptr) { extends = type_name(tservice->get_extends()); extends_processor = " extends " + extends + ".Processor"; } @@ -2791,7 +2791,7 @@ string t_javame_generator::type_name(t_type* ttype, // Check for namespacing t_program* program = ttype->get_program(); - if (program != NULL && program != program_) { + if (program != nullptr && program != program_) { string package = program->get_namespace("java"); if (!package.empty()) { return package + "." + ttype->get_name(); @@ -2846,7 +2846,7 @@ string t_javame_generator::declare_field(t_field* tfield, bool init) { string result = type_name(tfield->get_type()) + " " + tfield->get_name(); if (init) { t_type* ttype = get_true_type(tfield->get_type()); - if (ttype->is_base_type() && tfield->get_value() != NULL) { + if (ttype->is_base_type() && tfield->get_value() != nullptr) { std::ofstream dummy; result += " = " + render_const_value(dummy, tfield->get_name(), ttype, tfield->get_value()); } else if (ttype->is_base_type()) { @@ -2980,9 +2980,7 @@ string t_javame_generator::constant_name(string name) { bool is_first = true; bool was_previous_char_upper = false; - for (string::iterator iter = name.begin(); iter != name.end(); ++iter) { - string::value_type character = (*iter); - + for (char character : name) { bool is_upper = isupper(character); if (is_upper && !is_first && !was_previous_char_upper) { @@ -3210,7 +3208,7 @@ void t_javame_generator::generate_isset_set(ostream& out, t_field* field) { std::string t_javame_generator::get_enum_class_name(t_type* type) { string package = ""; t_program* program = type->get_program(); - if (program != NULL && program != program_) { + if (program != nullptr && program != program_) { package = program->get_namespace("java") + "."; } return package + type->get_name(); @@ -3254,7 +3252,7 @@ void t_javame_generator::generate_java_struct_clear(std::ostream& out, t_struct* indent_up(); for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) { t_type* t = get_true_type((*m_iter)->get_type()); - if ((*m_iter)->get_value() != NULL) { + if ((*m_iter)->get_value() != nullptr) { print_const_value(out, "this." + (*m_iter)->get_name(), t, diff --git a/compiler/cpp/src/thrift/generate/t_js_generator.cc b/compiler/cpp/src/thrift/generate/t_js_generator.cc index 61782b9d8bd..fddcef49a5d 100644 --- a/compiler/cpp/src/thrift/generate/t_js_generator.cc +++ b/compiler/cpp/src/thrift/generate/t_js_generator.cc @@ -26,6 +26,7 @@ #include #include #include +#include #include #include @@ -38,9 +39,15 @@ using std::ostream; using std::ostringstream; using std::string; using std::stringstream; +using std::unordered_map; using std::vector; static const string endl = "\n"; // avoid ostream << std::endl flushes +static const string episode_file_name = "thrift.js.episode"; +// largest consecutive integer representable by a double (2 ^ 53 - 1) +static const int64_t max_safe_integer = 0x1fffffffffffff; +// smallest consecutive number representable by a double (-2 ^ 53 + 1) +static const int64_t min_safe_integer = -max_safe_integer; #include "thrift/generate/t_oop_generator.h" @@ -61,6 +68,7 @@ class t_js_generator : public t_oop_generator { gen_jquery_ = false; gen_ts_ = false; gen_es6_ = false; + gen_episode_file_ = false; bool with_ns_ = false; @@ -75,22 +83,26 @@ class t_js_generator : public t_oop_generator { with_ns_ = true; } else if( iter->first.compare("es6") == 0) { gen_es6_ = true; + } else if( iter->first.compare("imports") == 0) { + parse_imports(program, iter->second); + } else if (iter->first.compare("thrift_package_output_directory") == 0) { + parse_thrift_package_output_directory(iter->second); } else { - throw "unknown option js:" + iter->first; + throw std::invalid_argument("unknown option js:" + iter->first); } } if (gen_es6_ && gen_jquery_) { - throw "Invalid switch: [-gen js:es6,jquery] options not compatible"; + throw std::invalid_argument("invalid switch: [-gen js:es6,jquery] options not compatible"); } if (gen_node_ && gen_jquery_) { - throw "Invalid switch: [-gen js:node,jquery] options not compatible, try: [-gen js:node -gen " - "js:jquery]"; + throw std::invalid_argument("invalid switch: [-gen js:node,jquery] options not compatible, try: [-gen js:node -gen " + "js:jquery]"); } if (!gen_node_ && with_ns_) { - throw "Invalid switch: [-gen js:with_ns] is only valid when using node.js"; + throw std::invalid_argument("invalid switch: [-gen js:with_ns] is only valid when using node.js"); } // Depending on the processing flags, we will update these to be ES6 compatible @@ -117,19 +129,19 @@ class t_js_generator : public t_oop_generator { * Init and close methods */ - void init_generator(); - void close_generator(); + void init_generator() override; + void close_generator() override; /** * Program-level generation functions */ - void generate_typedef(t_typedef* ttypedef); - void generate_enum(t_enum* tenum); - void generate_const(t_const* tconst); - void generate_struct(t_struct* tstruct); - void generate_xception(t_struct* txception); - void generate_service(t_service* tservice); + void generate_typedef(t_typedef* ttypedef) override; + void generate_enum(t_enum* tenum) override; + void generate_const(t_const* tconst) override; + void generate_struct(t_struct* tstruct) override; + void generate_xception(t_struct* txception) override; + void generate_service(t_service* tservice) override; std::string render_recv_throw(std::string var); std::string render_recv_return(std::string var); @@ -200,7 +212,10 @@ class t_js_generator : public t_oop_generator { std::string js_includes(); std::string ts_includes(); + std::string ts_service_includes(); std::string render_includes(); + std::string render_ts_includes(); + std::string get_import_path(t_program* program); std::string declare_field(t_field* tfield, bool init = false, bool obj = false); std::string function_signature(t_function* tfunction, std::string prefix = "", @@ -208,8 +223,16 @@ class t_js_generator : public t_oop_generator { std::string argument_list(t_struct* tstruct, bool include_callback = false); std::string type_to_enum(t_type* ttype); std::string make_valid_nodeJs_identifier(std::string const& name); + std::string next_identifier_name(std::vector const& fields, std::string const& base_name); + bool find_field(std::vector const& fields, std::string const& name); - std::string autogen_comment() { + /** + * Helper parser functions + */ + void parse_imports(t_program* program, const std::string& imports_string); + void parse_thrift_package_output_directory(const std::string& thrift_package_output_directory); + + std::string autogen_comment() override { return std::string("//\n") + "// Autogenerated by Thrift Compiler (" + THRIFT_VERSION + ")\n" + "//\n" + "// DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING\n" + "//\n"; @@ -243,7 +266,7 @@ class t_js_generator : public t_oop_generator { std::string js_type_namespace(t_program* p) { if (gen_node_) { - if (p != NULL && p != program_) { + if (p != nullptr && p != program_) { return make_valid_nodeJs_identifier(p->get_name()) + "_ttypes."; } return "ttypes."; @@ -282,7 +305,7 @@ class t_js_generator : public t_oop_generator { * TypeScript Definition File helper functions */ - string ts_function_signature(t_function* tfunction, bool include_callback, bool optional_callback); + string ts_function_signature(t_function* tfunction, bool include_callback); string ts_get_type(t_type* type); /** @@ -296,14 +319,14 @@ class t_js_generator : public t_oop_generator { * Returns "declare " if no module was defined. * @return string */ - string ts_declare() { return (ts_module_.empty() ? "declare " : ""); } + string ts_declare() { return (ts_module_.empty() ? (gen_node_ ? "declare " : "export declare ") : ""); } /** * Returns "?" if the given field is optional or has a default value. * @param t_field The field to check * @return string */ - string ts_get_req(t_field* field) {return (field->get_req() == t_field::T_OPTIONAL || field->get_value() != NULL ? "?" : ""); } + string ts_get_req(t_field* field) {return (field->get_req() == t_field::T_OPTIONAL || field->get_value() != nullptr ? "?" : ""); } /** * Returns the documentation, if the provided documentable object has one. @@ -347,6 +370,11 @@ class t_js_generator : public t_oop_generator { */ bool gen_es6_; + /** + * True if we will generate an episode file. + */ + bool gen_episode_file_; + /** * The name of the defined module(s), for TypeScript Definition Files. */ @@ -357,6 +385,26 @@ class t_js_generator : public t_oop_generator { */ bool no_ns_; + /** + * The node modules to use when importing the previously generated files. + */ + vector imports; + + /** + * Cache for imported modules. + */ + unordered_map module_name_2_import_path; + + /** + * Cache for TypeScript includes to generated import name. + */ + unordered_map include_2_import_name; + + /** + * The prefix to use when generating the episode file. + */ + string thrift_package_output_directory_; + /** * The variable decorator for "const" variables. Will default to "var" if in an incompatible language. */ @@ -375,6 +423,7 @@ class t_js_generator : public t_oop_generator { /** * File streams */ + ofstream_with_content_based_conditional_update f_episode_; ofstream_with_content_based_conditional_update f_types_; ofstream_with_content_based_conditional_update f_service_; ofstream_with_content_based_conditional_update f_types_ts_; @@ -391,14 +440,23 @@ void t_js_generator::init_generator() { // Make output directory MKDIR(get_out_dir().c_str()); - string outdir = get_out_dir(); + const auto outdir = get_out_dir(); // Make output file(s) - string f_types_name = outdir + program_->get_name() + "_types.js"; + if (gen_episode_file_) { + const auto f_episode_file_path = outdir + episode_file_name; + f_episode_.open(f_episode_file_path); + } + + const auto f_types_name = outdir + program_->get_name() + "_types.js"; f_types_.open(f_types_name.c_str()); + if (gen_episode_file_) { + const auto types_module = program_->get_name() + "_types"; + f_episode_ << types_module << ":" << thrift_package_output_directory_ << "/" << types_module << endl; + } if (gen_ts_) { - string f_types_ts_name = outdir + program_->get_name() + "_types.d.ts"; + const auto f_types_ts_name = outdir + program_->get_name() + "_types.d.ts"; f_types_ts_.open(f_types_ts_name.c_str()); } @@ -412,7 +470,7 @@ void t_js_generator::init_generator() { f_types_ << js_includes() << endl << render_includes() << endl; if (gen_ts_) { - f_types_ts_ << autogen_comment() << ts_includes() << endl; + f_types_ts_ << autogen_comment() << ts_includes() << endl << render_ts_includes() << endl; } if (gen_node_) { @@ -430,6 +488,8 @@ void t_js_generator::init_generator() { f_types_ << "if (typeof " << pns << " === 'undefined') {" << endl; f_types_ << " " << pns << " = {};" << endl; f_types_ << "}" << endl; + f_types_ << "" << "if (typeof module !== 'undefined' && module.exports) {" << endl; + f_types_ << " module.exports." << pns << " = " << pns << ";" << endl << "}" << endl; } if (gen_ts_) { ts_module_ = pns; @@ -448,10 +508,11 @@ string t_js_generator::js_includes() { if (!gen_es6_) { result += js_const_type_ + "Q = thrift.Q;\n"; } + result += js_const_type_ + "Int64 = require('node-int64');\n"; return result; } - - return ""; + string result = "if (typeof Int64 === 'undefined' && typeof require === 'function') {\n " + js_const_type_ + "Int64 = require('node-int64');\n}\n"; + return result; } /** @@ -462,10 +523,24 @@ string t_js_generator::ts_includes() { return string( "import thrift = require('thrift');\n" "import Thrift = thrift.Thrift;\n" - "import Q = thrift.Q;\n"); + "import Q = thrift.Q;\n" + "import Int64 = require('node-int64');"); } + return string("import Int64 = require('node-int64');"); +} - return ""; +/** + * Prints service ts imports + */ +string t_js_generator::ts_service_includes() { + if (gen_node_) { + return string( + "import thrift = require('thrift');\n" + "import Thrift = thrift.Thrift;\n" + "import Q = thrift.Q;\n" + "import Int64 = require('node-int64');"); + } + return string("import Int64 = require('node-int64');"); } /** @@ -476,9 +551,8 @@ string t_js_generator::render_includes() { if (gen_node_) { const vector& includes = program_->get_includes(); - for (size_t i = 0; i < includes.size(); ++i) { - result += js_const_type_ + make_valid_nodeJs_identifier(includes[i]->get_name()) + "_ttypes = require('./" + includes[i]->get_name() - + "_types');\n"; + for (auto include : includes) { + result += js_const_type_ + make_valid_nodeJs_identifier(include->get_name()) + "_ttypes = require('" + get_import_path(include) + "');\n"; } if (includes.size() > 0) { result += "\n"; @@ -488,6 +562,43 @@ string t_js_generator::render_includes() { return result; } +/** + * Renders all the imports necessary for including another Thrift program + */ +string t_js_generator::render_ts_includes() { + string result; + + if (!gen_node_) { + return result; + } + const vector& includes = program_->get_includes(); + for (auto include : includes) { + string include_name = make_valid_nodeJs_identifier(include->get_name()) + "_ttypes"; + include_2_import_name.insert({include, include_name}); + result += "import " + include_name + " = require('" + get_import_path(include) + "');\n"; + } + if (includes.size() > 0) { + result += "\n"; + } + + return result; +} + +string t_js_generator::get_import_path(t_program* program) { + const string import_file_name(program->get_name() + "_types"); + if (program->get_recursive()) { + return "./" + import_file_name; + } + + const string import_file_name_with_extension = import_file_name + ".js"; + + auto module_name_and_import_path_iterator = module_name_2_import_path.find(import_file_name); + if (module_name_and_import_path_iterator != module_name_2_import_path.end()) { + return module_name_and_import_path_iterator->second; + } + return "./" + import_file_name; +} + /** * Close up (or down) some filez. */ @@ -502,6 +613,9 @@ void t_js_generator::close_generator() { } f_types_ts_.close(); } + if (gen_episode_file_){ + f_episode_.close(); + } } /** @@ -593,9 +707,18 @@ string t_js_generator::render_const_value(t_type* type, t_const_value* value) { case t_base_type::TYPE_I8: case t_base_type::TYPE_I16: case t_base_type::TYPE_I32: - case t_base_type::TYPE_I64: out << value->get_integer(); break; + case t_base_type::TYPE_I64: + { + int64_t const& integer_value = value->get_integer(); + if (integer_value <= max_safe_integer && integer_value >= min_safe_integer) { + out << "new Int64(" << integer_value << ")"; + } else { + out << "new Int64('" << std::hex << integer_value << std::dec << "')"; + } + } + break; case t_base_type::TYPE_DOUBLE: if (value->get_type() == t_const_value::CV_INTEGER) { out << value->get_integer(); @@ -604,7 +727,7 @@ string t_js_generator::render_const_value(t_type* type, t_const_value* value) { } break; default: - throw "compiler error: no const of base type " + t_base_type::t_base_name(tbase); + throw std::runtime_error("compiler error: no const of base type " + t_base_type::t_base_name(tbase)); } } else if (type->is_enum()) { out << value->get_integer(); @@ -616,14 +739,14 @@ string t_js_generator::render_const_value(t_type* type, t_const_value* value) { const map& val = value->get_map(); map::const_iterator v_iter; for (v_iter = val.begin(); v_iter != val.end(); ++v_iter) { - t_type* field_type = NULL; + t_type* field_type = nullptr; for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { if ((*f_iter)->get_name() == v_iter->first->get_string()) { field_type = (*f_iter)->get_type(); } } - if (field_type == NULL) { - throw "type error: " + type->get_name() + " has no field " + v_iter->first->get_string(); + if (field_type == nullptr) { + throw std::runtime_error("type error: " + type->get_name() + " has no field " + v_iter->first->get_string()); } if (v_iter != val.begin()) out << ","; @@ -646,7 +769,11 @@ string t_js_generator::render_const_value(t_type* type, t_const_value* value) { if (v_iter != val.begin()) out << "," << endl; - out << indent() << render_const_value(ktype, v_iter->first); + if (ktype->is_base_type() && ((t_base_type*)get_true_type(ktype))->get_base() == t_base_type::TYPE_I64){ + out << indent() << "\"" << v_iter->first->get_integer() << "\""; + } else { + out << indent() << render_const_value(ktype, v_iter->first); + } out << " : "; out << render_const_value(vtype, v_iter->second); @@ -775,7 +902,7 @@ void t_js_generator::generate_js_struct_definition(ostream& out, for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) { string dval = declare_field(*m_iter, false, true); t_type* t = get_true_type((*m_iter)->get_type()); - if ((*m_iter)->get_value() != NULL && !(t->is_struct() || t->is_xception())) { + if ((*m_iter)->get_value() != nullptr && !(t->is_struct() || t->is_xception())) { dval = render_const_value((*m_iter)->get_type(), (*m_iter)->get_value()); out << indent() << "this." << (*m_iter)->get_name() << " = " << dval << ";" << endl; } else { @@ -789,7 +916,6 @@ void t_js_generator::generate_js_struct_definition(ostream& out, f_types_ts_ << ts_indent() << (*m_iter)->get_name() << ": " << ts_get_type((*m_iter)->get_type()) << ";" << endl; } - } } @@ -798,7 +924,7 @@ void t_js_generator::generate_js_struct_definition(ostream& out, for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) { t_type* t = get_true_type((*m_iter)->get_type()); - if ((*m_iter)->get_value() != NULL && (t->is_struct() || t->is_xception())) { + if ((*m_iter)->get_value() != nullptr && (t->is_struct() || t->is_xception())) { indent(out) << "this." << (*m_iter)->get_name() << " = " << render_const_value(t, (*m_iter)->get_value()) << ";" << endl; } @@ -1067,6 +1193,9 @@ void t_js_generator::generate_js_struct_writer(ostream& out, t_struct* tstruct) void t_js_generator::generate_service(t_service* tservice) { string f_service_name = get_out_dir() + service_name_ + ".js"; f_service_.open(f_service_name.c_str()); + if (gen_episode_file_) { + f_episode_ << service_name_ << ":" << thrift_package_output_directory_ << "/" << service_name_ << endl; + } if (gen_ts_) { string f_service_ts_name = get_out_dir() + service_name_ + ".d.ts"; @@ -1082,13 +1211,12 @@ void t_js_generator::generate_service(t_service* tservice) { f_service_ << js_includes() << endl << render_includes() << endl; if (gen_ts_) { - if (tservice->get_extends() != NULL) { + if (tservice->get_extends() != nullptr) { f_service_ts_ << "/// get_extends()->get_name() << ".d.ts\" />" << endl; } - f_service_ts_ << autogen_comment() << endl; + f_service_ts_ << autogen_comment() << endl << ts_includes() << endl << render_ts_includes() << endl; if (gen_node_) { - f_service_ts_ << ts_includes() << endl; f_service_ts_ << "import ttypes = require('./" + program_->get_name() + "_types');" << endl; // Generate type aliases // enum @@ -1119,20 +1247,32 @@ void t_js_generator::generate_service(t_service* tservice) { f_service_ts_ << "import " << (*s_iter)->get_name() << " = ttypes." << js_namespace(program_) << (*s_iter)->get_name() << endl; } + } else { + f_service_ts_ << "import { " << program_->get_name() << " } from \"./" << program_->get_name() << "_types\";" << endl << endl; } if (!ts_module_.empty()) { - f_service_ts_ << "declare module " << ts_module_ << " {"; + if (gen_node_) { + f_service_ts_ << "declare module " << ts_module_ << " {"; + } else { + f_service_ts_ << "declare module \"./" << program_->get_name() << "_types\" {" << endl; + indent_up(); + f_service_ts_ << ts_indent() << "module " << program_->get_name() << " {" << endl; + indent_up(); + } } } if (gen_node_) { - if (tservice->get_extends() != NULL) { + if (tservice->get_extends() != nullptr) { f_service_ << js_const_type_ << tservice->get_extends()->get_name() << " = require('./" << tservice->get_extends()->get_name() << "');" << endl << js_const_type_ << tservice->get_extends()->get_name() << "Client = " << tservice->get_extends()->get_name() << ".Client;" << endl << js_const_type_ << tservice->get_extends()->get_name() << "Processor = " << tservice->get_extends()->get_name() << ".Processor;" << endl; + + f_service_ts_ << "import " << tservice->get_extends()->get_name() << " = require('./" + << tservice->get_extends()->get_name() << "');" << endl; } f_service_ << js_const_type_ << "ttypes = require('./" + program_->get_name() + "_types');" << endl; @@ -1149,7 +1289,13 @@ void t_js_generator::generate_service(t_service* tservice) { f_service_.close(); if (gen_ts_) { if (!ts_module_.empty()) { - f_service_ts_ << "}"; + if (gen_node_) { + f_service_ts_ << "}" << endl; + } else { + indent_down(); + f_service_ts_ << ts_indent() << "}" << endl; + f_service_ts_ << "}" << endl; + } } f_service_ts_.close(); } @@ -1169,14 +1315,17 @@ void t_js_generator::generate_service_processor(t_service* tservice) { f_service_ << prefix << service_name_ << "Processor = " << "exports.Processor"; if (gen_ts_) { f_service_ts_ << endl << "declare class Processor "; - if (tservice->get_extends() != NULL) { - f_service_ts_ << "extends " << tservice->get_extends()->get_name() << "Processor "; + if (tservice->get_extends() != nullptr) { + f_service_ts_ << "extends " << tservice->get_extends()->get_name() << ".Processor "; } f_service_ts_ << "{" << endl; indent_up(); - f_service_ts_ << ts_indent() << "private _handler: Object;" << endl << endl; - f_service_ts_ << ts_indent() << "constructor(handler: Object);" << endl; - f_service_ts_ << ts_indent() << "process(input: Thrift.TJSONProtocol, output: Thrift.TJSONProtocol): void;" << endl; + + if(tservice->get_extends() == nullptr) { + f_service_ts_ << ts_indent() << "private _handler: object;" << endl << endl; + } + f_service_ts_ << ts_indent() << "constructor(handler: object);" << endl; + f_service_ts_ << ts_indent() << "process(input: thrift.TProtocol, output: thrift.TProtocol): void;" << endl; indent_down(); } } else { @@ -1184,14 +1333,14 @@ void t_js_generator::generate_service_processor(t_service* tservice) { << "exports.Processor"; } - bool is_subclass_service = tservice->get_extends() != NULL; + bool is_subclass_service = tservice->get_extends() != nullptr; // ES6 Constructor if (gen_es6_) { if (is_subclass_service) { - f_service_ << " = class extends " << tservice->get_extends()->get_name() << "Processor {" << endl; + f_service_ << " = class " << service_name_ << "Processor extends " << tservice->get_extends()->get_name() << "Processor {" << endl; } else { - f_service_ << " = class {" << endl; + f_service_ << " = class " << service_name_ << "Processor {" << endl; } indent_up(); indent(f_service_) << "constructor(handler) {" << endl; @@ -1280,7 +1429,7 @@ void t_js_generator::generate_process_function(t_service* tservice, t_function* } if (gen_ts_) { indent_up(); - f_service_ts_ << ts_indent() << "process_" << tfunction->get_name() << "(seqid: number, input: Thrift.TJSONProtocol, output: Thrift.TJSONProtocol): void;" << endl; + f_service_ts_ << ts_indent() << "process_" << tfunction->get_name() << "(seqid: number, input: thrift.TProtocol, output: thrift.TProtocol): void;" << endl; indent_down(); } @@ -1369,8 +1518,8 @@ void t_js_generator::generate_process_function(t_service* tservice, t_function* t_struct* exceptions = tfunction->get_xceptions(); if (exceptions) { const vector& members = exceptions->get_members(); - for (vector::const_iterator it = members.begin(); it != members.end(); ++it) { - t_type* t = get_true_type((*it)->get_type()); + for (auto member : members) { + t_type* t = get_true_type(member->get_type()); if (t->is_xception()) { if (!has_exception) { has_exception = true; @@ -1433,8 +1582,8 @@ void t_js_generator::generate_process_function(t_service* tservice, t_function* indent(f_service_) << "if ((err === null || typeof err === 'undefined')"; if (has_exception) { const vector& members = exceptions->get_members(); - for (vector::const_iterator it = members.begin(); it != members.end(); ++it) { - t_type* t = get_true_type((*it)->get_type()); + for (auto member : members) { + t_type* t = get_true_type(member->get_type()); if (t->is_xception()) { f_service_ << " || err instanceof " << js_type_namespace(t->get_program()) << t->get_name(); } @@ -1544,7 +1693,7 @@ void t_js_generator::generate_service_rest(t_service* tservice) { */ void t_js_generator::generate_service_client(t_service* tservice) { - bool is_subclass_service = tservice->get_extends() != NULL; + bool is_subclass_service = tservice->get_extends() != nullptr; if (gen_node_) { string prefix = has_js_namespace(tservice->get_program()) ? js_namespace(tservice->get_program()) : js_const_type_; @@ -1552,8 +1701,8 @@ void t_js_generator::generate_service_client(t_service* tservice) { if (gen_ts_) { f_service_ts_ << ts_print_doc(tservice) << ts_indent() << ts_declare() << "class " << "Client "; - if (tservice->get_extends() != NULL) { - f_service_ts_ << "extends " << tservice->get_extends()->get_name() << "Client "; + if (tservice->get_extends() != nullptr) { + f_service_ts_ << "extends " << tservice->get_extends()->get_name() << ".Client "; } f_service_ts_ << "{" << endl; } @@ -1572,11 +1721,12 @@ void t_js_generator::generate_service_client(t_service* tservice) { // ES6 Constructor if (gen_es6_) { + if (is_subclass_service) { - f_service_ << " = class extends " << js_namespace(tservice->get_extends()->get_program()) + f_service_ << " = class " << service_name_ << "Client extends " << js_namespace(tservice->get_extends()->get_program()) << tservice->get_extends()->get_name() << "Client {" << endl; } else { - f_service_ << " = class {" << endl; + f_service_ << " = class " << service_name_ << "Client {" << endl; } indent_up(); if (gen_node_) { @@ -1595,15 +1745,22 @@ void t_js_generator::generate_service_client(t_service* tservice) { indent_up(); if (gen_node_) { + if (gen_es6_ && is_subclass_service) { + indent(f_service_) << "super(output, pClass);" << endl; + } indent(f_service_) << "this.output = output;" << endl; indent(f_service_) << "this.pClass = pClass;" << endl; indent(f_service_) << "this._seqid = 0;" << endl; indent(f_service_) << "this._reqs = {};" << endl; if (gen_ts_) { - f_service_ts_ << ts_indent() << "private input: Thrift.TJSONProtocol;" << endl << ts_indent() - << "private output: Thrift.TJSONProtocol;" << endl << ts_indent() << "private seqid: number;" - << endl << endl << ts_indent() - << "constructor(input: Thrift.TJSONProtocol, output?: Thrift.TJSONProtocol);" + if(!is_subclass_service) { + f_service_ts_ << ts_indent() << "private output: thrift.TTransport;" << endl + << ts_indent() << "private pClass: thrift.TProtocol;" << endl + << ts_indent() << "private _seqid: number;" << endl + << endl; + } + + f_service_ts_ << ts_indent() << "constructor(output: thrift.TTransport, pClass: { new(trans: thrift.TTransport): thrift.TProtocol });" << endl; } } else { @@ -1674,13 +1831,13 @@ void t_js_generator::generate_service_client(t_service* tservice) { if (gen_ts_) { // function definition without callback - f_service_ts_ << ts_print_doc(*f_iter) << ts_indent() << ts_function_signature(*f_iter, false, false) << endl; + f_service_ts_ << ts_print_doc(*f_iter) << ts_indent() << ts_function_signature(*f_iter, false) << endl; if (!gen_es6_) { // overload with callback - f_service_ts_ << ts_print_doc(*f_iter) << ts_indent() << ts_function_signature(*f_iter, true, false) << endl; + f_service_ts_ << ts_print_doc(*f_iter) << ts_indent() << ts_function_signature(*f_iter, true) << endl; } else { // overload with callback - f_service_ts_ << ts_print_doc(*f_iter) << ts_indent() << ts_function_signature(*f_iter, true, true) << endl; + f_service_ts_ << ts_print_doc(*f_iter) << ts_indent() << ts_function_signature(*f_iter, true) << endl; } } @@ -1812,7 +1969,10 @@ void t_js_generator::generate_service_client(t_service* tservice) { : "Thrift.MessageType.CALL"; // Build args if (fields.size() > 0){ - f_service_ << indent() << js_const_type_ << "params = {" << endl; + // It is possible that a method argument is named "params", we need to ensure the locally + // generated identifier "params" is uniquely named + std::string params_identifier = this->next_identifier_name(fields, "params"); + f_service_ << indent() << js_const_type_ << params_identifier << " = {" << endl; indent_up(); for (fld_iter = fields.begin(); fld_iter != fields.end(); ++fld_iter) { indent(f_service_) << (*fld_iter)->get_name() << ": " << (*fld_iter)->get_name(); @@ -1824,7 +1984,9 @@ void t_js_generator::generate_service_client(t_service* tservice) { } indent_down(); indent(f_service_) << "};" << endl; - indent(f_service_) << js_const_type_ << "args = new " << argsname << "(params);" << endl; + + // NOTE: "args" is a reserved keyword, so no need to generate a unique identifier + indent(f_service_) << js_const_type_ << "args = new " << argsname << "(" << params_identifier << ");" << endl; } else { indent(f_service_) << js_const_type_ << "args = new " << argsname << "();" << endl; } @@ -2064,7 +2226,7 @@ void t_js_generator::generate_deserialize_field(ostream& out, t_type* type = get_true_type(tfield->get_type()); if (type->is_void()) { - throw "CANNOT GENERATE DESERIALIZE CODE FOR void TYPE: " + prefix + tfield->get_name(); + throw std::runtime_error("CANNOT GENERATE DESERIALIZE CODE FOR void TYPE: " + prefix + tfield->get_name()); } string name = prefix + tfield->get_name(); @@ -2080,7 +2242,7 @@ void t_js_generator::generate_deserialize_field(ostream& out, t_base_type::t_base tbase = ((t_base_type*)type)->get_base(); switch (tbase) { case t_base_type::TYPE_VOID: - throw "compiler error: cannot serialize void field in a struct: " + name; + throw std::runtime_error("compiler error: cannot serialize void field in a struct: " + name); break; case t_base_type::TYPE_STRING: out << (type->is_binary() ? "readBinary()" : "readString()"); @@ -2104,7 +2266,7 @@ void t_js_generator::generate_deserialize_field(ostream& out, out << "readDouble()"; break; default: - throw "compiler error: no JS name for base type " + t_base_type::t_base_name(tbase); + throw std::runtime_error("compiler error: no JS name for base type " + t_base_type::t_base_name(tbase)); } } else if (type->is_enum()) { out << "readI32()"; @@ -2245,7 +2407,7 @@ void t_js_generator::generate_serialize_field(ostream& out, t_field* tfield, str // Do nothing for void types if (type->is_void()) { - throw "CANNOT GENERATE SERIALIZE CODE FOR void TYPE: " + prefix + tfield->get_name(); + throw std::runtime_error("CANNOT GENERATE SERIALIZE CODE FOR void TYPE: " + prefix + tfield->get_name()); } if (type->is_struct() || type->is_xception()) { @@ -2266,7 +2428,7 @@ void t_js_generator::generate_serialize_field(ostream& out, t_field* tfield, str t_base_type::t_base tbase = ((t_base_type*)type)->get_base(); switch (tbase) { case t_base_type::TYPE_VOID: - throw "compiler error: cannot serialize void field in a struct: " + name; + throw std::runtime_error("compiler error: cannot serialize void field in a struct: " + name); break; case t_base_type::TYPE_STRING: out << (type->is_binary() ? "writeBinary(" : "writeString(") << name << ")"; @@ -2290,7 +2452,7 @@ void t_js_generator::generate_serialize_field(ostream& out, t_field* tfield, str out << "writeDouble(" << name << ")"; break; default: - throw "compiler error: no JS name for base type " + t_base_type::t_base_name(tbase); + throw std::runtime_error("compiler error: no JS name for base type " + t_base_type::t_base_name(tbase)); } } else if (type->is_enum()) { out << "writeI32(" << name << ")"; @@ -2438,7 +2600,7 @@ string t_js_generator::declare_field(t_field* tfield, bool init, bool obj) { result += " = null"; break; default: - throw "compiler error: no JS initializer for base type " + t_base_type::t_base_name(tbase); + throw std::runtime_error("compiler error: no JS initializer for base type " + t_base_type::t_base_name(tbase)); } } else if (type->is_enum()) { result += " = null"; @@ -2517,7 +2679,7 @@ string t_js_generator::type_to_enum(t_type* type) { t_base_type::t_base tbase = ((t_base_type*)type)->get_base(); switch (tbase) { case t_base_type::TYPE_VOID: - throw "NO T_VOID CONSTRUCT"; + throw std::runtime_error("NO T_VOID CONSTRUCT"); case t_base_type::TYPE_STRING: return "Thrift.Type.STRING"; case t_base_type::TYPE_BOOL: @@ -2545,7 +2707,7 @@ string t_js_generator::type_to_enum(t_type* type) { return "Thrift.Type.LIST"; } - throw "INVALID TYPE IN type_to_enum: " + type->get_name(); + throw std::runtime_error("INVALID TYPE IN type_to_enum: " + type->get_name()); } /** @@ -2572,18 +2734,33 @@ string t_js_generator::ts_get_type(t_type* type) { break; case t_base_type::TYPE_I16: case t_base_type::TYPE_I32: - case t_base_type::TYPE_I64: case t_base_type::TYPE_DOUBLE: ts_type = "number"; break; + case t_base_type::TYPE_I64: + ts_type = "Int64"; + break; case t_base_type::TYPE_VOID: ts_type = "void"; } } else if (type->is_enum() || type->is_struct() || type->is_xception()) { std::string type_name; + if (type->get_program()) { type_name = js_namespace(type->get_program()); + + // If the type is not defined within the current program, we need to prefix it with the same name as + // the generated "import" statement for the types containing program + if(type->get_program() != program_) { + auto prefix = include_2_import_name.find(type->get_program()); + + if(prefix != include_2_import_name.end()) { + type_name.append(prefix->second); + type_name.append("."); + } + } } + type_name.append(type->get_name()); ts_type = type_name; } else if (type->is_list() || type->is_set()) { @@ -2622,7 +2799,7 @@ string t_js_generator::ts_get_type(t_type* type) { * @param bool in-/exclude the callback argument * @return String of rendered function definition */ -std::string t_js_generator::ts_function_signature(t_function* tfunction, bool include_callback, bool optional_callback) { +std::string t_js_generator::ts_function_signature(t_function* tfunction, bool include_callback) { string str; const vector& fields = tfunction->get_arglist()->get_members(); vector::const_iterator f_iter; @@ -2638,7 +2815,6 @@ std::string t_js_generator::ts_function_signature(t_function* tfunction, bool in } if (include_callback) { - string callback_optional_string = optional_callback ? "?" : ""; if (gen_node_) { t_struct* exceptions = tfunction->get_xceptions(); string exception_types; @@ -2647,19 +2823,19 @@ std::string t_js_generator::ts_function_signature(t_function* tfunction, bool in for (vector::const_iterator it = members.begin(); it != members.end(); ++it) { t_type* t = get_true_type((*it)->get_type()); if (it == members.begin()) { - exception_types = t->get_name(); + exception_types = js_type_namespace(t->get_program()) + t->get_name(); } else { - exception_types += " | " + t->get_name(); + exception_types += " | " + js_type_namespace(t->get_program()) + t->get_name(); } } } if (exception_types == "") { - str += "callback" + callback_optional_string + ": (error: void, response: " + ts_get_type(tfunction->get_returntype()) + ")=>void): "; + str += "callback?: (error: void, response: " + ts_get_type(tfunction->get_returntype()) + ")=>void): "; } else { - str += "callback" + callback_optional_string + ": (error: " + exception_types + ", response: " + ts_get_type(tfunction->get_returntype()) + ")=>void): "; + str += "callback?: (error: " + exception_types + ", response: " + ts_get_type(tfunction->get_returntype()) + ")=>void): "; } } else { - str += "callback" + callback_optional_string + ": (data: " + ts_get_type(tfunction->get_returntype()) + ")=>void): "; + str += "callback?: (data: " + ts_get_type(tfunction->get_returntype()) + ")=>void): "; } if (gen_jquery_) { @@ -2712,10 +2888,107 @@ std::string t_js_generator::make_valid_nodeJs_identifier(std::string const& name return str; } +void t_js_generator::parse_imports(t_program* program, const std::string& imports_string) { + if (program->get_recursive()) { + throw std::invalid_argument("[-gen js:imports=] option is not usable in recursive code generation mode"); + } + std::stringstream sstream(imports_string); + std::string import; + while (std::getline(sstream, import, ':')) { + imports.emplace_back(import); + } + if (imports.empty()) { + throw std::invalid_argument("invalid usage: [-gen js:imports=] requires at least one path " + "(multiple paths are separated by ':')"); + } + for (auto& import : imports) { + // Strip trailing '/' + if (!import.empty() && import[import.size() - 1] == '/') { + import = import.substr(0, import.size() - 1); + } + if (import.empty()) { + throw std::invalid_argument("empty paths are not allowed in imports"); + } + std::ifstream episode_file; + string line; + const auto episode_file_path = import + "/" + episode_file_name; + episode_file.open(episode_file_path); + if (!episode_file) { + throw std::runtime_error("failed to open the file '" + episode_file_path + "'"); + } + while (std::getline(episode_file, line)) { + const auto separator_position = line.find(':'); + if (separator_position == string::npos) { + // could not find the separator in the line + throw std::runtime_error("the episode file '" + episode_file_path + "' is malformed, the line '" + line + + "' does not have a key:value separator ':'"); + } + const auto module_name = line.substr(0, separator_position); + const auto import_path = line.substr(separator_position + 1); + if (module_name.empty()) { + throw std::runtime_error("the episode file '" + episode_file_path + "' is malformed, the module name is empty"); + } + if (import_path.empty()) { + throw std::runtime_error("the episode file '" + episode_file_path + "' is malformed, the import path is empty"); + } + const auto module_import_path = import.substr(import.find_last_of('/') + 1) + "/" + import_path; + const auto result = module_name_2_import_path.emplace(module_name, module_import_path); + if (!result.second) { + throw std::runtime_error("multiple providers of import path found for " + module_name + + "\n\t" + module_import_path + "\n\t" + result.first->second); + } + } + } +} +void t_js_generator::parse_thrift_package_output_directory(const std::string& thrift_package_output_directory) { + thrift_package_output_directory_ = thrift_package_output_directory; + // Strip trailing '/' + if (!thrift_package_output_directory_.empty() && thrift_package_output_directory_[thrift_package_output_directory_.size() - 1] == '/') { + thrift_package_output_directory_ = thrift_package_output_directory_.substr(0, thrift_package_output_directory_.size() - 1); + } + // Check that the thrift_package_output_directory is not empty after stripping + if (thrift_package_output_directory_.empty()) { + throw std::invalid_argument("the thrift_package_output_directory argument must not be empty"); + } else { + gen_episode_file_ = true; + } +} + +/** + * Checks is the specified field name is contained in the specified field vector + */ +bool t_js_generator::find_field(const std::vector& fields, const std::string& name) { + vector::const_iterator f_iter; + for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { + if ((*f_iter)->get_name() == name) { + return true; + } + } + + return false; +} + +/** + * Given a vector of fields, generate a valid identifier name that does not conflict with avaliable field names + */ +std::string t_js_generator::next_identifier_name(const std::vector& fields, const std::string& base_name) { + // Search through fields until a match is not found, if a match is found prepend "_" to the identifier name + std::string current_name = this->make_valid_nodeJs_identifier(base_name); + while(this->find_field(fields, current_name)) { + current_name = this->make_valid_nodeJs_identifier("_" + current_name); + } + + return current_name; +} + THRIFT_REGISTER_GENERATOR(js, "Javascript", " jquery: Generate jQuery compatible code.\n" " node: Generate node.js compatible code.\n" " ts: Generate TypeScript definition files.\n" " with_ns: Create global namespace objects when using node.js\n" - " es6: Create ES6 code with Promises\n") + " es6: Create ES6 code with Promises\n" + " thrift_package_output_directory=:\n" + " Generate episode file and use the as prefix\n" + " imports=:\n" + " ':' separated list of paths of modules that has episode files in their root\n") diff --git a/compiler/cpp/src/thrift/generate/t_json_generator.cc b/compiler/cpp/src/thrift/generate/t_json_generator.cc index cd5361274c8..eb4ef790be8 100644 --- a/compiler/cpp/src/thrift/generate/t_json_generator.cc +++ b/compiler/cpp/src/thrift/generate/t_json_generator.cc @@ -68,23 +68,23 @@ class t_json_generator : public t_generator { out_dir_base_ = "gen-json"; } - virtual ~t_json_generator() {} + ~t_json_generator() override = default; /** * Init and close methods */ - void init_generator(); - void close_generator(); + void init_generator() override; + void close_generator() override; - void generate_typedef(t_typedef* ttypedef); - void generate_enum(t_enum* tenum); - void generate_program(); + void generate_typedef(t_typedef* ttypedef) override; + void generate_enum(t_enum* tenum) override; + void generate_program() override; void generate_function(t_function* tfunc); void generate_field(t_field* field); - void generate_service(t_service* tservice); - void generate_struct(t_struct* tstruct); + void generate_service(t_service* tservice) override; + void generate_struct(t_struct* tstruct) override; private: bool should_merge_includes_; @@ -147,8 +147,8 @@ void t_json_generator::init_generator() { string t_json_generator::escape_json_string(const string& input) { std::ostringstream ss; - for (std::string::const_iterator iter = input.begin(); iter != input.end(); iter++) { - switch (*iter) { + for (char iter : input) { + switch (iter) { case '\\': ss << "\\\\"; break; @@ -174,7 +174,7 @@ string t_json_generator::escape_json_string(const string& input) { ss << "\\t"; break; default: - ss << *iter; + ss << iter; break; } } @@ -267,8 +267,8 @@ void t_json_generator::write_type_spec(t_type* ttype) { if (ttype->annotations_.size() > 0) { write_key_and("annotations"); start_object(); - for (map::iterator it = ttype->annotations_.begin(); it != ttype->annotations_.end(); ++it) { - write_key_and_string(it->first, it->second); + for (auto & annotation : ttype->annotations_) { + write_key_and_string(annotation.first, annotation.second); } end_object(); } @@ -457,8 +457,8 @@ void t_json_generator::generate_typedef(t_typedef* ttypedef) { if (ttypedef->annotations_.size() > 0) { write_key_and("annotations"); start_object(); - for (map::iterator it = ttypedef->annotations_.begin(); it != ttypedef->annotations_.end(); ++it) { - write_key_and_string(it->first, it->second); + for (auto & annotation : ttypedef->annotations_) { + write_key_and_string(annotation.first, annotation.second); } end_object(); } @@ -564,8 +564,8 @@ void t_json_generator::generate_enum(t_enum* tenum) { if (tenum->annotations_.size() > 0) { write_key_and("annotations"); start_object(); - for (map::iterator it = tenum->annotations_.begin(); it != tenum->annotations_.end(); ++it) { - write_key_and_string(it->first, it->second); + for (auto & annotation : tenum->annotations_) { + write_key_and_string(annotation.first, annotation.second); } end_object(); } @@ -603,8 +603,8 @@ void t_json_generator::generate_struct(t_struct* tstruct) { if (tstruct->annotations_.size() > 0) { write_key_and("annotations"); start_object(); - for (map::iterator it = tstruct->annotations_.begin(); it != tstruct->annotations_.end(); ++it) { - write_key_and_string(it->first, it->second); + for (auto & annotation : tstruct->annotations_) { + write_key_and_string(annotation.first, annotation.second); } end_object(); } @@ -643,8 +643,8 @@ void t_json_generator::generate_service(t_service* tservice) { if (tservice->annotations_.size() > 0) { write_key_and("annotations"); start_object(); - for (map::iterator it = tservice->annotations_.begin(); it != tservice->annotations_.end(); ++it) { - write_key_and_string(it->first, it->second); + for (auto & annotation : tservice->annotations_) { + write_key_and_string(annotation.first, annotation.second); } end_object(); } @@ -680,8 +680,8 @@ void t_json_generator::generate_function(t_function* tfunc) { if (tfunc->annotations_.size() > 0) { write_key_and("annotations"); start_object(); - for (map::iterator it = tfunc->annotations_.begin(); it != tfunc->annotations_.end(); ++it) { - write_key_and_string(it->first, it->second); + for (auto & annotation : tfunc->annotations_) { + write_key_and_string(annotation.first, annotation.second); } end_object(); } @@ -726,8 +726,8 @@ void t_json_generator::generate_field(t_field* field) { if (field->annotations_.size() > 0) { write_key_and("annotations"); start_object(); - for (map::iterator it = field->annotations_.begin(); it != field->annotations_.end(); ++it) { - write_key_and_string(it->first, it->second); + for (auto & annotation : field->annotations_) { + write_key_and_string(annotation.first, annotation.second); } end_object(); } diff --git a/compiler/cpp/src/thrift/generate/t_lua_generator.cc b/compiler/cpp/src/thrift/generate/t_lua_generator.cc index adee8968a69..17dbac7ff71 100644 --- a/compiler/cpp/src/thrift/generate/t_lua_generator.cc +++ b/compiler/cpp/src/thrift/generate/t_lua_generator.cc @@ -56,18 +56,18 @@ class t_lua_generator : public t_oop_generator { /** * Init and close methods */ - void init_generator(); - void close_generator(); + void init_generator() override; + void close_generator() override; /** * Program-level generation functions */ - void generate_typedef(t_typedef* ttypedef); - void generate_enum(t_enum* tenum); - void generate_const(t_const* tconst); - void generate_struct(t_struct* tstruct); - void generate_xception(t_struct* txception); - void generate_service(t_service* tservice); + void generate_typedef(t_typedef* ttypedef) override; + void generate_enum(t_enum* tenum) override; + void generate_const(t_const* tconst) override; + void generate_struct(t_struct* tstruct) override; + void generate_xception(t_struct* txception) override; + void generate_service(t_service* tservice) override; std::string render_const_value(t_type* type, t_const_value* value); @@ -149,7 +149,7 @@ class t_lua_generator : public t_oop_generator { std::string type_to_enum(t_type* ttype); static std::string get_namespace(const t_program* program); - std::string autogen_comment() { + std::string autogen_comment() override { return std::string("--\n") + "-- Autogenerated by Thrift\n" + "--\n" + "-- DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING\n" + "-- @" "generated\n" @@ -197,6 +197,9 @@ void t_lua_generator::close_generator() { * Generate a typedef (essentially a constant) */ void t_lua_generator::generate_typedef(t_typedef* ttypedef) { + if (ttypedef->get_type()->get_name().empty()) { + return; + } f_types_ << endl << endl << indent() << ttypedef->get_symbolic() << " = " << ttypedef->get_type()->get_name(); } @@ -279,13 +282,13 @@ string t_lua_generator::render_const_value(t_type* type, t_const_value* value) { const map& val = value->get_map(); map::const_iterator v_iter; for (v_iter = val.begin(); v_iter != val.end();) { - t_type* field_type = NULL; + t_type* field_type = nullptr; for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { if ((*f_iter)->get_name() == v_iter->first->get_string()) { field_type = (*f_iter)->get_type(); } } - if (field_type == NULL) { + if (field_type == nullptr) { throw "type error: " + type->get_name() + " has no field " + v_iter->first->get_string(); } @@ -510,7 +513,7 @@ void t_lua_generator::generate_service(t_service* tservice) { if (gen_requires_) { f_service_ << endl << "require '" << cur_ns << "ttypes'" << endl; - if (tservice->get_extends() != NULL) { + if (tservice->get_extends() != nullptr) { f_service_ << "require '" << get_namespace(tservice->get_extends()->get_program()) << tservice->get_extends()->get_name() << "'" << endl; } @@ -547,7 +550,7 @@ void t_lua_generator::generate_service_client(ostream& out, t_service* tservice) // Client object definition out << classname << " = __TObject.new("; - if (extends_s != NULL) { + if (extends_s != nullptr) { out << extends_s->get_name() << "Client"; } else { out << "__TClient"; @@ -643,7 +646,7 @@ void t_lua_generator::generate_service_processor(ostream& out, t_service* tservi // Define processor table out << endl << classname << " = __TObject.new("; - if (extends_s != NULL) { + if (extends_s != nullptr) { out << extends_s->get_name() << "Processor" << endl; } else { out << "__TProcessor" << endl; @@ -656,7 +659,9 @@ void t_lua_generator::generate_service_processor(ostream& out, t_service* tservi indent(out) << "local name, mtype, seqid = iprot:readMessageBegin()" << endl; indent(out) << "local func_name = 'process_' .. name" << endl; - indent(out) << "if not self[func_name] or ttype(self[func_name]) ~= 'function' then"; + indent(out) << "if not self[func_name] or ttype(self[func_name]) ~= 'function' then" << endl; + indent_up(); + indent(out) << "if oprot ~= nil then"; indent_up(); out << endl << indent() << "iprot:skip(TType.STRUCT)" << endl << indent() << "iprot:readMessageEnd()" << endl << indent() << "x = TApplicationException:new{" << endl @@ -665,8 +670,11 @@ void t_lua_generator::generate_service_processor(ostream& out, t_service* tservi << "seqid)" << endl << indent() << "x:write(oprot)" << endl << indent() << "oprot:writeMessageEnd()" << endl << indent() << "oprot.trans:flush()" << endl; indent_down(); + out << indent() << "end" << endl << indent() + << "return false, 'Unknown function '..name" << endl; + indent_down(); indent(out) << "else" << endl << indent() - << " self[func_name](self, seqid, iprot, oprot, server_ctx)" << endl << indent() + << " return self[func_name](self, seqid, iprot, oprot, server_ctx)" << endl << indent() << "end" << endl; indent_down(); @@ -695,37 +703,45 @@ void t_lua_generator::generate_process_function(ostream& out, // Read the request out << indent() << "local args = " << argsname << ":new{}" << endl << indent() << "local reply_type = TMessageType.REPLY" << endl << indent() << "args:read(iprot)" << endl - << indent() << "iprot:readMessageEnd()" << endl << indent() << "local result = " << resultname - << ":new{}" << endl << indent() << "local status, res = pcall(self.handler." << fn_name - << ", self.handler"; + << indent() << "iprot:readMessageEnd()" << endl; + if (!tfunction->is_oneway()) { + out << indent() << "local result = " << resultname + << ":new{}" << endl; + } + + out << indent() << "local status, res = pcall(self.handler." << fn_name + << ", self.handler"; // Print arguments t_struct* args = tfunction->get_arglist(); if (args->get_members().size() > 0) { out << ", " << argument_list(args, "args."); } + out << ")" << endl; - // Check for errors - out << ")" << endl << indent() << "if not status then" << endl << indent() - << " reply_type = TMessageType.EXCEPTION" << endl << indent() - << " result = TApplicationException:new{message = res}" << endl; - - // Handle custom exceptions - const std::vector& xf = tfunction->get_xceptions()->get_members(); - if (xf.size() > 0) { - vector::const_iterator x_iter; - for (x_iter = xf.begin(); x_iter != xf.end(); ++x_iter) { - out << indent() << "elseif ttype(res) == '" << (*x_iter)->get_type()->get_name() << "' then" - << endl << indent() << " result." << (*x_iter)->get_name() << " = res" << endl; - } - } - - // Set the result and write the reply - out << indent() << "else" << endl << indent() << " result.success = res" << endl << indent() - << "end" << endl << indent() << "oprot:writeMessageBegin('" << fn_name << "', reply_type, " - << "seqid)" << endl << indent() << "result:write(oprot)" << endl << indent() - << "oprot:writeMessageEnd()" << endl << indent() << "oprot.trans:flush()" << endl; + if (!tfunction->is_oneway()) { + // Check for errors + out << indent() << "if not status then" << endl << indent() + << " reply_type = TMessageType.EXCEPTION" << endl << indent() + << " result = TApplicationException:new{message = res}" << endl; + + // Handle custom exceptions + const std::vector& xf = tfunction->get_xceptions()->get_members(); + if (xf.size() > 0) { + vector::const_iterator x_iter; + for (x_iter = xf.begin(); x_iter != xf.end(); ++x_iter) { + out << indent() << "elseif ttype(res) == '" << (*x_iter)->get_type()->get_name() << "' then" + << endl << indent() << " result." << (*x_iter)->get_name() << " = res" << endl; + } + } + // Set the result and write the reply + out << indent() << "else" << endl << indent() << " result.success = res" << endl << indent() + << "end" << endl << indent() << "oprot:writeMessageBegin('" << fn_name << "', reply_type, " + << "seqid)" << endl << indent() << "result:write(oprot)" << endl << indent() + << "oprot:writeMessageEnd()" << endl << indent() << "oprot.trans:flush()" << endl; + } + out << indent() << "return status, res" << endl; indent_down(); indent(out) << "end" << endl; } diff --git a/compiler/cpp/src/thrift/generate/t_markdown_generator.cc b/compiler/cpp/src/thrift/generate/t_markdown_generator.cc new file mode 100644 index 00000000000..17e90f75998 --- /dev/null +++ b/compiler/cpp/src/thrift/generate/t_markdown_generator.cc @@ -0,0 +1,1110 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT 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 +#include +#include +#include +#include +#include + +#include +#include +#include +#include "thrift/platform.h" +#include "thrift/generate/t_generator.h" + +using std::map; +using std::ofstream; +using std::ostringstream; +using std::pair; +using std::string; +using std::stringstream; +using std::vector; + +static const char endl = '\n'; // avoid ostream << std::endl flushes + + +/** + * MARKDOWN code generator + * + * mostly copy/pasting/tweaking from t_html_generator's work. + */ +class t_markdown_generator : public t_generator { + +enum input_type { INPUT_UNKNOWN, INPUT_UTF8, INPUT_PLAIN }; + +public: + t_markdown_generator(t_program* program, + const std::map& parsed_options, + const std::string& option_string) + : t_generator(program) { + + (void)option_string; + std::map::const_iterator iter; + + unsafe_ = false; + for( iter = parsed_options.begin(); iter != parsed_options.end(); ++iter) { + if( iter->first.compare("noescape") == 0) { + unsafe_ = true; + } else if( iter->first.compare("suffix") == 0 && !iter->second.empty()) { + extension_ = "." + iter->second; + } else { + throw "unknown option markdown:" + iter->first; + } + } + + + out_dir_base_ = "gen-markdown"; + input_type_ = INPUT_UNKNOWN; + + escape_.clear(); + escape_['&'] = "&"; + escape_['<'] = "<"; + escape_['>'] = ">"; + escape_['"'] = """; + escape_['\''] = "'"; + + init_allowed__markup(); + } + + void generate_program() override; + void generate_program_toc(); + void generate_program_toc_row(t_program* tprog); + void generate_program_toc_rows(t_program* tprog, std::vector& finished); + void generate_index(); + std::string escape_html(std::string const& str); + std::string escape_html_tags(std::string const& str); + std::string make_file_link(std::string name); + std::string make_file_name(std::string name); + bool is_utf8_sequence(std::string const& str, size_t firstpos); + void detect_input_encoding(std::string const& str, size_t firstpos); + void init_allowed__markup(); + + /** + * Program-level generation functions + */ + + void generate_typedef(t_typedef* ttypedef) override; + void generate_enum(t_enum* tenum) override; + void generate_const(t_const* tconst) override; + void generate_struct(t_struct* tstruct) override; + void generate_service(t_service* tservice) override; + void generate_xception(t_struct* txception) override; + + void print_doc(t_doc* tdoc); + int print_type(t_type* ttype); + void print_const_value(t_type* type, t_const_value* tvalue); + void print_fn_args_doc(t_function* tfunction); + + std::string str_to_id(const std::string& s); + +private: + ofstream_with_content_based_conditional_update f_out_; + std::string current_file_; + input_type input_type_; + std::map allowed_markup; + bool unsafe_; + std::string extension_; +}; + + +/** + * string to markdown-id link reference + */ +std::string t_markdown_generator::str_to_id(const std::string& s) { + std::string id; + for(auto chr=s.begin();chr<=s.end(); ++chr) { + if(*chr == '.' || *chr == 0) + continue; + id += tolower(*chr); + } + return id; +} + +/** + * Emits the Table of Contents links at the top of the module's page + */ +void t_markdown_generator::generate_program_toc() { + f_out_ << "| Module | Services & Functions | Data types | Constants |" << endl + << "| --- | --- | --- | --- |" << endl; + generate_program_toc_row(program_); + f_out_ << endl; +} + +/** + * Recurses through from the provided program and generates a ToC row + * for each discovered program exactly once by maintaining the list of + * completed rows in 'finished' + */ +void t_markdown_generator::generate_program_toc_rows(t_program* tprog, + std::vector& finished) { + for (auto & iter : finished) { + if (tprog->get_path() == iter->get_path()) { + return; + } + } + finished.push_back(tprog); + generate_program_toc_row(tprog); + vector includes = tprog->get_includes(); + for (auto & include : includes) { + generate_program_toc_rows(include, finished); + } +} + +/** + * Emits the Table of Contents links at the top of the module's page + */ +void t_markdown_generator::generate_program_toc_row(t_program* tprog) { + // "| Module | Services | Data types | Constants | + vector> filling; + + string fname = make_file_name(tprog->get_name()); + filling.emplace_back(); + auto fill = &filling.back(); + (*fill)[0] = tprog->get_name(); + + if (!tprog->get_services().empty()) { + vector services = tprog->get_services(); + vector::iterator sv_iter; + for (sv_iter = services.begin(); sv_iter != services.end(); ++sv_iter) { + if(sv_iter != services.begin()) { + filling.emplace_back(); + fill = &filling.back(); + } + string name = get_service_name(*sv_iter); + (*fill)[1] = "[" + name + "](" + + make_file_link(fname) + + "#service-" + str_to_id(name) + ")"; + + vector functions = (*sv_iter)->get_functions(); + vector::iterator fn_iter; + for (fn_iter = functions.begin(); fn_iter != functions.end(); ++fn_iter) { + string fn_name = (*fn_iter)->get_name(); + filling.emplace_back(); + fill = &filling.back(); + (*fill)[1] = " [ • " + fn_name + "](" + + make_file_link(fname) + + "#function-" + str_to_id(name + fn_name) + ")"; + } + } + } + + // Data Types Column + auto it_fill = filling.begin(); + + if (!tprog->get_enums().empty()) { + vector enums = tprog->get_enums(); + vector::iterator en_iter; + for (en_iter = enums.begin(); en_iter != enums.end(); ++en_iter) { + if(it_fill == filling.end()) { + filling.emplace_back(); + fill = &filling.back(); + it_fill = filling.end(); + } else { + fill = &*it_fill; + ++it_fill; + } + string name = (*en_iter)->get_name(); + (*fill)[2] = "[" + name + "](" + + make_file_link(fname) + + "#enumeration-" + str_to_id(name) + ")"; + } + } + if (!tprog->get_typedefs().empty()) { + vector typedefs = tprog->get_typedefs(); + vector::iterator td_iter; + for (td_iter = typedefs.begin(); td_iter != typedefs.end(); ++td_iter) { + if(it_fill == filling.end()) { + filling.emplace_back(); + fill = &filling.back(); + it_fill = filling.end(); + } else { + fill = &*it_fill; + ++it_fill; + } + string name = (*td_iter)->get_symbolic(); + (*fill)[2] = "[" + name + "](" + + make_file_link(fname) + + "#typedef-" + str_to_id(name) + ")"; + } + } + if (!tprog->get_objects().empty()) { + vector objects = tprog->get_objects(); + vector::iterator o_iter; + for (o_iter = objects.begin(); o_iter != objects.end(); ++o_iter) { + if(it_fill == filling.end()) { + filling.emplace_back(); + fill = &filling.back(); + it_fill = filling.end(); + } else { + fill = &*it_fill; + ++it_fill; + } + string name = (*o_iter)->get_name(); + (*fill)[2] = "[" + name + "](" + make_file_link(fname); + + if ((*o_iter)->is_xception()) { + (*fill)[2] += "#exception-"; + } else if ((*o_iter)->is_struct() && (*o_iter)->is_union()) { + (*fill)[2] += "#union-"; + } else { + (*fill)[2] += "#struct-"; + } + (*fill)[2] += str_to_id(name) + ")"; + + } + } + + // Constants Column + it_fill = filling.begin(); + + if (!tprog->get_consts().empty()) { + map const_markdown; + vector consts = tprog->get_consts(); + vector::iterator con_iter; + for (con_iter = consts.begin(); con_iter != consts.end(); ++con_iter) { + if(it_fill == filling.end()) { + filling.emplace_back(); + fill = &filling.back(); + it_fill = filling.end(); + } else { + fill = &*it_fill; + ++it_fill; + } + string name = (*con_iter)->get_name(); + (*fill)[3] = "[" + name + "](" + + make_file_link(fname) + + "#constant-" + str_to_id(name) + ")"; + } + + } + + for(auto& fill : filling) { + for(auto& c : fill) + f_out_ << '|' << c; + f_out_ << '|' << endl; + } + f_out_ << endl; +} + +/** + * Prepares for file generation by opening up the necessary file output + * stream. + */ +void t_markdown_generator::generate_program() { + // Make output directory + MKDIR(get_out_dir().c_str()); + + string pname = program_->get_name(); + current_file_ = make_file_name(pname); + string fname = get_out_dir() + current_file_; + f_out_.open(fname.c_str()); + f_out_ << "# Thrift module: " << pname << endl << endl; + + print_doc(program_); + f_out_ << endl << endl; + + generate_program_toc(); + + if (!program_->get_consts().empty()) { + f_out_ << "***" << endl << "## Constants" << endl << endl; + vector consts = program_->get_consts(); + f_out_ << "|Constant|Type|Value||" << endl + << "|---|---|---|---|" << endl; + generate_consts(consts); + f_out_ << endl; + } + + if (!program_->get_enums().empty()) { + f_out_ << "***" << endl << "## Enumerations" << endl << endl; + // Generate enums + vector enums = program_->get_enums(); + vector::iterator en_iter; + for (en_iter = enums.begin(); en_iter != enums.end(); ++en_iter) { + generate_enum(*en_iter); + } + } + + if (!program_->get_typedefs().empty()) { + f_out_ << "***" << endl << "## Type declarations" << endl << endl; + // Generate typedefs + vector typedefs = program_->get_typedefs(); + vector::iterator td_iter; + for (td_iter = typedefs.begin(); td_iter != typedefs.end(); ++td_iter) { + generate_typedef(*td_iter); + } + } + + if (!program_->get_objects().empty()) { + f_out_ << "***" << endl << "## Data structures" << endl << endl; + // Generate structs and exceptions in declared order + vector objects = program_->get_objects(); + vector::iterator o_iter; + for (o_iter = objects.begin(); o_iter != objects.end(); ++o_iter) { + if ((*o_iter)->is_xception()) { + generate_xception(*o_iter); + } else { + generate_struct(*o_iter); + } + } + } + + if (!program_->get_services().empty()) { + f_out_ << "***" << endl << "## Services" << endl << endl; + // Generate services + vector services = program_->get_services(); + vector::iterator sv_iter; + for (sv_iter = services.begin(); sv_iter != services.end(); ++sv_iter) { + service_name_ = get_service_name(*sv_iter); + generate_service(*sv_iter); + } + } + + f_out_ << endl; + f_out_.close(); + + generate_index(); +} + +/** + * Emits the index(.ext) file for the recursive set of Thrift programs + */ +void t_markdown_generator::generate_index() { + current_file_ = make_file_name("index"); + string index_fname = get_out_dir() + current_file_; + f_out_.open(index_fname.c_str()); + + f_out_ << "# Thrift declarations" << endl; + f_out_ << "| Module | Services & Functions | Data types | Constants |" + << endl + << "| --- | --- | --- | --- |" + << endl; + vector programs; + generate_program_toc_rows(program_, programs); + f_out_ << endl; + f_out_.close(); +} + + + +/** + * Returns the target file for a link + * The returned string is empty, whenever filename refers to the current file. + */ +std::string t_markdown_generator::make_file_link(std::string filename) { + return (current_file_.compare(filename) != 0) ? filename : ""; +} +/** + * Returns the target file for a link + * The returned string is empty, whenever filename refers to the current file. + */ +std::string t_markdown_generator::make_file_name(std::string filename) { + return extension_.empty() ? filename : (filename + extension_); +} + + +/** + * If the provided documentable object has documentation attached, this + * will emit it to the output stream in HTML format. + */ +void t_markdown_generator::print_doc(t_doc* tdoc) { + if (tdoc->has_doc()) { + if (unsafe_) { + f_out_ << tdoc->get_doc(); + } else { + f_out_ << escape_html(tdoc->get_doc()); + } + } +} + +bool t_markdown_generator::is_utf8_sequence(std::string const& str, size_t firstpos) { + // leading char determines the length of the sequence + unsigned char c = str.at(firstpos); + int count = 0; + if ((c & 0xE0) == 0xC0) { + count = 1; + } else if ((c & 0xF0) == 0xE0) { + count = 2; + } else if ((c & 0xF8) == 0xF0) { + count = 3; + } else if ((c & 0xFC) == 0xF8) { + count = 4; + } else if ((c & 0xFE) == 0xFC) { + count = 5; + } else { + // pdebug("UTF-8 test: char '%c' (%d) is not a valid UTF-8 leading byte", c, int(c)); + return false; // no UTF-8 + } + + // following chars + size_t pos = firstpos + 1; + while ((pos < str.length()) && (0 < count)) { + c = str.at(pos); + if ((c & 0xC0) != 0x80) { + // pdebug("UTF-8 test: char '%c' (%d) is not a valid UTF-8 following byte", c, int(c)); + return false; // no UTF-8 + } + --count; + ++pos; + } + + // true if the sequence is complete + return (0 == count); +} + +void t_markdown_generator::detect_input_encoding(std::string const& str, size_t firstpos) { + if (is_utf8_sequence(str, firstpos)) { + pdebug("Input seems to be already UTF-8 encoded"); + input_type_ = INPUT_UTF8; + return; + } + + // fallback + pwarning(1, "Input is not UTF-8, treating as plain ANSI"); + input_type_ = INPUT_PLAIN; +} + +void t_markdown_generator::init_allowed__markup() { + allowed_markup.clear(); + // standalone tags + allowed_markup["br"] = 1; + allowed_markup["br/"] = 1; + allowed_markup["img"] = 1; + // paired tags + allowed_markup["b"] = 1; + allowed_markup["/b"] = 1; + allowed_markup["u"] = 1; + allowed_markup["/u"] = 1; + allowed_markup["i"] = 1; + allowed_markup["/i"] = 1; + allowed_markup["s"] = 1; + allowed_markup["/s"] = 1; + allowed_markup["big"] = 1; + allowed_markup["/big"] = 1; + allowed_markup["small"] = 1; + allowed_markup["/small"] = 1; + allowed_markup["sup"] = 1; + allowed_markup["/sup"] = 1; + allowed_markup["sub"] = 1; + allowed_markup["/sub"] = 1; + allowed_markup["pre"] = 1; + allowed_markup["/pre"] = 1; + allowed_markup["tt"] = 1; + allowed_markup["/tt"] = 1; + allowed_markup["ul"] = 1; + allowed_markup["/ul"] = 1; + allowed_markup["ol"] = 1; + allowed_markup["/ol"] = 1; + allowed_markup["li"] = 1; + allowed_markup["/li"] = 1; + allowed_markup["a"] = 1; + allowed_markup["/a"] = 1; + allowed_markup["p"] = 1; + allowed_markup["/p"] = 1; + allowed_markup["code"] = 1; + allowed_markup["/code"] = 1; + allowed_markup["dl"] = 1; + allowed_markup["/dl"] = 1; + allowed_markup["dt"] = 1; + allowed_markup["/dt"] = 1; + allowed_markup["dd"] = 1; + allowed_markup["/dd"] = 1; + allowed_markup["h1"] = 1; + allowed_markup["/h1"] = 1; + allowed_markup["h2"] = 1; + allowed_markup["/h2"] = 1; + allowed_markup["h3"] = 1; + allowed_markup["/h3"] = 1; + allowed_markup["h4"] = 1; + allowed_markup["/h4"] = 1; + allowed_markup["h5"] = 1; + allowed_markup["/h5"] = 1; + allowed_markup["h6"] = 1; + allowed_markup["/h6"] = 1; +} + +std::string t_markdown_generator::escape_html_tags(std::string const& str) { + std::ostringstream result; + + unsigned char c = '?'; + size_t lastpos; + size_t firstpos = 0; + while (firstpos < str.length()) { + + // look for non-ASCII char + lastpos = firstpos; + while (lastpos < str.length()) { + c = str.at(lastpos); + if (('<' == c) || ('>' == c)) { + break; + } + ++lastpos; + } + + // copy what we got so far + if (lastpos > firstpos) { + result << str.substr(firstpos, lastpos - firstpos); + firstpos = lastpos; + } + + // reached the end? + if (firstpos >= str.length()) { + break; + } + + // tag end without corresponding begin + ++firstpos; + if ('>' == c) { + result << ">"; + continue; + } + + // extract the tag + std::ostringstream tagstream; + while (firstpos < str.length()) { + c = str.at(firstpos); + ++firstpos; + if ('<' == c) { + tagstream << "<"; // nested begin? + } else if ('>' == c) { + break; + } else { + tagstream << c; // not very efficient, but tags should be quite short + } + } + + // we allow for several markup in docstrings, all else will become escaped + string tag_content = tagstream.str(); + string tag_key = tag_content; + size_t first_white = tag_key.find_first_of(" \t\f\v\n\r"); + if (first_white != string::npos) { + tag_key.erase(first_white); + } + for (char & i : tag_key) { + i = tolower(i); + } + if (allowed_markup.find(tag_key) != allowed_markup.end()) { + result << "<" << tag_content << ">"; + } else { + result << "<" << tagstream.str() << ">"; + pverbose("illegal markup <%s> in doc-comment\n", tag_key.c_str()); + } + } + + return result.str(); +} + +std::string t_markdown_generator::escape_html(std::string const& str) { + // the generated HTML header says it is UTF-8 encoded + // if UTF-8 input has been detected before, we don't need to change anything + //if (input_type_ == INPUT_UTF8) { + // return escape_html_tags(str); + //} + + // convert unsafe chars to their &#; equivalent + std::ostringstream result; + unsigned char c = '?'; + unsigned int ic = 0; + size_t lastpos; + size_t firstpos = 0; + while (firstpos < str.length()) { + + // look for non-ASCII char + lastpos = firstpos; + while (lastpos < str.length()) { + c = str.at(lastpos); + ic = c; + if ((32 > ic) || (127 < ic)) { + break; + } + ++lastpos; + } + + // copy what we got so far + if (lastpos > firstpos) { + result << str.substr(firstpos, lastpos - firstpos); + firstpos = lastpos; + } + + // reached the end? + if (firstpos >= str.length()) { + break; + } + + // some control code? + if (ic <= 31) { + switch (c) { + case '\r': + case '\n': + case '\t': + result << ' '; + break; + default: // silently consume all other ctrl chars + break; + } + ++firstpos; + continue; + } + + // reached the end? + if (firstpos >= str.length()) { + break; + } + + // try to detect input encoding + if (input_type_ == INPUT_UNKNOWN) { + detect_input_encoding(str, firstpos); + if (input_type_ == INPUT_UTF8) { + lastpos = str.length(); + result << str.substr(firstpos, lastpos - firstpos); + break; + } + } + + // convert the character to something useful based on the detected encoding + switch (input_type_) { + case INPUT_PLAIN: + result << "&#" << ic << ";"; + ++firstpos; + break; + default: + throw "Unexpected or unrecognized input encoding"; + } + } + + return escape_html_tags(result.str()); +} + +/** + * Prints out the provided type in Markdown + */ +int t_markdown_generator::print_type(t_type* ttype) { + std::string::size_type len = 0; + if (ttype->is_container()) { + if (ttype->is_list()) { + f_out_ << "list<"; + len = 6 + print_type(((t_list*)ttype)->get_elem_type()); + f_out_ << ">"; + } else if (ttype->is_set()) { + f_out_ << "set<"; + len = 5 + print_type(((t_set*)ttype)->get_elem_type()); + f_out_ << ">"; + } else if (ttype->is_map()) { + f_out_ << "map<"; + len = 5 + print_type(((t_map*)ttype)->get_key_type()); + f_out_ << ", "; + len += print_type(((t_map*)ttype)->get_val_type()); + f_out_ << ">"; + } + } else if (ttype->is_base_type()) { + f_out_ << "```" << (ttype->is_binary() ? "binary" : ttype->get_name()) + << "```"; + len = ttype->get_name().size(); + } else { + string prog_name = ttype->get_program()->get_name(); + string type_name = ttype->get_name(); + f_out_ << "[```" << type_name << "```](" + << make_file_link(make_file_name(prog_name)) << "#"; + if (ttype->is_typedef()) { + f_out_ << "typedef-"; + } else if (ttype->is_xception()) { + f_out_ << "exception-"; + } else if (ttype->is_struct()) { + if(((t_struct*)ttype)->is_union()) + f_out_ << "union-"; + else + f_out_ << "struct-"; + } else if (ttype->is_enum()) { + f_out_ << "enumeration-"; + } else if (ttype->is_service()) { + f_out_ << "service-"; + } + len = type_name.size(); + if (ttype->get_program() != program_) { + f_out_ << str_to_id(prog_name); + len += prog_name.size() + 1; + } + f_out_ << str_to_id(type_name) << ')'; + } + return (int)len; +} + +/** + * Prints out an Markdown representation of the provided constant value + */ +void t_markdown_generator::print_const_value(t_type* type, t_const_value* tvalue) { + + // if tvalue is an identifier, the constant content is already shown elsewhere + if (tvalue->get_type() == t_const_value::CV_IDENTIFIER) { + string fname = make_file_name(program_->get_name()); + string name = escape_html(tvalue->get_identifier()); + f_out_ << "[```" << name << "```](" + + make_file_link(fname) + + "#constant-" + str_to_id(name) + ")"; + return; + } + + t_type* truetype = type; + while (truetype->is_typedef()) { + truetype = ((t_typedef*)truetype)->get_type(); + } + + bool first = true; + if (truetype->is_base_type()) { + t_base_type::t_base tbase = ((t_base_type*)truetype)->get_base(); + f_out_ << "```"; + switch (tbase) { + case t_base_type::TYPE_STRING: + f_out_ << escape_html(get_escaped_string(tvalue)); + break; + case t_base_type::TYPE_BOOL: + f_out_ << ((tvalue->get_integer() != 0) ? "true" : "false"); + break; + case t_base_type::TYPE_I8: + f_out_ << tvalue->get_integer(); + break; + case t_base_type::TYPE_I16: + f_out_ << tvalue->get_integer(); + break; + case t_base_type::TYPE_I32: + f_out_ << tvalue->get_integer(); + break; + case t_base_type::TYPE_I64: + f_out_ << tvalue->get_integer(); + break; + case t_base_type::TYPE_DOUBLE: + if (tvalue->get_type() == t_const_value::CV_INTEGER) { + f_out_ << tvalue->get_integer(); + } else { + f_out_ << tvalue->get_double(); + } + break; + default: + f_out_ << "UNKNOWN BASE TYPE"; + break; + } + f_out_ << "```"; + } else if (truetype->is_enum()) { + f_out_ << escape_html(truetype->get_name()) << "." + << escape_html(tvalue->get_identifier_name()); + } else if (truetype->is_struct() || truetype->is_xception()) { + f_out_ << "{ "; + const vector& fields = ((t_struct*)truetype)->get_members(); + vector::const_iterator f_iter; + const map& val = tvalue->get_map(); + map::const_iterator v_iter; + for (v_iter = val.begin(); v_iter != val.end(); ++v_iter) { + t_type* field_type = nullptr; + for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { + if ((*f_iter)->get_name() == v_iter->first->get_string()) { + field_type = (*f_iter)->get_type(); + } + } + if (field_type == nullptr) { + throw "type error: " + truetype->get_name() + " has no field " + + v_iter->first->get_string(); + } + if (!first) { + f_out_ << ", "; + } + first = false; + f_out_ << escape_html(v_iter->first->get_string()) << " = "; + print_const_value(field_type, v_iter->second); + } + f_out_ << " }"; + } else if (truetype->is_map()) { + f_out_ << "{ "; + map map_elems = tvalue->get_map(); + map::iterator map_iter; + for (map_iter = map_elems.begin(); map_iter != map_elems.end(); map_iter++) { + if (!first) { + f_out_ << ", "; + } + first = false; + print_const_value(((t_map*)truetype)->get_key_type(), map_iter->first); + f_out_ << " = "; + print_const_value(((t_map*)truetype)->get_val_type(), map_iter->second); + } + f_out_ << " }"; + } else if (truetype->is_list()) { + f_out_ << "{ "; + vector list_elems = tvalue->get_list(); + ; + vector::iterator list_iter; + for (list_iter = list_elems.begin(); list_iter != list_elems.end(); list_iter++) { + if (!first) { + f_out_ << ", "; + } + first = false; + print_const_value(((t_list*)truetype)->get_elem_type(), *list_iter); + } + f_out_ << " }"; + } else if (truetype->is_set()) { + f_out_ << "{ "; + vector list_elems = tvalue->get_list(); + ; + vector::iterator list_iter; + for (list_iter = list_elems.begin(); list_iter != list_elems.end(); list_iter++) { + if (!first) { + f_out_ << ", "; + } + first = false; + print_const_value(((t_set*)truetype)->get_elem_type(), *list_iter); + } + f_out_ << " }"; + } else { + f_out_ << "UNKNOWN TYPE"; + } +} + +/** + * Prints out documentation for arguments/exceptions of a function, if any documentation has been + * supplied. + */ +void t_markdown_generator::print_fn_args_doc(t_function* tfunction) { + bool has_docs = false; + vector args = tfunction->get_arglist()->get_members(); + vector::iterator arg_iter = args.begin(); + if (arg_iter != args.end()) { + for (; arg_iter != args.end(); arg_iter++) { + if ((*arg_iter)->has_doc() && !(*arg_iter)->get_doc().empty()) { + has_docs = true; + break; + } + } + if (has_docs) { + arg_iter = args.begin(); + f_out_ << endl << "* parameters:" << endl; + for (int n = 1; arg_iter != args.end(); ++arg_iter, ++n ) { + f_out_ << n << ". " << (*arg_iter)->get_name(); + f_out_ << " - " << escape_html((*arg_iter)->get_doc()); + f_out_ << endl; + } + f_out_ << endl; + } + } + if(!has_docs) + f_out_ << endl; + + has_docs = false; + vector excepts = tfunction->get_xceptions()->get_members(); + vector::iterator ex_iter = excepts.begin(); + if (ex_iter != excepts.end()) { + for (; ex_iter != excepts.end(); ++ex_iter) { + if ((*ex_iter)->has_doc() && !(*ex_iter)->get_doc().empty()) { + has_docs = true; + break; + } + } + if (has_docs) { + ex_iter = excepts.begin(); + f_out_ << "* exceptions:" << endl; + for (; ex_iter != excepts.end(); ex_iter++) { + f_out_ << " * " << (*ex_iter)->get_type()->get_name(); + f_out_ << " - "; + f_out_ << escape_html((*ex_iter)->get_doc()); + f_out_ << endl; + } + f_out_ << endl; + } + } +} + +/** + * Generates a typedef. + * + * @param ttypedef The type definition + */ +void t_markdown_generator::generate_typedef(t_typedef* ttypedef) { + string name = ttypedef->get_name(); + f_out_ << "### Typedef: " << name << endl; + print_doc(ttypedef); + f_out_ << endl << endl; + f_out_ << "_Base type_: **"; + print_type(ttypedef->get_type()); + f_out_ << "**" << endl << endl; + f_out_ << endl; +} + +/** + * Generates code for an enumerated type. + * + * @param tenum The enumeration + */ +void t_markdown_generator::generate_enum(t_enum* tenum) { + string name = tenum->get_name(); + f_out_ << "### Enumeration: " << name << endl; + print_doc(tenum); + f_out_ << endl << endl << "|Name|Value|Description|" << endl + << "|---|---|---|" << endl; + vector values = tenum->get_constants(); + vector::iterator val_iter; + for (val_iter = values.begin(); val_iter != values.end(); ++val_iter) { + f_out_ << "|```"; + f_out_ << (*val_iter)->get_name(); + f_out_ << "```|```"; + f_out_ << (*val_iter)->get_value(); + f_out_ << "```|"; + print_doc((*val_iter)); + f_out_ << "|" << endl; + } + f_out_ << endl; +} + +/** + * Generates a constant value + */ +void t_markdown_generator::generate_const(t_const* tconst) { + // |Constant|Type|Value|HAS_DOC| + string name = tconst->get_name(); + f_out_ << "| ```" << name << "``` | "; + print_type(tconst->get_type()); + f_out_ << "| ```"; + print_const_value(tconst->get_type(), tconst->get_value()); + f_out_ << "``` |"; + if (tconst->has_doc()) { + print_doc(tconst); + } + f_out_ << '|' << endl; +} + +/** + * Generates a struct definition for a thrift data type. + * + * @param tstruct The struct definition + */ +void t_markdown_generator::generate_struct(t_struct* tstruct) { + string name = tstruct->get_name(); + f_out_ << "### "; + if (tstruct->is_xception()) { + f_out_ << "Exception: "; + } else if (tstruct->is_union()) { + f_out_ << "Union: "; + } else { + f_out_ << "Struct: "; + } + f_out_ << name << endl; + print_doc(tstruct); + f_out_ << endl << endl; + vector members = tstruct->get_members(); + vector::iterator mem_iter = members.begin(); + f_out_ << "| Key | Field | Type | Description | Requiredness " + "| Default value |" << endl + << "| --- | --- | --- | --- | --- | --- |" << endl; + for (; mem_iter != members.end(); mem_iter++) { + f_out_ << '|' << (*mem_iter)->get_key(); + f_out_ << '|' << (*mem_iter)->get_name(); + f_out_ << '|'; + print_type((*mem_iter)->get_type()); + f_out_ << '|' << escape_html((*mem_iter)->get_doc()) << '|'; + if ((*mem_iter)->get_req() == t_field::T_OPTIONAL) { + f_out_ << "optional"; + } else if ((*mem_iter)->get_req() == t_field::T_REQUIRED) { + f_out_ << "required"; + } else { + f_out_ << "default"; + } + f_out_ << '|'; + t_const_value* default_val = (*mem_iter)->get_value(); + if (default_val != nullptr) { + f_out_ << "```"; + print_const_value((*mem_iter)->get_type(), default_val); + f_out_ << "```"; + } + f_out_ << '|' << endl; + } + f_out_ << endl; +} + +/** + * Exceptions are special structs + * + * @param tstruct The struct definition + */ +void t_markdown_generator::generate_xception(t_struct* txception) { + generate_struct(txception); +} + +/** + * Generates the Markdown block for a Thrift service. + * + * @param tservice The service definition + */ +void t_markdown_generator::generate_service(t_service* tservice) { + f_out_ << "### Service: " << service_name_ << endl; + + if (tservice->get_extends()) { + f_out_ << "**extends ** _"; + print_type(tservice->get_extends()); + f_out_ << "_" << endl; + } + + print_doc(tservice); + f_out_ << endl; + + vector functions = tservice->get_functions(); + vector::iterator fn_iter = functions.begin(); + for (; fn_iter != functions.end(); fn_iter++) { + string fn_name = (*fn_iter)->get_name(); + f_out_ << "#### Function: " << service_name_ << "." << fn_name << endl; + print_doc(*fn_iter); + f_out_ << endl << endl; + print_type((*fn_iter)->get_returntype()); + bool first = true; + f_out_ << endl << " _" << fn_name << "_("; + vector args = (*fn_iter)->get_arglist()->get_members(); + vector::iterator arg_iter = args.begin(); + for (; arg_iter != args.end(); arg_iter++) { + if (!first) { + f_out_ << "," << endl; + } + first = false; + print_type((*arg_iter)->get_type()); + f_out_ << " " << (*arg_iter)->get_name(); + if ((*arg_iter)->get_value() != nullptr) { + f_out_ << " = "; + print_const_value((*arg_iter)->get_type(), (*arg_iter)->get_value()); + } + } + f_out_ << ")" << endl; + first = true; + vector excepts = (*fn_iter)->get_xceptions()->get_members(); + vector::iterator ex_iter = excepts.begin(); + if (ex_iter != excepts.end()) { + f_out_ << "> throws "; + for (; ex_iter != excepts.end(); ex_iter++) { + if (!first) { + f_out_ << ", "; + } + first = false; + print_type((*ex_iter)->get_type()); + } + f_out_ << endl; + } + print_fn_args_doc(*fn_iter); + f_out_ << endl; + } +} + +THRIFT_REGISTER_GENERATOR( + markdown, + "Markdown", + " suffix: Create files/links with/out 'md|html' default None\n" + " noescape: Do not escape with html-entities in doc text.\n") diff --git a/compiler/cpp/src/thrift/generate/t_netcore_generator.h b/compiler/cpp/src/thrift/generate/t_netcore_generator.h deleted file mode 100644 index 6efc922b74f..00000000000 --- a/compiler/cpp/src/thrift/generate/t_netcore_generator.h +++ /dev/null @@ -1,137 +0,0 @@ -#include - -#include -#include -#include -#include -#include - -#include -#include -#include - -#include "thrift/platform.h" -#include "thrift/generate/t_oop_generator.h" - -using std::map; -using std::ostream; -using std::ostringstream; -using std::string; -using std::stringstream; -using std::vector; - -static const string endl = "\n"; // avoid ostream << std::endl flushes - -class t_netcore_generator : public t_oop_generator -{ - - struct member_mapping_scope - { - public: - member_mapping_scope() : scope_member(0) { } - void* scope_member; - map mapping_table; - }; - -public: - t_netcore_generator(t_program* program, const map& parsed_options, const string& option_string); - - bool is_wcf_enabled() const; - bool is_nullable_enabled() const; - bool is_hashcode_enabled() const; - bool is_serialize_enabled() const; - bool is_union_enabled() const; - map get_keywords_list() const; - - // overrides - void init_generator(); - void close_generator(); - void generate_consts(vector consts); - void generate_consts(ostream& out, vector consts); - void generate_typedef(t_typedef* ttypedef); - void generate_enum(t_enum* tenum); - void generate_enum(ostream& out, t_enum* tenum); - void generate_struct(t_struct* tstruct); - void generate_xception(t_struct* txception); - void generate_service(t_service* tservice); - - void generate_property(ostream& out, t_field* tfield, bool isPublic, bool generateIsset); - void generate_netcore_property(ostream& out, t_field* tfield, bool isPublic, bool includeIsset = true, string fieldPrefix = ""); - bool print_const_value(ostream& out, string name, t_type* type, t_const_value* value, bool in_static, bool defval = false, bool needtype = false); - string render_const_value(ostream& out, string name, t_type* type, t_const_value* value); - void print_const_constructor(ostream& out, vector consts); - void print_const_def_value(ostream& out, string name, t_type* type, t_const_value* value); - void generate_netcore_struct(t_struct* tstruct, bool is_exception); - void generate_netcore_union(t_struct* tunion); - void generate_netcore_struct_definition(ostream& out, t_struct* tstruct, bool is_xception = false, bool in_class = false, bool is_result = false); - void generate_netcore_union_definition(ostream& out, t_struct* tunion); - void generate_netcore_union_class(ostream& out, t_struct* tunion, t_field* tfield); - void generate_netcore_wcffault(ostream& out, t_struct* tstruct); - void generate_netcore_struct_reader(ostream& out, t_struct* tstruct); - void generate_netcore_struct_result_writer(ostream& out, t_struct* tstruct); - void generate_netcore_struct_writer(ostream& out, t_struct* tstruct); - void generate_netcore_struct_tostring(ostream& out, t_struct* tstruct); - void generate_netcore_struct_equals(ostream& out, t_struct* tstruct); - void generate_netcore_struct_hashcode(ostream& out, t_struct* tstruct); - void generate_netcore_union_reader(ostream& out, t_struct* tunion); - void generate_function_helpers(ostream& out, t_function* tfunction); - void generate_service_interface(ostream& out, t_service* tservice); - void generate_service_helpers(ostream& out, t_service* tservice); - void generate_service_client(ostream& out, t_service* tservice); - void generate_service_server(ostream& out, t_service* tservice); - void generate_process_function_async(ostream& out, t_service* tservice, t_function* function); - void generate_deserialize_field(ostream& out, t_field* tfield, string prefix = "", bool is_propertyless = false); - void generate_deserialize_struct(ostream& out, t_struct* tstruct, string prefix = ""); - void generate_deserialize_container(ostream& out, t_type* ttype, string prefix = ""); - void generate_deserialize_set_element(ostream& out, t_set* tset, string prefix = ""); - void generate_deserialize_map_element(ostream& out, t_map* tmap, string prefix = ""); - void generate_deserialize_list_element(ostream& out, t_list* list, string prefix = ""); - void generate_serialize_field(ostream& out, t_field* tfield, string prefix = "", bool is_element = false, bool is_propertyless = false); - void generate_serialize_struct(ostream& out, t_struct* tstruct, string prefix = ""); - void generate_serialize_container(ostream& out, t_type* ttype, string prefix = ""); - void generate_serialize_map_element(ostream& out, t_map* tmap, string iter, string map); - void generate_serialize_set_element(ostream& out, t_set* tmap, string iter); - void generate_serialize_list_element(ostream& out, t_list* tlist, string iter); - void generate_netcore_doc(ostream& out, t_field* field); - void generate_netcore_doc(ostream& out, t_doc* tdoc); - void generate_netcore_doc(ostream& out, t_function* tdoc); - void generate_netcore_docstring_comment(ostream& out, string contents); - void docstring_comment(ostream& out, const string& comment_start, const string& line_prefix, const string& contents, const string& comment_end); - void start_netcore_namespace(ostream& out); - void end_netcore_namespace(ostream& out); - - string netcore_type_usings() const; - string netcore_thrift_usings() const; - - string type_name(t_type* ttype, bool in_countainer = false, bool in_init = false, bool in_param = false, bool is_required = false); - string base_type_name(t_base_type* tbase, bool in_container = false, bool in_param = false, bool is_required = false); - string declare_field(t_field* tfield, bool init = false, string prefix = ""); - string function_signature_async(t_function* tfunction, string prefix = ""); - string function_signature(t_function* tfunction, string prefix = ""); - string argument_list(t_struct* tstruct); - string type_to_enum(t_type* ttype); - string prop_name(t_field* tfield, bool suppress_mapping = false); - string get_enum_class_name(t_type* type); - -private: - string namespace_name_; - string namespace_dir_; - - bool nullable_; - bool union_; - bool hashcode_; - bool serialize_; - bool wcf_; - - string wcf_namespace_; - map netcore_keywords; - vector member_mapping_scopes; - - void init_keywords(); - string normalize_name(string name); - string make_valid_csharp_identifier(string const& fromName); - void prepare_member_name_mapping(t_struct* tstruct); - void prepare_member_name_mapping(void* scope, const vector& members, const string& structname); - void cleanup_member_name_mapping(void* scope); - string get_mapped_member_name(string oldname); -}; diff --git a/compiler/cpp/src/thrift/generate/t_netcore_generator.cc b/compiler/cpp/src/thrift/generate/t_netstd_generator.cc similarity index 63% rename from compiler/cpp/src/thrift/generate/t_netcore_generator.cc rename to compiler/cpp/src/thrift/generate/t_netstd_generator.cc index d2e7da0cb9f..5048c0ef2b0 100644 --- a/compiler/cpp/src/thrift/generate/t_netcore_generator.cc +++ b/compiler/cpp/src/thrift/generate/t_netstd_generator.cc @@ -35,7 +35,7 @@ #include "thrift/platform.h" #include "thrift/generate/t_oop_generator.h" -#include "thrift/generate/t_netcore_generator.h" +#include "thrift/generate/t_netstd_generator.h" using std::map; using std::ostream; @@ -47,51 +47,45 @@ using std::vector; //TODO: check for indentation //TODO: Do we need seqId_ in generation? -t_netcore_generator::t_netcore_generator(t_program* program, const map& parsed_options, const string& option_string) +t_netstd_generator::t_netstd_generator(t_program* program, const map& parsed_options, const string& option_string) : t_oop_generator(program) { (void)option_string; - - nullable_ = false; - hashcode_ = false; + suppress_deepcopy = false; + use_pascal_case_properties = false; union_ = false; serialize_ = false; wcf_ = false; + wcf_namespace_.clear(); map::const_iterator iter; for (iter = parsed_options.begin(); iter != parsed_options.end(); ++iter) { - if (iter->first.compare("nullable") == 0) - { - nullable_ = true; - } - else if (iter->first.compare("hashcode") == 0) - { - hashcode_ = true; - } - else if (iter->first.compare("union") == 0) - { + if (iter->first.compare("union") == 0) { union_ = true; } - else if (iter->first.compare("serial") == 0) - { + else if (iter->first.compare("serial") == 0) { serialize_ = true; wcf_namespace_ = iter->second; // since there can be only one namespace } - else if (iter->first.compare("wcf") == 0) - { + else if (iter->first.compare("wcf") == 0) { wcf_ = true; wcf_namespace_ = iter->second; } - else - { - throw "unknown option netcore:" + iter->first; + else if (iter->first.compare("pascal") == 0) { + use_pascal_case_properties = true; + } + else if (iter->first.compare("no_deepcopy") == 0) { + suppress_deepcopy = true; + } + else { + throw "unknown option netstd:" + iter->first; } } - out_dir_base_ = "gen-netcore"; + out_dir_base_ = "gen-netstd"; } static string correct_function_name_for_async(string const& function_name) @@ -134,45 +128,43 @@ static string check_and_correct_struct_name(const string& struct_name) return struct_name; } -static bool field_has_default(t_field* tfield) { return tfield->get_value() != NULL; } +static bool field_has_default(t_field* tfield) { return tfield->get_value() != nullptr; } static bool field_is_required(t_field* tfield) { return tfield->get_req() == t_field::T_REQUIRED; } -static bool type_can_be_null(t_type* ttype) +static t_type* resolve_typedef(t_type* ttype) { while (ttype->is_typedef()) { ttype = static_cast(ttype)->get_type(); } - - return ttype->is_container() || ttype->is_struct() || ttype->is_xception() || ttype->is_string(); + return ttype; } -bool t_netcore_generator::is_wcf_enabled() const { return wcf_; } -bool t_netcore_generator::is_nullable_enabled() const { return nullable_; } +static bool type_can_be_null(t_type* ttype) +{ + ttype = resolve_typedef(ttype); + + return ttype->is_container() || ttype->is_struct() || ttype->is_xception() || ttype->is_string(); +} -bool t_netcore_generator::is_hashcode_enabled() const { return hashcode_; } +bool t_netstd_generator::is_wcf_enabled() const { return wcf_; } -bool t_netcore_generator::is_serialize_enabled() const { return serialize_; } +bool t_netstd_generator::is_serialize_enabled() const { return serialize_; } -bool t_netcore_generator::is_union_enabled() const { return union_; } +bool t_netstd_generator::is_union_enabled() const { return union_; } -map t_netcore_generator::get_keywords_list() const +map t_netstd_generator::get_keywords_list() const { - return netcore_keywords; + return netstd_keywords; } -void t_netcore_generator::init_generator() +void t_netstd_generator::init_generator() { MKDIR(get_out_dir().c_str()); - // for usage of csharp namespaces in thrift files (from files for csharp) - namespace_name_ = program_->get_namespace("netcore"); - if (namespace_name_.empty()) - { - namespace_name_ = program_->get_namespace("netcore"); - } + namespace_name_ = program_->get_namespace("netstd"); string dir = namespace_name_; string subdir = get_out_dir().c_str(); @@ -198,21 +190,21 @@ void t_netcore_generator::init_generator() cleanup_member_name_mapping(member_mapping_scopes.back().scope_member); } - pverbose(".NET Core options:\n"); - pverbose("- nullable ... %s\n", (nullable_ ? "ON" : "off")); - pverbose("- union ...... %s\n", (union_ ? "ON" : "off")); - pverbose("- hashcode ... %s\n", (hashcode_ ? "ON" : "off")); - pverbose("- serialize .. %s\n", (serialize_ ? "ON" : "off")); - pverbose("- wcf ........ %s\n", (wcf_ ? "ON" : "off")); + pverbose(".NET Standard options:\n"); + pverbose("- union ......... %s\n", (is_union_enabled() ? "ON" : "off")); + pverbose("- serialize ..... %s\n", (is_serialize_enabled() ? "ON" : "off")); + pverbose("- wcf ........... %s\n", (is_wcf_enabled() ? "ON" : "off")); + pverbose("- pascal ........ %s\n", (use_pascal_case_properties ? "ON" : "off")); + pverbose("- no_deepcopy ... %s\n", (suppress_deepcopy ? "ON" : "off")); } -string t_netcore_generator::normalize_name(string name) +string t_netstd_generator::normalize_name(string name) { string tmp(name); transform(tmp.begin(), tmp.end(), tmp.begin(), static_cast(tolower)); // un-conflict keywords by prefixing with "@" - if (netcore_keywords.find(tmp) != netcore_keywords.end()) + if (netstd_keywords.find(tmp) != netstd_keywords.end()) { return "@" + name; } @@ -221,119 +213,130 @@ string t_netcore_generator::normalize_name(string name) return name; } -void t_netcore_generator::init_keywords() +void t_netstd_generator::init_keywords() { - netcore_keywords.clear(); + netstd_keywords.clear(); // C# keywords - netcore_keywords["abstract"] = 1; - netcore_keywords["as"] = 1; - netcore_keywords["base"] = 1; - netcore_keywords["bool"] = 1; - netcore_keywords["break"] = 1; - netcore_keywords["byte"] = 1; - netcore_keywords["case"] = 1; - netcore_keywords["catch"] = 1; - netcore_keywords["char"] = 1; - netcore_keywords["checked"] = 1; - netcore_keywords["class"] = 1; - netcore_keywords["const"] = 1; - netcore_keywords["continue"] = 1; - netcore_keywords["decimal"] = 1; - netcore_keywords["default"] = 1; - netcore_keywords["delegate"] = 1; - netcore_keywords["do"] = 1; - netcore_keywords["double"] = 1; - netcore_keywords["else"] = 1; - netcore_keywords["enum"] = 1; - netcore_keywords["event"] = 1; - netcore_keywords["explicit"] = 1; - netcore_keywords["extern"] = 1; - netcore_keywords["false"] = 1; - netcore_keywords["finally"] = 1; - netcore_keywords["fixed"] = 1; - netcore_keywords["float"] = 1; - netcore_keywords["for"] = 1; - netcore_keywords["foreach"] = 1; - netcore_keywords["goto"] = 1; - netcore_keywords["if"] = 1; - netcore_keywords["implicit"] = 1; - netcore_keywords["in"] = 1; - netcore_keywords["int"] = 1; - netcore_keywords["interface"] = 1; - netcore_keywords["internal"] = 1; - netcore_keywords["is"] = 1; - netcore_keywords["lock"] = 1; - netcore_keywords["long"] = 1; - netcore_keywords["namespace"] = 1; - netcore_keywords["new"] = 1; - netcore_keywords["null"] = 1; - netcore_keywords["object"] = 1; - netcore_keywords["operator"] = 1; - netcore_keywords["out"] = 1; - netcore_keywords["override"] = 1; - netcore_keywords["params"] = 1; - netcore_keywords["private"] = 1; - netcore_keywords["protected"] = 1; - netcore_keywords["public"] = 1; - netcore_keywords["readonly"] = 1; - netcore_keywords["ref"] = 1; - netcore_keywords["return"] = 1; - netcore_keywords["sbyte"] = 1; - netcore_keywords["sealed"] = 1; - netcore_keywords["short"] = 1; - netcore_keywords["sizeof"] = 1; - netcore_keywords["stackalloc"] = 1; - netcore_keywords["static"] = 1; - netcore_keywords["string"] = 1; - netcore_keywords["struct"] = 1; - netcore_keywords["switch"] = 1; - netcore_keywords["this"] = 1; - netcore_keywords["throw"] = 1; - netcore_keywords["true"] = 1; - netcore_keywords["try"] = 1; - netcore_keywords["typeof"] = 1; - netcore_keywords["uint"] = 1; - netcore_keywords["ulong"] = 1; - netcore_keywords["unchecked"] = 1; - netcore_keywords["unsafe"] = 1; - netcore_keywords["ushort"] = 1; - netcore_keywords["using"] = 1; - netcore_keywords["virtual"] = 1; - netcore_keywords["void"] = 1; - netcore_keywords["volatile"] = 1; - netcore_keywords["while"] = 1; + netstd_keywords["abstract"] = 1; + netstd_keywords["as"] = 1; + netstd_keywords["base"] = 1; + netstd_keywords["bool"] = 1; + netstd_keywords["break"] = 1; + netstd_keywords["byte"] = 1; + netstd_keywords["case"] = 1; + netstd_keywords["catch"] = 1; + netstd_keywords["char"] = 1; + netstd_keywords["checked"] = 1; + netstd_keywords["class"] = 1; + netstd_keywords["const"] = 1; + netstd_keywords["continue"] = 1; + netstd_keywords["decimal"] = 1; + netstd_keywords["default"] = 1; + netstd_keywords["delegate"] = 1; + netstd_keywords["do"] = 1; + netstd_keywords["double"] = 1; + netstd_keywords["else"] = 1; + netstd_keywords["enum"] = 1; + netstd_keywords["event"] = 1; + netstd_keywords["explicit"] = 1; + netstd_keywords["extern"] = 1; + netstd_keywords["false"] = 1; + netstd_keywords["finally"] = 1; + netstd_keywords["fixed"] = 1; + netstd_keywords["float"] = 1; + netstd_keywords["for"] = 1; + netstd_keywords["foreach"] = 1; + netstd_keywords["goto"] = 1; + netstd_keywords["if"] = 1; + netstd_keywords["implicit"] = 1; + netstd_keywords["in"] = 1; + netstd_keywords["int"] = 1; + netstd_keywords["interface"] = 1; + netstd_keywords["internal"] = 1; + netstd_keywords["is"] = 1; + netstd_keywords["lock"] = 1; + netstd_keywords["long"] = 1; + netstd_keywords["namespace"] = 1; + netstd_keywords["new"] = 1; + netstd_keywords["null"] = 1; + netstd_keywords["object"] = 1; + netstd_keywords["operator"] = 1; + netstd_keywords["out"] = 1; + netstd_keywords["override"] = 1; + netstd_keywords["params"] = 1; + netstd_keywords["private"] = 1; + netstd_keywords["protected"] = 1; + netstd_keywords["public"] = 1; + netstd_keywords["readonly"] = 1; + netstd_keywords["ref"] = 1; + netstd_keywords["return"] = 1; + netstd_keywords["sbyte"] = 1; + netstd_keywords["sealed"] = 1; + netstd_keywords["short"] = 1; + netstd_keywords["sizeof"] = 1; + netstd_keywords["stackalloc"] = 1; + netstd_keywords["static"] = 1; + netstd_keywords["string"] = 1; + netstd_keywords["struct"] = 1; + netstd_keywords["switch"] = 1; + netstd_keywords["this"] = 1; + netstd_keywords["throw"] = 1; + netstd_keywords["true"] = 1; + netstd_keywords["try"] = 1; + netstd_keywords["typeof"] = 1; + netstd_keywords["uint"] = 1; + netstd_keywords["ulong"] = 1; + netstd_keywords["unchecked"] = 1; + netstd_keywords["unsafe"] = 1; + netstd_keywords["ushort"] = 1; + netstd_keywords["using"] = 1; + netstd_keywords["virtual"] = 1; + netstd_keywords["void"] = 1; + netstd_keywords["volatile"] = 1; + netstd_keywords["while"] = 1; // C# contextual keywords - netcore_keywords["add"] = 1; - netcore_keywords["alias"] = 1; - netcore_keywords["ascending"] = 1; - netcore_keywords["async"] = 1; - netcore_keywords["await"] = 1; - netcore_keywords["descending"] = 1; - netcore_keywords["dynamic"] = 1; - netcore_keywords["from"] = 1; - netcore_keywords["get"] = 1; - netcore_keywords["global"] = 1; - netcore_keywords["group"] = 1; - netcore_keywords["into"] = 1; - netcore_keywords["join"] = 1; - netcore_keywords["let"] = 1; - netcore_keywords["orderby"] = 1; - netcore_keywords["partial"] = 1; - netcore_keywords["remove"] = 1; - netcore_keywords["select"] = 1; - netcore_keywords["set"] = 1; - netcore_keywords["value"] = 1; - netcore_keywords["var"] = 1; - netcore_keywords["where"] = 1; - netcore_keywords["yield"] = 1; - - netcore_keywords["when"] = 1; -} - -void t_netcore_generator::start_netcore_namespace(ostream& out) -{ + netstd_keywords["add"] = 1; + netstd_keywords["alias"] = 1; + netstd_keywords["ascending"] = 1; + netstd_keywords["async"] = 1; + netstd_keywords["await"] = 1; + netstd_keywords["descending"] = 1; + netstd_keywords["dynamic"] = 1; + netstd_keywords["from"] = 1; + netstd_keywords["get"] = 1; + netstd_keywords["global"] = 1; + netstd_keywords["group"] = 1; + netstd_keywords["into"] = 1; + netstd_keywords["join"] = 1; + netstd_keywords["let"] = 1; + netstd_keywords["orderby"] = 1; + netstd_keywords["partial"] = 1; + netstd_keywords["remove"] = 1; + netstd_keywords["select"] = 1; + netstd_keywords["set"] = 1; + netstd_keywords["value"] = 1; + netstd_keywords["var"] = 1; + netstd_keywords["where"] = 1; + netstd_keywords["yield"] = 1; + + netstd_keywords["when"] = 1; +} + +void t_netstd_generator::reset_indent() { + while( indent_count() > 0) { + indent_down(); + } +} + + +void t_netstd_generator::start_netstd_namespace(ostream& out) +{ + out << "#pragma warning disable IDE0079 // remove unnecessary pragmas" << endl + << "#pragma warning disable IDE1006 // parts of the code use IDL spelling" << endl + << endl; + if (!namespace_name_.empty()) { out << "namespace " << namespace_name_ << endl; @@ -341,7 +344,7 @@ void t_netcore_generator::start_netcore_namespace(ostream& out) } } -void t_netcore_generator::end_netcore_namespace(ostream& out) +void t_netstd_generator::end_netstd_namespace(ostream& out) { if (!namespace_name_.empty()) { @@ -349,7 +352,7 @@ void t_netcore_generator::end_netcore_namespace(ostream& out) } } -string t_netcore_generator::netcore_type_usings() const +string t_netstd_generator::netstd_type_usings() const { string namespaces = "using System;\n" @@ -357,12 +360,14 @@ string t_netcore_generator::netcore_type_usings() const "using System.Collections.Generic;\n" "using System.Text;\n" "using System.IO;\n" + "using System.Linq;\n" "using System.Threading;\n" "using System.Threading.Tasks;\n" + "using Microsoft.Extensions.Logging;\n" "using Thrift;\n" "using Thrift.Collections;\n"; - if (wcf_) + if (is_wcf_enabled()) { namespaces += "using System.ServiceModel;\n"; namespaces += "using System.Runtime.Serialization;\n"; @@ -371,67 +376,71 @@ string t_netcore_generator::netcore_type_usings() const return namespaces + endl; } -string t_netcore_generator::netcore_thrift_usings() const +string t_netstd_generator::netstd_thrift_usings() const { string namespaces = - "using Thrift.Protocols;\n" - "using Thrift.Protocols.Entities;\n" - "using Thrift.Protocols.Utilities;\n" - "using Thrift.Transports;\n" - "using Thrift.Transports.Client;\n" - "using Thrift.Transports.Server;\n"; + "using Thrift.Protocol;\n" + "using Thrift.Protocol.Entities;\n" + "using Thrift.Protocol.Utilities;\n" + "using Thrift.Transport;\n" + "using Thrift.Transport.Client;\n" + "using Thrift.Transport.Server;\n" + "using Thrift.Processor;\n"; return namespaces + endl; } -void t_netcore_generator::close_generator() +void t_netstd_generator::close_generator() { + // right at the end, after everything else + generate_extensions_file(); } -void t_netcore_generator::generate_typedef(t_typedef* ttypedef) +void t_netstd_generator::generate_typedef(t_typedef* ttypedef) { (void)ttypedef; } -void t_netcore_generator::generate_enum(t_enum* tenum) +void t_netstd_generator::generate_enum(t_enum* tenum) { - int ic = indent_count(); + int ic = indent_count(); string f_enum_name = namespace_dir_ + "/" + tenum->get_name() + ".cs"; ofstream_with_content_based_conditional_update f_enum; f_enum.open(f_enum_name.c_str()); - generate_enum(f_enum, tenum); + generate_enum(f_enum, tenum); f_enum.close(); - indent_validate(ic, "generate_enum"); + indent_validate(ic, "generate_enum"); } -void t_netcore_generator::generate_enum(ostream& out, t_enum* tenum) +void t_netstd_generator::generate_enum(ostream& out, t_enum* tenum) { - out << autogen_comment() << endl; + reset_indent(); + out << autogen_comment() << endl; - start_netcore_namespace(out); - generate_netcore_doc(out, tenum); + start_netstd_namespace(out); + generate_netstd_doc(out, tenum); - out << indent() << "public enum " << tenum->get_name() << endl; - scope_up(out); + out << indent() << "public enum " << tenum->get_name() << endl; + scope_up(out); - vector constants = tenum->get_constants(); - vector::iterator c_iter; + vector constants = tenum->get_constants(); + vector::iterator c_iter; - for (c_iter = constants.begin(); c_iter != constants.end(); ++c_iter) - { - generate_netcore_doc(out, *c_iter); - int value = (*c_iter)->get_value(); - out << indent() << (*c_iter)->get_name() << " = " << value << "," << endl; - } + for (c_iter = constants.begin(); c_iter != constants.end(); ++c_iter) + { + generate_netstd_doc(out, *c_iter); + int value = (*c_iter)->get_value(); + out << indent() << (*c_iter)->get_name() << " = " << value << "," << endl; + } - scope_down(out); - end_netcore_namespace(out); + scope_down(out); + end_netstd_namespace(out); } -void t_netcore_generator::generate_consts(vector consts) +void t_netstd_generator::generate_consts(vector consts) { if (consts.empty()) { @@ -447,16 +456,17 @@ void t_netcore_generator::generate_consts(vector consts) f_consts.close(); } -void t_netcore_generator::generate_consts(ostream& out, vector consts) +void t_netstd_generator::generate_consts(ostream& out, vector consts) { if (consts.empty()) { return; } - out << autogen_comment() << netcore_type_usings() << endl; + reset_indent(); + out << autogen_comment() << netstd_type_usings() << endl; - start_netcore_namespace(out); + start_netstd_namespace(out); out << indent() << "public static class " << make_valid_csharp_identifier(program_name_) << "Constants" << endl; @@ -466,7 +476,7 @@ void t_netcore_generator::generate_consts(ostream& out, vector consts) bool need_static_constructor = false; for (c_iter = consts.begin(); c_iter != consts.end(); ++c_iter) { - generate_netcore_doc(out, *c_iter); + generate_netstd_doc(out, *c_iter); if (print_const_value(out, (*c_iter)->get_name(), (*c_iter)->get_type(), (*c_iter)->get_value(), false)) { need_static_constructor = true; @@ -479,10 +489,10 @@ void t_netcore_generator::generate_consts(ostream& out, vector consts) } scope_down(out); - end_netcore_namespace(out); + end_netstd_namespace(out); } -void t_netcore_generator::print_const_def_value(ostream& out, string name, t_type* type, t_const_value* value) +void t_netstd_generator::print_const_def_value(ostream& out, string name, t_type* type, t_const_value* value) { if (type->is_struct() || type->is_xception()) { @@ -490,11 +500,12 @@ void t_netcore_generator::print_const_def_value(ostream& out, string name, t_typ const map& val = value->get_map(); vector::const_iterator f_iter; map::const_iterator v_iter; + collect_extensions_types(static_cast(type)); prepare_member_name_mapping(static_cast(type)); for (v_iter = val.begin(); v_iter != val.end(); ++v_iter) { - t_field* field = NULL; + t_field* field = nullptr; for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { @@ -504,7 +515,7 @@ void t_netcore_generator::print_const_def_value(ostream& out, string name, t_typ } } - if (field == NULL) + if (field == nullptr) { throw "type error: " + type->get_name() + " has no field " + v_iter->first->get_string(); } @@ -552,7 +563,7 @@ void t_netcore_generator::print_const_def_value(ostream& out, string name, t_typ } } -void t_netcore_generator::print_const_constructor(ostream& out, vector consts) +void t_netstd_generator::print_const_constructor(ostream& out, vector consts) { out << indent() << "static " << make_valid_csharp_identifier(program_name_).c_str() << "Constants()" << endl; scope_up(out); @@ -569,14 +580,12 @@ void t_netcore_generator::print_const_constructor(ostream& out, vector scope_down(out); } -bool t_netcore_generator::print_const_value(ostream& out, string name, t_type* type, t_const_value* value, bool in_static, bool defval, bool needtype) +bool t_netstd_generator::print_const_value(ostream& out, string name, t_type* type, t_const_value* value, bool in_static, bool defval, bool needtype) { out << indent(); bool need_static_construction = !in_static; - while (type->is_typedef()) - { - type = static_cast(type)->get_type(); - } + + type = resolve_typedef( type); if (!defval || needtype) { @@ -591,7 +600,7 @@ bool t_netcore_generator::print_const_value(ostream& out, string name, t_type* t } else if (type->is_enum()) { - out << name << " = " << type_name(type, false, true) << "." << value->get_identifier_name() << ";" << endl; + out << name << " = " << type_name(type) << "." << value->get_identifier_name() << ";" << endl; need_static_construction = false; } else if (type->is_struct() || type->is_xception()) @@ -600,7 +609,7 @@ bool t_netcore_generator::print_const_value(ostream& out, string name, t_type* t } else if (type->is_map()) { - out << name << " = new " << type_name(type, true, true) << "();" << endl; + out << name << " = new " << type_name(type) << "();" << endl; } else if (type->is_list() || type->is_set()) { @@ -615,7 +624,7 @@ bool t_netcore_generator::print_const_value(ostream& out, string name, t_type* t return need_static_construction; } -string t_netcore_generator::render_const_value(ostream& out, string name, t_type* type, t_const_value* value) +string t_netstd_generator::render_const_value(ostream& out, string name, t_type* type, t_const_value* value) { (void)name; ostringstream render; @@ -626,7 +635,11 @@ string t_netcore_generator::render_const_value(ostream& out, string name, t_type switch (tbase) { case t_base_type::TYPE_STRING: - render << '"' << get_escaped_string(value) << '"'; + if (type->is_binary()) { + render << "System.Text.Encoding.UTF8.GetBytes(\"" << get_escaped_string(value) << "\")"; + } else { + render << '"' << get_escaped_string(value) << '"'; + } break; case t_base_type::TYPE_BOOL: render << ((value->get_integer() > 0) ? "true" : "false"); @@ -665,24 +678,210 @@ string t_netcore_generator::render_const_value(ostream& out, string name, t_type return render.str(); } -void t_netcore_generator::generate_struct(t_struct* tstruct) +void t_netstd_generator::collect_extensions_types(t_struct* tstruct) { - if (union_ && tstruct->is_union()) + const vector& members = tstruct->get_members(); + vector::const_iterator m_iter; + + // make private members with public Properties + for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) { - generate_netcore_union(tstruct); + collect_extensions_types((*m_iter)->get_type()); + } +} + +void t_netstd_generator::collect_extensions_types(t_type* ttype) +{ + ttype = resolve_typedef( ttype); + string key = type_name(ttype); + + if (ttype->is_struct() || ttype->is_xception()) + { + if( checked_extension_types.find(key) == checked_extension_types.end()) + { + checked_extension_types[key] = ttype; // prevent recursion + + t_struct* tstruct = static_cast(ttype); + collect_extensions_types(tstruct); + } + return; + } + + if (ttype->is_map() || ttype->is_set() || ttype->is_list()) + { + if( collected_extension_types.find(key) == collected_extension_types.end()) + { + collected_extension_types[key] = ttype; // prevent recursion + + if( ttype->is_map()) + { + t_map* tmap = static_cast(ttype); + collect_extensions_types(tmap->get_key_type()); + collect_extensions_types(tmap->get_val_type()); + } + else if (ttype->is_set()) + { + t_set* tset = static_cast(ttype); + collect_extensions_types(tset->get_elem_type()); + } + else if (ttype->is_list()) + { + t_list* tlist = static_cast(ttype); + collect_extensions_types(tlist->get_elem_type()); + } + } + + return; + } +} + +void t_netstd_generator::generate_extensions_file() +{ + if (collected_extension_types.empty()) + { + return; + } + + string f_exts_name = namespace_dir_ + '/' + program_name_ + ".Extensions.cs"; + ofstream_with_content_based_conditional_update f_exts; + f_exts.open(f_exts_name.c_str()); + + generate_extensions(f_exts, collected_extension_types); + + f_exts.close(); +} + +void t_netstd_generator::generate_extensions(ostream& out, map types) +{ + if (types.empty()) + { + return; + } + + reset_indent(); + out << autogen_comment() << netstd_type_usings() << endl; + + start_netstd_namespace(out); + + out << indent() << "public static class " << make_valid_csharp_identifier(program_name_) << "Extensions" << endl; + scope_up(out); + + bool needs_typecast = false; + std::map::const_iterator iter; + for( iter = types.begin(); iter != types.end(); ++iter) + { + out << indent() << "public static bool Equals(this " << iter->first << " instance, object that)" << endl; + scope_up(out); + out << indent() << "if (!(that is " << iter->first << " other)) return false;" << endl; + out << indent() << "if (ReferenceEquals(instance, other)) return true;" << endl; + out << endl; + out << indent() << "return TCollections.Equals(instance, other);" << endl; + scope_down(out); + out << endl << endl; + + out << indent() << "public static int GetHashCode(this " << iter->first << " instance)" << endl; + scope_up(out); + out << indent() << "return TCollections.GetHashCode(instance);" << endl; + scope_down(out); + out << endl << endl; + + if(! suppress_deepcopy) { + out << indent() << "public static " << iter->first << " " << DEEP_COPY_METHOD_NAME << "(this " << iter->first << " source)" << endl; + scope_up(out); + out << indent() << "if (source == null)" << endl; + indent_up(); + out << indent() << "return null;" << endl << endl; + indent_down(); + + string tmp_instance = tmp("tmp"); + out << indent() << "var " << tmp_instance << " = new " << iter->first << "(source.Count);" << endl; + if( iter->second->is_map()) + { + t_map* tmap = static_cast(iter->second); + string copy_key = get_deep_copy_method_call(tmap->get_key_type(), needs_typecast); + string copy_val = get_deep_copy_method_call(tmap->get_val_type(), needs_typecast); + bool null_key = type_can_be_null(tmap->get_key_type()); + bool null_val = type_can_be_null(tmap->get_val_type()); + + out << indent() << "foreach (var pair in source)" << endl; + indent_up(); + out << indent() << tmp_instance << ".Add("; + if( null_key) + { + out << "(pair.Key != null) ? pair.Key" << copy_key << " : null"; + } else { + out << "pair.Key" << copy_key; + } + out << ", "; + if( null_val) + { + out << "(pair.Value != null) ? pair.Value" << copy_val << " : null"; + } else { + out << "pair.Value" << copy_val; + } + out << ");" << endl; + indent_down(); + + } else if( iter->second->is_set() || iter->second->is_list()) { + string copy_elm; + bool null_elm = false; + if (iter->second->is_set()) + { + t_set* tset = static_cast(iter->second); + copy_elm = get_deep_copy_method_call(tset->get_elem_type(), needs_typecast); + null_elm = type_can_be_null(tset->get_elem_type()); + } + else // list + { + t_list* tlist = static_cast(iter->second); + copy_elm = get_deep_copy_method_call(tlist->get_elem_type(), needs_typecast); + null_elm = type_can_be_null(tlist->get_elem_type()); + } + + out << indent() << "foreach (var elem in source)" << endl; + indent_up(); + out << indent() << tmp_instance << ".Add("; + if( null_elm) + { + out << "(elem != null) ? elem" << copy_elm << " : null"; + } else { + out << "elem" << copy_elm; + } + out << ");" << endl; + indent_down(); + } + + out << indent() << "return " << tmp_instance << ";" << endl; + scope_down(out); + out << endl << endl; + } + } + + + scope_down(out); + end_netstd_namespace(out); +} + +void t_netstd_generator::generate_struct(t_struct* tstruct) +{ + collect_extensions_types(tstruct); + + if (is_union_enabled() && tstruct->is_union()) + { + generate_netstd_union(tstruct); } else { - generate_netcore_struct(tstruct, false); + generate_netstd_struct(tstruct, false); } } -void t_netcore_generator::generate_xception(t_struct* txception) +void t_netstd_generator::generate_xception(t_struct* txception) { - generate_netcore_struct(txception, true); + generate_netstd_struct(txception, true); } -void t_netcore_generator::generate_netcore_struct(t_struct* tstruct, bool is_exception) +void t_netstd_generator::generate_netstd_struct(t_struct* tstruct, bool is_exception) { int ic = indent_count(); @@ -691,28 +890,30 @@ void t_netcore_generator::generate_netcore_struct(t_struct* tstruct, bool is_exc f_struct.open(f_struct_name.c_str()); - f_struct << autogen_comment() << netcore_type_usings() << netcore_thrift_usings() << endl; + reset_indent(); + f_struct << autogen_comment() << netstd_type_usings() << netstd_thrift_usings() << endl; - generate_netcore_struct_definition(f_struct, tstruct, is_exception); + generate_netstd_struct_definition(f_struct, tstruct, is_exception); f_struct.close(); - indent_validate(ic, "generate_netcore_struct"); + indent_validate(ic, "generate_netstd_struct"); } -void t_netcore_generator::generate_netcore_struct_definition(ostream& out, t_struct* tstruct, bool is_exception, bool in_class, bool is_result) +void t_netstd_generator::generate_netstd_struct_definition(ostream& out, t_struct* tstruct, bool is_exception, bool in_class, bool is_result) { if (!in_class) { - start_netcore_namespace(out); + start_netstd_namespace(out); } out << endl; - generate_netcore_doc(out, tstruct); + generate_netstd_doc(out, tstruct); + collect_extensions_types(tstruct); prepare_member_name_mapping(tstruct); - if ((serialize_ || wcf_) && !is_exception) + if ((is_serialize_enabled() || is_wcf_enabled()) && !is_exception) { out << indent() << "[DataContract(Namespace=\"" << wcf_namespace_ << "\")]" << endl; } @@ -738,8 +939,8 @@ void t_netcore_generator::generate_netcore_struct_definition(ostream& out, t_str // make private members with public Properties for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) { - // if the field is requied, then we use auto-properties - if (!field_is_required((*m_iter)) && (!nullable_ || field_has_default((*m_iter)))) + // if the field is required, then we use auto-properties + if (!field_is_required((*m_iter))) { out << indent() << "private " << declare_field(*m_iter, false, "_") << endl; } @@ -747,38 +948,32 @@ void t_netcore_generator::generate_netcore_struct_definition(ostream& out, t_str out << endl; bool has_non_required_fields = false; - bool has_non_required_default_value_fields = false; bool has_required_fields = false; for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) { - generate_netcore_doc(out, *m_iter); + generate_netstd_doc(out, *m_iter); generate_property(out, *m_iter, true, true); bool is_required = field_is_required((*m_iter)); - bool has_default = field_has_default((*m_iter)); if (is_required) { has_required_fields = true; - } + } else { - if (has_default) - { - has_non_required_default_value_fields = true; - } has_non_required_fields = true; } } - bool generate_isset = (nullable_ && has_non_required_default_value_fields) || (!nullable_ && has_non_required_fields); + bool generate_isset = has_non_required_fields; if (generate_isset) { out << endl; - if (serialize_ || wcf_) + if (is_serialize_enabled() || is_wcf_enabled()) { out << indent() << "[DataMember(Order = 1)]" << endl; } out << indent() << "public Isset __isset;" << endl; - if (serialize_ || wcf_) + if (is_serialize_enabled() || is_wcf_enabled()) { out << indent() << "[DataContract]" << endl; } @@ -790,40 +985,36 @@ void t_netcore_generator::generate_netcore_struct_definition(ostream& out, t_str for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) { bool is_required = field_is_required((*m_iter)); - bool has_default = field_has_default((*m_iter)); // if it is required, don't need Isset for that variable // if it is not required, if it has a default value, we need to generate Isset - // if we are not nullable, then we generate Isset - if (!is_required && (!nullable_ || has_default)) + if (!is_required) { - if (serialize_ || wcf_) + if (is_serialize_enabled() || is_wcf_enabled()) { out << indent() << "[DataMember]" << endl; } - out << indent() << "public bool " << normalize_name((*m_iter)->get_name()) << ";" << endl; + out << indent() << "public bool " << get_isset_name(normalize_name((*m_iter)->get_name())) << ";" << endl; } } indent_down(); out << indent() << "}" << endl << endl; - if (generate_isset && (serialize_ || wcf_)) + if (generate_isset && (is_serialize_enabled() || is_wcf_enabled())) { out << indent() << "#region XmlSerializer support" << endl << endl; for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) { bool is_required = field_is_required(*m_iter); - bool has_default = field_has_default(*m_iter); // if it is required, don't need Isset for that variable // if it is not required, if it has a default value, we need to generate Isset - // if we are not nullable, then we generate Isset - if (!is_required && (!nullable_ || has_default)) + if (!is_required) { out << indent() << "public bool ShouldSerialize" << prop_name(*m_iter) << "()" << endl << indent() << "{" << endl; indent_up(); - out << indent() << "return __isset." << normalize_name((*m_iter)->get_name()) << ";" << endl; + out << indent() << "return __isset." << get_isset_name(normalize_name((*m_iter)->get_name())) << ";" << endl; indent_down(); out << indent() << "}" << endl << endl; } @@ -841,11 +1032,9 @@ void t_netcore_generator::generate_netcore_struct_definition(ostream& out, t_str for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) { t_type* t = (*m_iter)->get_type(); - while (t->is_typedef()) - { - t = static_cast(t)->get_type(); - } - if ((*m_iter)->get_value() != NULL) + t = resolve_typedef(t); + + if ((*m_iter)->get_value() != nullptr) { if (field_is_required((*m_iter))) { @@ -855,13 +1044,14 @@ void t_netcore_generator::generate_netcore_struct_definition(ostream& out, t_str { print_const_value(out, "this._" + (*m_iter)->get_name(), t, (*m_iter)->get_value(), true, true); // Optionals with defaults are marked set - out << indent() << "this.__isset." << normalize_name((*m_iter)->get_name()) << " = true;" << endl; + out << indent() << "this.__isset." << get_isset_name(normalize_name((*m_iter)->get_name())) << " = true;" << endl; } } } indent_down(); out << indent() << "}" << endl << endl; + // if we have required fields, we add that CTOR too if (has_required_fields) { out << indent() << "public " << sharp_struct_name << "("; @@ -878,7 +1068,7 @@ void t_netcore_generator::generate_netcore_struct_definition(ostream& out, t_str { out << ", "; } - out << type_name((*m_iter)->get_type()) << " " << (*m_iter)->get_name(); + out << type_name((*m_iter)->get_type()) << " " << normalize_name((*m_iter)->get_name()); } } out << ") : this()" << endl @@ -889,7 +1079,7 @@ void t_netcore_generator::generate_netcore_struct_definition(ostream& out, t_str { if (field_is_required(*m_iter)) { - out << indent() << "this." << prop_name(*m_iter) << " = " << (*m_iter)->get_name() << ";" << endl; + out << indent() << "this." << prop_name(*m_iter) << " = " << normalize_name((*m_iter)->get_name()) << ";" << endl; } } @@ -897,39 +1087,40 @@ void t_netcore_generator::generate_netcore_struct_definition(ostream& out, t_str out << indent() << "}" << endl << endl; } - generate_netcore_struct_reader(out, tstruct); + // DeepCopy() + generate_netstd_deepcopy_method(out, tstruct, sharp_struct_name); + + generate_netstd_struct_reader(out, tstruct); if (is_result) { - generate_netcore_struct_result_writer(out, tstruct); + generate_netstd_struct_result_writer(out, tstruct); } else { - generate_netcore_struct_writer(out, tstruct); + generate_netstd_struct_writer(out, tstruct); } - if (hashcode_) - { - generate_netcore_struct_equals(out, tstruct); - generate_netcore_struct_hashcode(out, tstruct); - } - generate_netcore_struct_tostring(out, tstruct); + generate_netstd_struct_equals(out, tstruct); + generate_netstd_struct_hashcode(out, tstruct); + generate_netstd_struct_tostring(out, tstruct); indent_down(); out << indent() << "}" << endl << endl; // generate a corresponding WCF fault to wrap the exception - if ((serialize_ || wcf_) && is_exception) + if ((is_serialize_enabled() || is_wcf_enabled()) && is_exception) { - generate_netcore_wcffault(out, tstruct); + generate_netstd_wcffault(out, tstruct); } cleanup_member_name_mapping(tstruct); if (!in_class) { - end_netcore_namespace(out); + end_netstd_namespace(out); } } -void t_netcore_generator::generate_netcore_wcffault(ostream& out, t_struct* tstruct) + +void t_netstd_generator::generate_netstd_wcffault(ostream& out, t_struct* tstruct) { out << endl; out << indent() << "[DataContract]" << endl; @@ -946,7 +1137,11 @@ void t_netcore_generator::generate_netcore_wcffault(ostream& out, t_struct* tstr // make private members with public Properties for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) { - out << indent() << "private " << declare_field(*m_iter, false, "_") << endl; + // if the field is required, then we use auto-properties + if (!field_is_required((*m_iter))) + { + out << indent() << "private " << declare_field(*m_iter, false, "_") << endl; + } } out << endl; @@ -959,9 +1154,53 @@ void t_netcore_generator::generate_netcore_wcffault(ostream& out, t_struct* tstr out << indent() << "}" << endl << endl; } -void t_netcore_generator::generate_netcore_struct_reader(ostream& out, t_struct* tstruct) +void t_netstd_generator::generate_netstd_deepcopy_method(ostream& out, t_struct* tstruct, std::string sharp_struct_name) +{ + if( suppress_deepcopy) { + return; // feature disabled + } + + const vector& members = tstruct->get_members(); + vector::const_iterator m_iter; + + out << indent() << "public " << sharp_struct_name << " " << DEEP_COPY_METHOD_NAME << "()" << endl; + out << indent() << "{" << endl; + indent_up(); + + // return directly if there are only required fields + string tmp_instance = tmp("tmp"); + out << indent() << "var " << tmp_instance << " = new " << sharp_struct_name << "();" << endl; + + for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) { + bool needs_typecast = false; + t_type* ttype = (*m_iter)->get_type(); + string copy_op = get_deep_copy_method_call(ttype, needs_typecast); + + bool is_required = field_is_required(*m_iter); + generate_null_check_begin( out, *m_iter); + + out << indent() << tmp_instance << "." << prop_name(*m_iter) << " = "; + if( needs_typecast) { + out << "(" << type_name(ttype) << ")"; + } + out << "this." << prop_name(*m_iter) << copy_op << ";" << endl; + + generate_null_check_end( out, *m_iter); + if( !is_required) { + out << indent() << tmp_instance << ".__isset." << get_isset_name(normalize_name((*m_iter)->get_name())) + << " = this.__isset." << get_isset_name(normalize_name((*m_iter)->get_name())) << ";" << endl; + } + } + + out << indent() << "return " << tmp_instance << ";" << endl; + + indent_down(); + out << indent() << "}" << endl << endl; +} + +void t_netstd_generator::generate_netstd_struct_reader(ostream& out, t_struct* tstruct) { - out << indent() << "public async Task ReadAsync(TProtocol iprot, CancellationToken cancellationToken)" << endl + out << indent() << "public async global::System.Threading.Tasks.Task ReadAsync(TProtocol iprot, CancellationToken cancellationToken)" << endl << indent() << "{" << endl; indent_up(); out << indent() << "iprot.IncrementRecursionDepth();" << endl @@ -1063,9 +1302,47 @@ void t_netcore_generator::generate_netcore_struct_reader(ostream& out, t_struct* out << indent() << "}" << endl << endl; } -void t_netcore_generator::generate_netcore_struct_writer(ostream& out, t_struct* tstruct) + +void t_netstd_generator::generate_null_check_begin(ostream& out, t_field* tfield) { + bool is_required = field_is_required(tfield); + bool null_allowed = type_can_be_null(tfield->get_type()); + + if( null_allowed || (!is_required)) { + bool first = true; + out << indent() << "if("; + + if( null_allowed) { + out << "(" << prop_name(tfield) << " != null)"; + first = false; + } + + if( !is_required) { + if( !first) { + out << " && "; + } + out << "__isset." << get_isset_name(normalize_name(tfield->get_name())); + } + + out << ")" << endl + << indent() << "{" << endl; + indent_up(); + } +} + + +void t_netstd_generator::generate_null_check_end(ostream& out, t_field* tfield) { + bool is_required = field_is_required(tfield); + bool null_allowed = type_can_be_null(tfield->get_type()); + + if( null_allowed || (!is_required)) { + indent_down(); + out << indent() << "}" << endl; + } +} + +void t_netstd_generator::generate_netstd_struct_writer(ostream& out, t_struct* tstruct) { - out << indent() << "public async Task WriteAsync(TProtocol oprot, CancellationToken cancellationToken)" << endl + out << indent() << "public async global::System.Threading.Tasks.Task WriteAsync(TProtocol oprot, CancellationToken cancellationToken)" << endl << indent() << "{" << endl; indent_up(); @@ -1086,30 +1363,7 @@ void t_netcore_generator::generate_netcore_struct_writer(ostream& out, t_struct* out << indent() << "var field = new TField();" << endl; for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { - bool is_required = field_is_required(*f_iter); - bool has_default = field_has_default(*f_iter); - if (nullable_ && !has_default && !is_required) - { - out << indent() << "if (" << prop_name(*f_iter) << " != null)" << endl - << indent() << "{" << endl; - indent_up(); - } - else if (!is_required) - { - bool null_allowed = type_can_be_null((*f_iter)->get_type()); - if (null_allowed) - { - out << indent() << "if (" << prop_name(*f_iter) << " != null && __isset." << normalize_name((*f_iter)->get_name()) << ")" << endl - << indent() << "{" << endl; - indent_up(); - } - else - { - out << indent() << "if (__isset." << normalize_name((*f_iter)->get_name()) << ")" << endl - << indent() << "{" << endl; - indent_up(); - } - } + generate_null_check_begin( out, *f_iter); out << indent() << "field.Name = \"" << (*f_iter)->get_name() << "\";" << endl << indent() << "field.Type = " << type_to_enum((*f_iter)->get_type()) << ";" << endl << indent() << "field.ID = " << (*f_iter)->get_key() << ";" << endl @@ -1118,11 +1372,7 @@ void t_netcore_generator::generate_netcore_struct_writer(ostream& out, t_struct* generate_serialize_field(out, *f_iter); out << indent() << "await oprot.WriteFieldEndAsync(cancellationToken);" << endl; - if (!is_required) - { - indent_down(); - out << indent() << "}" << endl; - } + generate_null_check_end(out, *f_iter); } } @@ -1140,9 +1390,9 @@ void t_netcore_generator::generate_netcore_struct_writer(ostream& out, t_struct* out << indent() << "}" << endl << endl; } -void t_netcore_generator::generate_netcore_struct_result_writer(ostream& out, t_struct* tstruct) +void t_netstd_generator::generate_netstd_struct_result_writer(ostream& out, t_struct* tstruct) { - out << indent() << "public async Task WriteAsync(TProtocol oprot, CancellationToken cancellationToken)" << endl + out << indent() << "public async global::System.Threading.Tasks.Task WriteAsync(TProtocol oprot, CancellationToken cancellationToken)" << endl << indent() << "{" << endl; indent_up(); @@ -1174,19 +1424,11 @@ void t_netcore_generator::generate_netcore_struct_result_writer(ostream& out, t_ out << indent() << "else if"; } - if (nullable_) - { - out << "(this." << prop_name((*f_iter)) << " != null)" << endl - << indent() << "{" << endl; - } - else - { - out << "(this.__isset." << normalize_name((*f_iter)->get_name()) << ")" << endl - << indent() << "{" << endl; - } + out << "(this.__isset." << get_isset_name(normalize_name((*f_iter)->get_name())) << ")" << endl + << indent() << "{" << endl; indent_up(); - bool null_allowed = !nullable_ && type_can_be_null((*f_iter)->get_type()); + bool null_allowed = type_can_be_null((*f_iter)->get_type()); if (null_allowed) { out << indent() << "if (" << prop_name(*f_iter) << " != null)" << endl @@ -1228,7 +1470,7 @@ void t_netcore_generator::generate_netcore_struct_result_writer(ostream& out, t_ out << indent() << "}" << endl << endl; } -void t_netcore_generator::generate_netcore_struct_tostring(ostream& out, t_struct* tstruct) +void t_netstd_generator::generate_netstd_struct_tostring(ostream& out, t_struct* tstruct) { out << indent() << "public override string ToString()" << endl << indent() << "{" << endl; @@ -1239,11 +1481,12 @@ void t_netcore_generator::generate_netcore_struct_tostring(ostream& out, t_struc vector::const_iterator f_iter; bool useFirstFlag = false; + string tmp_count = tmp("tmp"); for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { if (!field_is_required((*f_iter))) { - out << indent() << "bool __first = true;" << endl; + out << indent() << "int " << tmp_count.c_str() << " = 0;" << endl; useFirstFlag = true; } break; @@ -1253,38 +1496,12 @@ void t_netcore_generator::generate_netcore_struct_tostring(ostream& out, t_struc for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { - bool is_required = field_is_required((*f_iter)); - bool has_default = field_has_default((*f_iter)); - if (nullable_ && !has_default && !is_required) - { - out << indent() << "if (" << prop_name((*f_iter)) << " != null)" << endl - << indent() << "{" << endl; - indent_up(); - } - else if (!is_required) - { - bool null_allowed = type_can_be_null((*f_iter)->get_type()); - if (null_allowed) - { - out << indent() << "if (" << prop_name((*f_iter)) << " != null && __isset." << normalize_name((*f_iter)->get_name()) << ")" << endl - << indent() << "{" << endl; - indent_up(); - } - else - { - out << indent() << "if (__isset." << normalize_name((*f_iter)->get_name()) << ")" << endl - << indent() << "{" << endl; - indent_up(); - } - } + bool is_required = field_is_required(*f_iter); + generate_null_check_begin(out, *f_iter); if (useFirstFlag && (!had_required)) { - out << indent() << "if(!__first) { sb.Append(\", \"); }" << endl; - if (!is_required) - { - out << indent() << "__first = false;" << endl; - } + out << indent() << "if(0 < " << tmp_count.c_str() << (is_required ? "" : "++") << ") { sb.Append(\", \"); }" << endl; out << indent() << "sb.Append(\"" << prop_name(*f_iter) << ": \");" << endl; } else @@ -1292,34 +1509,21 @@ void t_netcore_generator::generate_netcore_struct_tostring(ostream& out, t_struc out << indent() << "sb.Append(\", " << prop_name(*f_iter) << ": \");" << endl; } - t_type* ttype = (*f_iter)->get_type(); - if (ttype->is_xception() || ttype->is_struct()) - { - out << indent() << "sb.Append(" << prop_name(*f_iter) << "== null ? \"\" : " << prop_name(*f_iter) << ".ToString());" << endl; - } - else - { - out << indent() << "sb.Append(" << prop_name(*f_iter) << ");" << endl; - } + out << indent() << prop_name(*f_iter) << ".ToString(sb);" << endl; - if (!is_required) - { - indent_down(); - out << indent() << "}" << endl; - } - else - { - had_required = true; // now __first must be false, so we don't need to check it anymore + generate_null_check_end(out, *f_iter); + if (is_required) { + had_required = true; // now __count must be > 0, so we don't need to check it anymore } } - out << indent() << "sb.Append(\")\");" << endl + out << indent() << "sb.Append(')');" << endl << indent() << "return sb.ToString();" << endl; indent_down(); out << indent() << "}" << endl; } -void t_netcore_generator::generate_netcore_union(t_struct* tunion) +void t_netstd_generator::generate_netstd_union(t_struct* tunion) { int ic = indent_count(); @@ -1328,42 +1532,136 @@ void t_netcore_generator::generate_netcore_union(t_struct* tunion) f_union.open(f_union_name.c_str()); - f_union << autogen_comment() << netcore_type_usings() << netcore_thrift_usings() << endl; + reset_indent(); + f_union << autogen_comment() << netstd_type_usings() << netstd_thrift_usings() << endl; - generate_netcore_union_definition(f_union, tunion); + generate_netstd_union_definition(f_union, tunion); f_union.close(); - indent_validate(ic, "generate_netcore_union."); + indent_validate(ic, "generate_netstd_union."); } -void t_netcore_generator::generate_netcore_union_definition(ostream& out, t_struct* tunion) +void t_netstd_generator::generate_netstd_union_definition(ostream& out, t_struct* tunion) { // Let's define the class first - start_netcore_namespace(out); + start_netstd_namespace(out); - out << indent() << "public abstract partial class " << tunion->get_name() << " : TAbstractBase" << endl - << indent() << "{" << endl; + out << indent() << "public abstract partial class " << tunion->get_name() << " : TUnionBase" << endl; + out << indent() << "{" << endl; indent_up(); - out << indent() << "public abstract Task WriteAsync(TProtocol tProtocol, CancellationToken cancellationToken);" << endl - << indent() << "public readonly bool Isset;" << endl + out << indent() << "public abstract global::System.Threading.Tasks.Task WriteAsync(TProtocol tProtocol, CancellationToken cancellationToken);" << endl + << indent() << "public readonly int Isset;" << endl << indent() << "public abstract object Data { get; }" << endl - << indent() << "protected " << tunion->get_name() << "(bool isset)" << endl + << indent() << "protected " << tunion->get_name() << "(int isset)" << endl << indent() << "{" << endl; indent_up(); out << indent() << "Isset = isset;" << endl; indent_down(); out << indent() << "}" << endl << endl; - out << indent() << "public class ___undefined : " << tunion->get_name() << endl - << indent() << "{" << endl; + const vector& fields = tunion->get_members(); + vector::const_iterator f_iter; + + out << indent() << "public override bool Equals(object that)" << endl; + scope_up(out); + out << indent() << "if (!(that is " << tunion->get_name() << " other)) return false;" << endl; + out << indent() << "if (ReferenceEquals(this, other)) return true;" << endl; + out << endl; + out << indent() << "if(this.Isset != other.Isset) return false;" << endl; + out << endl; + out << indent() << "switch (Isset)" << endl; + scope_up(out); + for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) + { + bool needs_typecast = false; + string copy_op = get_deep_copy_method_call((*f_iter)->get_type(), needs_typecast); + out << indent() << "case " << (*f_iter)->get_key() << ":" << endl; + indent_up(); + out << indent() << "return Equals(As_" << (*f_iter)->get_name() << ", other.As_" << (*f_iter)->get_name() << ");" << endl; + indent_down(); + } + out << indent() << "default:" << endl; + indent_up(); + out << indent() << "return true;" << endl; + indent_down(); + indent_down(); + scope_down(out); + scope_down(out); + out << endl; + + out << indent() << "public override int GetHashCode()" << endl; + out << indent() << "{" << endl; + indent_up(); + out << indent() << "switch (Isset)" << endl; + out << indent() << "{" << endl; + indent_up(); + for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) + { + bool needs_typecast = false; + string copy_op = get_deep_copy_method_call((*f_iter)->get_type(), needs_typecast); + out << indent() << "case " << (*f_iter)->get_key() << ":" << endl; + indent_up(); + out << indent() << "return As_" << (*f_iter)->get_name() << ".GetHashCode();" << endl; + indent_down(); + } + out << indent() << "default:" << endl; + indent_up(); + out << indent() << "return (new ___undefined()).GetHashCode();" << endl; + indent_down(); + indent_down(); + out << indent() << "}" << endl; + indent_down(); + out << indent() << "}" << endl << endl; + + if( ! suppress_deepcopy) { + out << indent() << "public " << tunion->get_name() << " DeepCopy()" << endl; + out << indent() << "{" << endl; + indent_up(); + out << indent() << "switch (Isset)" << endl; + out << indent() << "{" << endl; + indent_up(); + for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) + { + bool needs_typecast = false; + string copy_op = get_deep_copy_method_call((*f_iter)->get_type(), needs_typecast); + out << indent() << "case " << (*f_iter)->get_key() << ":" << endl; + indent_up(); + out << indent() << "return new " << (*f_iter)->get_name() << "(As_" << (*f_iter)->get_name() << copy_op << ");" << endl; + indent_down(); + } + out << indent() << "default:" << endl; + indent_up(); + out << indent() << "return new ___undefined();" << endl; + indent_down(); + indent_down(); + out << indent() << "}" << endl; + indent_down(); + out << indent() << "}" << endl << endl; + } + + out << indent() << "public class ___undefined : " << tunion->get_name() << endl; + out << indent() << "{" << endl; indent_up(); out << indent() << "public override object Data { get { return null; } }" << endl - << indent() << "public ___undefined() : base(false) {}" << endl << endl; + << indent() << "public ___undefined() : base(0) {}" << endl << endl; + + if( ! suppress_deepcopy) { + out << indent() << "public new ___undefined DeepCopy()" << endl; + out << indent() << "{" << endl; + indent_up(); + out << indent() << "return new ___undefined();" << endl; + indent_down(); + out << indent() << "}" << endl << endl; + } - out << indent() << "public override Task WriteAsync(TProtocol oprot, CancellationToken cancellationToken)" << endl + t_struct undefined_struct(program_,"___undefined"); + generate_netstd_struct_equals(out, &undefined_struct); + generate_netstd_struct_hashcode(out, &undefined_struct); + + out << indent() << "public override global::System.Threading.Tasks.Task WriteAsync(TProtocol oprot, CancellationToken cancellationToken)" << endl << indent() << "{" << endl; indent_up(); out << indent() << "throw new TProtocolException( TProtocolException.INVALID_DATA, \"Cannot persist an union type which is not set.\");" << endl; @@ -1372,38 +1670,77 @@ void t_netcore_generator::generate_netcore_union_definition(ostream& out, t_stru indent_down(); out << indent() << "}" << endl << endl; - const vector& fields = tunion->get_members(); - vector::const_iterator f_iter; - for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { - generate_netcore_union_class(out, tunion, (*f_iter)); + generate_netstd_union_class(out, tunion, (*f_iter)); } - generate_netcore_union_reader(out, tunion); + generate_netstd_union_reader(out, tunion); indent_down(); out << indent() << "}" << endl << endl; - end_netcore_namespace(out); + end_netstd_namespace(out); } -void t_netcore_generator::generate_netcore_union_class(ostream& out, t_struct* tunion, t_field* tfield) +void t_netstd_generator::generate_netstd_union_class(ostream& out, t_struct* tunion, t_field* tfield) { - out << indent() << "public class " << tfield->get_name() << " : " << tunion->get_name() << endl - << indent() << "{" << endl; + out << indent() << "public " << type_name(tfield->get_type()) << " As_" << tfield->get_name() << endl; + out << indent() << "{" << endl; + indent_up(); + out << indent() << "get" << endl; + out << indent() << "{" << endl; + indent_up(); + out << indent() << "return (" << tfield->get_key() << " == Isset) ? (" << type_name(tfield->get_type()) << ")Data : default(" << type_name(tfield->get_type()) << ");" << endl; + indent_down(); + out << indent() << "}" << endl; + indent_down(); + out << indent() << "}" << endl + << endl; + + + out << indent() << "public class " << tfield->get_name() << " : " << tunion->get_name() << endl; + out << indent() << "{" << endl; indent_up(); out << indent() << "private " << type_name(tfield->get_type()) << " _data;" << endl << indent() << "public override object Data { get { return _data; } }" << endl - << indent() << "public " << tfield->get_name() << "(" << type_name(tfield->get_type()) << " data) : base(true)" << endl + << indent() << "public " << tfield->get_name() << "(" << type_name(tfield->get_type()) << " data) : base("<< tfield->get_key() <<")" << endl << indent() << "{" << endl; indent_up(); out << indent() << "this._data = data;" << endl; indent_down(); out << indent() << "}" << endl; - out << indent() << "public override async Task WriteAsync(TProtocol oprot, CancellationToken cancellationToken) {" << endl; + if( ! suppress_deepcopy) { + out << indent() << "public new " << tfield->get_name() << " DeepCopy()" << endl; + out << indent() << "{" << endl; + indent_up(); + bool needs_typecast = false; + string copy_op = get_deep_copy_method_call(tfield->get_type(), needs_typecast); + out << indent() << "return new " << tfield->get_name() << "(_data" << copy_op << ");" << endl; + indent_down(); + out << indent() << "}" << endl << endl; + } + + out << indent() << "public override bool Equals(object that)" << endl; + out << indent() << "{" << endl; + indent_up(); + out << indent() << "if (!(that is " << tunion->get_name() << " other)) return false;" << endl; + out << indent() << "if (ReferenceEquals(this, other)) return true;" << endl; + out << endl; + out << indent() << "return Equals( _data, other.As_" << tfield->get_name() << ");" << endl; + indent_down(); + out << indent() << "}" << endl << endl; + + out << indent() << "public override int GetHashCode()" << endl; + out << indent() << "{" << endl; + indent_up(); + out << indent() << "return _data.GetHashCode();" << endl; + indent_down(); + out << indent() << "}" << endl << endl; + + out << indent() << "public override async global::System.Threading.Tasks.Task WriteAsync(TProtocol oprot, CancellationToken cancellationToken) {" << endl; indent_up(); out << indent() << "oprot.IncrementRecursionDepth();" << endl @@ -1420,7 +1757,7 @@ void t_netcore_generator::generate_netcore_union_class(ostream& out, t_struct* t << indent() << "field.ID = " << tfield->get_key() << ";" << endl << indent() << "await oprot.WriteFieldBeginAsync(field, cancellationToken);" << endl; - generate_serialize_field(out, tfield, "_data", true, true); + generate_serialize_field(out, tfield, "_data", true); out << indent() << "await oprot.WriteFieldEndAsync(cancellationToken);" << endl << indent() << "await oprot.WriteFieldStopAsync(cancellationToken);" << endl @@ -1433,20 +1770,21 @@ void t_netcore_generator::generate_netcore_union_class(ostream& out, t_struct* t out << indent() << "oprot.DecrementRecursionDepth();" << endl; indent_down(); out << indent() << "}" << endl; + indent_down(); out << indent() << "}" << endl; indent_down(); out << indent() << "}" << endl << endl; } -void t_netcore_generator::generate_netcore_struct_equals(ostream& out, t_struct* tstruct) +void t_netstd_generator::generate_netstd_struct_equals(ostream& out, t_struct* tstruct) { out << indent() << "public override bool Equals(object that)" << endl << indent() << "{" << endl; indent_up(); - out << indent() << "var other = that as " << check_and_correct_struct_name(normalize_name(tstruct->get_name())) << ";" << endl - << indent() << "if (other == null) return false;" << endl + out << indent() << "if (!(that is " << check_and_correct_struct_name(normalize_name(tstruct->get_name())) << " other)) return false;" << endl << indent() << "if (ReferenceEquals(this, other)) return true;" << endl; + const vector& fields = tstruct->get_members(); vector::const_iterator f_iter; @@ -1465,11 +1803,11 @@ void t_netcore_generator::generate_netcore_struct_equals(ostream& out, t_struct* out << endl; out << indent() << "&& "; } - if (!field_is_required((*f_iter)) && !(nullable_ && !field_has_default((*f_iter)))) + if (!field_is_required((*f_iter))) { - out << "((__isset." << normalize_name((*f_iter)->get_name()) << " == other.__isset." - << normalize_name((*f_iter)->get_name()) << ") && ((!__isset." - << normalize_name((*f_iter)->get_name()) << ") || ("; + out << "((__isset." << get_isset_name(normalize_name((*f_iter)->get_name())) << " == other.__isset." + << get_isset_name(normalize_name((*f_iter)->get_name())) << ") && ((!__isset." + << get_isset_name(normalize_name((*f_iter)->get_name())) << ") || ("; } t_type* ttype = (*f_iter)->get_type(); if (ttype->is_container() || ttype->is_binary()) @@ -1481,7 +1819,7 @@ void t_netcore_generator::generate_netcore_struct_equals(ostream& out, t_struct* out << "System.Object.Equals("; } out << prop_name((*f_iter)) << ", other." << prop_name((*f_iter)) << ")"; - if (!field_is_required((*f_iter)) && !(nullable_ && !field_has_default((*f_iter)))) + if (!field_is_required((*f_iter))) { out << ")))"; } @@ -1500,12 +1838,12 @@ void t_netcore_generator::generate_netcore_struct_equals(ostream& out, t_struct* out << indent() << "}" << endl << endl; } -void t_netcore_generator::generate_netcore_struct_hashcode(ostream& out, t_struct* tstruct) +void t_netstd_generator::generate_netstd_struct_hashcode(ostream& out, t_struct* tstruct) { out << indent() << "public override int GetHashCode() {" << endl; indent_up(); - out << indent() << "int hashcode = 0;" << endl; + out << indent() << "int hashcode = 157;" << endl; out << indent() << "unchecked {" << endl; indent_up(); @@ -1515,28 +1853,18 @@ void t_netcore_generator::generate_netcore_struct_hashcode(ostream& out, t_struc for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { t_type* ttype = (*f_iter)->get_type(); - out << indent() << "hashcode = (hashcode * 397) ^ "; - if (field_is_required((*f_iter))) - { - out << "("; - } - else if (nullable_) - { - out << "(" << prop_name((*f_iter)) << " == null ? 0 : "; - } - else - { - out << "(!__isset." << normalize_name((*f_iter)->get_name()) << " ? 0 : "; - } - if (ttype->is_container()) - { - out << "(TCollections.GetHashCode(" << prop_name((*f_iter)) << "))"; + + generate_null_check_begin(out, *f_iter); + out << indent() << "hashcode = (hashcode * 397) + "; + if (ttype->is_container()) { + out << "TCollections.GetHashCode(" << prop_name((*f_iter)) << ")"; } - else - { - out << "(" << prop_name((*f_iter)) << ".GetHashCode())"; + else { + out << prop_name((*f_iter)) << ".GetHashCode()"; } - out << ");" << endl; + out << ";" << endl; + + generate_null_check_end(out, *f_iter); } indent_down(); @@ -1547,7 +1875,7 @@ void t_netcore_generator::generate_netcore_struct_hashcode(ostream& out, t_struc out << indent() << "}" << endl << endl; } -void t_netcore_generator::generate_service(t_service* tservice) +void t_netstd_generator::generate_service(t_service* tservice) { int ic = indent_count(); @@ -1555,9 +1883,10 @@ void t_netcore_generator::generate_service(t_service* tservice) ofstream_with_content_based_conditional_update f_service; f_service.open(f_service_name.c_str()); - f_service << autogen_comment() << netcore_type_usings() << netcore_thrift_usings() << endl; + reset_indent(); + f_service << autogen_comment() << netstd_type_usings() << netstd_thrift_usings() << endl; - start_netcore_namespace(f_service); + start_netstd_namespace(f_service); f_service << indent() << "public partial class " << normalize_name(service_name_) << endl << indent() << "{" << endl; @@ -1571,17 +1900,17 @@ void t_netcore_generator::generate_service(t_service* tservice) indent_down(); f_service << indent() << "}" << endl; - end_netcore_namespace(f_service); + end_netstd_namespace(f_service); f_service.close(); indent_validate(ic, "generate_service."); } -void t_netcore_generator::generate_service_interface(ostream& out, t_service* tservice) +void t_netstd_generator::generate_service_interface(ostream& out, t_service* tservice) { string extends = ""; string extends_iface = ""; - if (tservice->get_extends() != NULL) + if (tservice->get_extends() != nullptr) { extends = type_name(tservice->get_extends()); extends_iface = " : " + extends + ".IAsync"; @@ -1589,9 +1918,9 @@ void t_netcore_generator::generate_service_interface(ostream& out, t_service* ts //out << endl << endl; - generate_netcore_doc(out, tservice); + generate_netstd_doc(out, tservice); - if (wcf_) + if (is_wcf_enabled()) { out << indent() << "[ServiceContract(Namespace=\"" << wcf_namespace_ << "\")]" << endl; } @@ -1604,10 +1933,10 @@ void t_netcore_generator::generate_service_interface(ostream& out, t_service* ts vector::iterator f_iter; for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) { - generate_netcore_doc(out, *f_iter); + generate_netstd_doc(out, *f_iter); // if we're using WCF, add the corresponding attributes - if (wcf_) + if (is_wcf_enabled()) { out << indent() << "[OperationContract]" << endl; @@ -1615,7 +1944,7 @@ void t_netcore_generator::generate_service_interface(ostream& out, t_service* ts vector::const_iterator x_iter; for (x_iter = xceptions.begin(); x_iter != xceptions.end(); ++x_iter) { - out << indent() << "[FaultContract(typeof(" + type_name((*x_iter)->get_type(), false, false) + "Fault))]" << endl; + out << indent() << "[FaultContract(typeof(" + type_name((*x_iter)->get_type()) + "Fault))]" << endl; } } @@ -1625,24 +1954,32 @@ void t_netcore_generator::generate_service_interface(ostream& out, t_service* ts out << indent() << "}" << endl << endl; } -void t_netcore_generator::generate_service_helpers(ostream& out, t_service* tservice) +void t_netstd_generator::generate_service_helpers(ostream& out, t_service* tservice) { vector functions = tservice->get_functions(); vector::iterator f_iter; + out << indent() << "public class InternalStructs" << endl; + out << indent() << "{" << endl; + indent_up(); + for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) { t_struct* ts = (*f_iter)->get_arglist(); - generate_netcore_struct_definition(out, ts, false, true); + collect_extensions_types(ts); + generate_netstd_struct_definition(out, ts, false, true); generate_function_helpers(out, *f_iter); } + + indent_down(); + out << indent() << "}" << endl << endl; } -void t_netcore_generator::generate_service_client(ostream& out, t_service* tservice) +void t_netstd_generator::generate_service_client(ostream& out, t_service* tservice) { string extends = ""; string extends_client = ""; - if (tservice->get_extends() != NULL) + if (tservice->get_extends() != nullptr) { extends = type_name(tservice->get_extends()); extends_client = extends + ".Client, "; @@ -1654,7 +1991,7 @@ void t_netcore_generator::generate_service_client(ostream& out, t_service* tserv out << endl; - generate_netcore_doc(out, tservice); + generate_netstd_doc(out, tservice); out << indent() << "public class Client : " << extends_client << "IAsync" << endl << indent() << "{" << endl; @@ -1683,20 +2020,27 @@ void t_netcore_generator::generate_service_client(ostream& out, t_service* tserv string argsname = (*functions_iterator)->get_name() + "Args"; out << indent() << "await OutputProtocol.WriteMessageBeginAsync(new TMessage(\"" << function_name - << "\", " << ((*functions_iterator)->is_oneway() ? "TMessageType.Oneway" : "TMessageType.Call") << ", SeqId), cancellationToken);" << endl + << "\", TMessageType." << ((*functions_iterator)->is_oneway() ? "Oneway" : "Call") + << ", SeqId), cancellationToken);" << endl << indent() << endl - << indent() << "var args = new " << argsname << "();" << endl; + << indent() << "var args = new InternalStructs." << argsname << "() {" << endl; + indent_up(); t_struct* arg_struct = (*functions_iterator)->get_arglist(); + collect_extensions_types(arg_struct); prepare_member_name_mapping(arg_struct); const vector& fields = arg_struct->get_members(); vector::const_iterator fld_iter; for (fld_iter = fields.begin(); fld_iter != fields.end(); ++fld_iter) { - out << indent() << "args." << prop_name(*fld_iter) << " = " << normalize_name((*fld_iter)->get_name()) << ";" << endl; + out << indent() << prop_name(*fld_iter) << " = " << normalize_name((*fld_iter)->get_name()) << "," << endl; } + indent_down(); + out << indent() << "};" << endl; + + out << indent() << endl << indent() << "await args.WriteAsync(OutputProtocol, cancellationToken);" << endl << indent() << "await OutputProtocol.WriteMessageEndAsync(cancellationToken);" << endl @@ -1707,6 +2051,7 @@ void t_netcore_generator::generate_service_client(ostream& out, t_service* tserv string resultname = (*functions_iterator)->get_name() + "Result"; t_struct noargs(program_); t_struct* xs = (*functions_iterator)->get_xceptions(); + collect_extensions_types(xs); prepare_member_name_mapping(xs, xs->get_members(), resultname); out << indent() << endl @@ -1722,66 +2067,30 @@ void t_netcore_generator::generate_service_client(ostream& out, t_service* tserv out << indent() << "}" << endl << endl - << indent() << "var result = new " << resultname << "();" << endl + << indent() << "var result = new InternalStructs." << resultname << "();" << endl << indent() << "await result.ReadAsync(InputProtocol, cancellationToken);" << endl << indent() << "await InputProtocol.ReadMessageEndAsync(cancellationToken);" << endl; if (!(*functions_iterator)->get_returntype()->is_void()) { - if (nullable_) - { - if (type_can_be_null((*functions_iterator)->get_returntype())) - { - out << indent() << "if (result.Success != null)" << endl - << indent() << "{" << endl; - indent_up(); - out << indent() << "return result.Success;" << endl; - indent_down(); - out << indent() << "}" << endl; - } - else - { - out << indent() << "if (result.Success.HasValue)" << endl - << indent() << "{" << endl; - indent_up(); - out << indent() << "return result.Success.Value;" << endl; - indent_down(); - out << indent() << "}" << endl; - } - } - else - { - out << indent() << "if (result.__isset.success)" << endl - << indent() << "{" << endl; - indent_up(); - out << indent() << "return result.Success;" << endl; - indent_down(); - out << indent() << "}" << endl; - } + out << indent() << "if (result.__isset.success)" << endl + << indent() << "{" << endl; + indent_up(); + out << indent() << "return result.Success;" << endl; + indent_down(); + out << indent() << "}" << endl; } const vector& xceptions = xs->get_members(); vector::const_iterator x_iter; for (x_iter = xceptions.begin(); x_iter != xceptions.end(); ++x_iter) { - if (nullable_) - { - out << indent() << "if (result." << prop_name(*x_iter) << " != null)" << endl - << indent() << "{" << endl; - indent_up(); - out << indent() << "throw result." << prop_name(*x_iter) << ";" << endl; - indent_down(); - out << indent() << "}" << endl; - } - else - { - out << indent() << "if (result.__isset." << normalize_name((*x_iter)->get_name()) << ")" << endl - << indent() << "{" << endl; - indent_up(); - out << indent() << "throw result." << prop_name(*x_iter) << ";" << endl; - indent_down(); - out << indent() << "}" << endl; - } + out << indent() << "if (result.__isset." << get_isset_name(normalize_name((*x_iter)->get_name())) << ")" << endl + << indent() << "{" << endl; + indent_up(); + out << indent() << "throw result." << prop_name(*x_iter) << ";" << endl; + indent_down(); + out << indent() << "}" << endl; } if ((*functions_iterator)->get_returntype()->is_void()) @@ -1809,14 +2118,14 @@ void t_netcore_generator::generate_service_client(ostream& out, t_service* tserv out << indent() << "}" << endl << endl; } -void t_netcore_generator::generate_service_server(ostream& out, t_service* tservice) +void t_netstd_generator::generate_service_server(ostream& out, t_service* tservice) { vector functions = tservice->get_functions(); vector::iterator f_iter; string extends = ""; string extends_processor = ""; - if (tservice->get_extends() != NULL) + if (tservice->get_extends() != nullptr) { extends = type_name(tservice->get_extends()); extends_processor = extends + ".AsyncProcessor, "; @@ -1827,9 +2136,10 @@ void t_netcore_generator::generate_service_server(ostream& out, t_service* tserv indent_up(); - out << indent() << "private IAsync _iAsync;" << endl + out << indent() << "private readonly IAsync _iAsync;" << endl + << indent() << "private readonly ILogger _logger;" << endl << endl - << indent() << "public AsyncProcessor(IAsync iAsync)"; + << indent() << "public AsyncProcessor(IAsync iAsync, ILogger logger = default)"; if (!extends.empty()) { @@ -1840,10 +2150,8 @@ void t_netcore_generator::generate_service_server(ostream& out, t_service* tserv << indent() << "{" << endl; indent_up(); - out << indent() << "if (iAsync == null) throw new ArgumentNullException(nameof(iAsync));" << endl - << endl - << indent() << "_iAsync = iAsync;" << endl; - + out << indent() << "_iAsync = iAsync ?? throw new ArgumentNullException(nameof(iAsync));" << endl; + out << indent() << "_logger = logger;" << endl; for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) { string function_name = (*f_iter)->get_name(); @@ -1856,7 +2164,7 @@ void t_netcore_generator::generate_service_server(ostream& out, t_service* tserv if (extends.empty()) { - out << indent() << "protected delegate Task ProcessFunction(int seqid, TProtocol iprot, TProtocol oprot, CancellationToken cancellationToken);" << endl; + out << indent() << "protected delegate global::System.Threading.Tasks.Task ProcessFunction(int seqid, TProtocol iprot, TProtocol oprot, CancellationToken cancellationToken);" << endl; } if (extends.empty()) @@ -1896,8 +2204,7 @@ void t_netcore_generator::generate_service_server(ostream& out, t_service* tserv indent_up(); out << indent() << "var msg = await iprot.ReadMessageBeginAsync(cancellationToken);" << endl << endl - << indent() << "ProcessFunction fn;" << endl - << indent() << "processMap_.TryGetValue(msg.Name, out fn);" << endl + << indent() << "processMap_.TryGetValue(msg.Name, out ProcessFunction fn);" << endl << endl << indent() << "if (fn == null)" << endl << indent() << "{" << endl; @@ -1937,7 +2244,7 @@ void t_netcore_generator::generate_service_server(ostream& out, t_service* tserv out << indent() << "}" << endl << endl; } -void t_netcore_generator::generate_function_helpers(ostream& out, t_function* tfunction) +void t_netstd_generator::generate_function_helpers(ostream& out, t_function* tfunction) { if (tfunction->is_oneway()) { @@ -1959,13 +2266,14 @@ void t_netcore_generator::generate_function_helpers(ostream& out, t_function* tf result.append(*f_iter); } - generate_netcore_struct_definition(out, &result, false, true, true); + collect_extensions_types(&result); + generate_netstd_struct_definition(out, &result, false, true, true); } -void t_netcore_generator::generate_process_function_async(ostream& out, t_service* tservice, t_function* tfunction) +void t_netstd_generator::generate_process_function_async(ostream& out, t_service* tservice, t_function* tfunction) { (void)tservice; - out << indent() << "public async Task " << tfunction->get_name() + out << indent() << "public async global::System.Threading.Tasks.Task " << tfunction->get_name() << "_ProcessAsync(int seqid, TProtocol iprot, TProtocol oprot, CancellationToken cancellationToken)" << endl << indent() << "{" << endl; indent_up(); @@ -1973,13 +2281,13 @@ void t_netcore_generator::generate_process_function_async(ostream& out, t_servic string argsname = tfunction->get_name() + "Args"; string resultname = tfunction->get_name() + "Result"; - out << indent() << "var args = new " << argsname << "();" << endl + out << indent() << "var args = new InternalStructs." << argsname << "();" << endl << indent() << "await args.ReadAsync(iprot, cancellationToken);" << endl << indent() << "await iprot.ReadMessageEndAsync(cancellationToken);" << endl; if (!tfunction->is_oneway()) { - out << indent() << "var result = new " << resultname << "();" << endl; + out << indent() << "var result = new InternalStructs." << resultname << "();" << endl; } out << indent() << "try" << endl @@ -2009,6 +2317,7 @@ void t_netcore_generator::generate_process_function_async(ostream& out, t_servic out << "await _iAsync." << normalize_name(tfunction->get_name()) << "Async("; bool first = true; + collect_extensions_types(arg_struct); prepare_member_name_mapping(arg_struct); for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { @@ -2022,10 +2331,6 @@ void t_netcore_generator::generate_process_function_async(ostream& out, t_servic } out << "args." << prop_name(*f_iter); - if (nullable_ && !type_can_be_null((*f_iter)->get_type())) - { - out << ".Value"; - } } cleanup_member_name_mapping(arg_struct); @@ -2039,6 +2344,7 @@ void t_netcore_generator::generate_process_function_async(ostream& out, t_servic vector::const_iterator x_iter; + collect_extensions_types(xs); prepare_member_name_mapping(xs, xs->get_members(), resultname); if (xceptions.size() > 0) { @@ -2047,7 +2353,7 @@ void t_netcore_generator::generate_process_function_async(ostream& out, t_servic for (x_iter = xceptions.begin(); x_iter != xceptions.end(); ++x_iter) { - out << indent() << "catch (" << type_name((*x_iter)->get_type(), false, false) << " " << (*x_iter)->get_name() << ")" << endl + out << indent() << "catch (" << type_name((*x_iter)->get_type()) << " " << (*x_iter)->get_name() << ")" << endl << indent() << "{" << endl; if (!tfunction->is_oneway()) @@ -2079,8 +2385,15 @@ void t_netcore_generator::generate_process_function_async(ostream& out, t_servic << indent() << "{" << endl; indent_up(); - out << indent() << "Console.Error.WriteLine(\"Error occurred in processor:\");" << endl - << indent() << "Console.Error.WriteLine(ex.ToString());" << endl; + out << indent() << "var sErr = $\"Error occurred in {GetType().FullName}: {ex.Message}\";" << endl; + out << indent() << "if(_logger != null)" << endl; + indent_up(); + out << indent() << "_logger.LogError(ex, sErr);" << endl; + indent_down(); + out << indent() << "else" << endl; + indent_up(); + out << indent() << "Console.Error.WriteLine(sErr);" << endl; + indent_down(); if (tfunction->is_oneway()) { @@ -2104,7 +2417,7 @@ void t_netcore_generator::generate_process_function_async(ostream& out, t_servic out << indent() << "}" << endl << endl; } -void t_netcore_generator::generate_netcore_union_reader(ostream& out, t_struct* tunion) +void t_netstd_generator::generate_netstd_union_reader(ostream& out, t_struct* tunion) { // Thanks to THRIFT-1768, we don't need to check for required fields in the union const vector& fields = tunion->get_members(); @@ -2180,13 +2493,10 @@ void t_netcore_generator::generate_netcore_union_reader(ostream& out, t_struct* out << indent() << "}" << endl << endl; } -void t_netcore_generator::generate_deserialize_field(ostream& out, t_field* tfield, string prefix, bool is_propertyless) +void t_netstd_generator::generate_deserialize_field(ostream& out, t_field* tfield, string prefix, bool is_propertyless) { t_type* type = tfield->get_type(); - while (type->is_typedef()) - { - type = static_cast(type)->get_type(); - } + type = resolve_typedef( type); if (type->is_void()) { @@ -2209,7 +2519,7 @@ void t_netcore_generator::generate_deserialize_field(ostream& out, t_field* tfie if (type->is_enum()) { - out << "(" << type_name(type, false, true) << ")"; + out << "(" << type_name(type) << ")"; } out << "await iprot."; @@ -2266,9 +2576,9 @@ void t_netcore_generator::generate_deserialize_field(ostream& out, t_field* tfie } } -void t_netcore_generator::generate_deserialize_struct(ostream& out, t_struct* tstruct, string prefix) +void t_netstd_generator::generate_deserialize_struct(ostream& out, t_struct* tstruct, string prefix) { - if (union_ && tstruct->is_union()) + if (is_union_enabled() && tstruct->is_union()) { out << indent() << prefix << " = await " << type_name(tstruct) << ".ReadAsync(iprot, cancellationToken);" << endl; } @@ -2279,7 +2589,7 @@ void t_netcore_generator::generate_deserialize_struct(ostream& out, t_struct* ts } } -void t_netcore_generator::generate_deserialize_container(ostream& out, t_type* ttype, string prefix) +void t_netstd_generator::generate_deserialize_container(ostream& out, t_type* ttype, string prefix) { out << indent() << "{" << endl; indent_up(); @@ -2299,7 +2609,6 @@ void t_netcore_generator::generate_deserialize_container(ostream& out, t_type* t obj = tmp("_list"); } - out << indent() << prefix << " = new " << type_name(ttype, false, true) << "();" << endl; if (ttype->is_map()) { out << indent() << "TMap " << obj << " = await iprot.ReadMapBeginAsync(cancellationToken);" << endl; @@ -2313,6 +2622,7 @@ void t_netcore_generator::generate_deserialize_container(ostream& out, t_type* t out << indent() << "TList " << obj << " = await iprot.ReadListBeginAsync(cancellationToken);" << endl; } + out << indent() << prefix << " = new " << type_name(ttype) << "(" << obj << ".Count);" << endl; string i = tmp("_i"); out << indent() << "for(int " << i << " = 0; " << i << " < " << obj << ".Count; ++" << i << ")" << endl << indent() << "{" << endl; @@ -2351,7 +2661,7 @@ void t_netcore_generator::generate_deserialize_container(ostream& out, t_type* t out << indent() << "}" << endl; } -void t_netcore_generator::generate_deserialize_map_element(ostream& out, t_map* tmap, string prefix) +void t_netstd_generator::generate_deserialize_map_element(ostream& out, t_map* tmap, string prefix) { string key = tmp("_key"); string val = tmp("_val"); @@ -2368,7 +2678,7 @@ void t_netcore_generator::generate_deserialize_map_element(ostream& out, t_map* out << indent() << prefix << "[" << key << "] = " << val << ";" << endl; } -void t_netcore_generator::generate_deserialize_set_element(ostream& out, t_set* tset, string prefix) +void t_netstd_generator::generate_deserialize_set_element(ostream& out, t_set* tset, string prefix) { string elem = tmp("_elem"); t_field felem(tset->get_elem_type(), elem); @@ -2380,7 +2690,7 @@ void t_netcore_generator::generate_deserialize_set_element(ostream& out, t_set* out << indent() << prefix << ".Add(" << elem << ");" << endl; } -void t_netcore_generator::generate_deserialize_list_element(ostream& out, t_list* tlist, string prefix) +void t_netstd_generator::generate_deserialize_list_element(ostream& out, t_list* tlist, string prefix) { string elem = tmp("_elem"); t_field felem(tlist->get_elem_type(), elem); @@ -2392,13 +2702,10 @@ void t_netcore_generator::generate_deserialize_list_element(ostream& out, t_list out << indent() << prefix << ".Add(" << elem << ");" << endl; } -void t_netcore_generator::generate_serialize_field(ostream& out, t_field* tfield, string prefix, bool is_element, bool is_propertyless) +void t_netstd_generator::generate_serialize_field(ostream& out, t_field* tfield, string prefix, bool is_propertyless) { t_type* type = tfield->get_type(); - while (type->is_typedef()) - { - type = static_cast(type)->get_type(); - } + type = resolve_typedef( type); string name = prefix + (is_propertyless ? "" : prop_name(tfield)); @@ -2419,7 +2726,7 @@ void t_netcore_generator::generate_serialize_field(ostream& out, t_field* tfield { out << indent() << "await oprot."; - string nullable_name = nullable_ && !is_element && !field_is_required(tfield) ? name + ".Value" : name; + string nullable_name = name; if (type->is_base_type()) { @@ -2473,13 +2780,13 @@ void t_netcore_generator::generate_serialize_field(ostream& out, t_field* tfield } } -void t_netcore_generator::generate_serialize_struct(ostream& out, t_struct* tstruct, string prefix) +void t_netstd_generator::generate_serialize_struct(ostream& out, t_struct* tstruct, string prefix) { (void)tstruct; out << indent() << "await " << prefix << ".WriteAsync(oprot, cancellationToken);" << endl; } -void t_netcore_generator::generate_serialize_container(ostream& out, t_type* ttype, string prefix) +void t_netstd_generator::generate_serialize_container(ostream& out, t_type* ttype, string prefix) { out << indent() << "{" << endl; indent_up(); @@ -2556,46 +2863,45 @@ void t_netcore_generator::generate_serialize_container(ostream& out, t_type* tty out << indent() << "}" << endl; } -void t_netcore_generator::generate_serialize_map_element(ostream& out, t_map* tmap, string iter, string map) +void t_netstd_generator::generate_serialize_map_element(ostream& out, t_map* tmap, string iter, string map) { t_field kfield(tmap->get_key_type(), iter); - generate_serialize_field(out, &kfield, "", true); + generate_serialize_field(out, &kfield, ""); t_field vfield(tmap->get_val_type(), map + "[" + iter + "]"); - generate_serialize_field(out, &vfield, "", true); + generate_serialize_field(out, &vfield, ""); } -void t_netcore_generator::generate_serialize_set_element(ostream& out, t_set* tset, string iter) +void t_netstd_generator::generate_serialize_set_element(ostream& out, t_set* tset, string iter) { t_field efield(tset->get_elem_type(), iter); - generate_serialize_field(out, &efield, "", true); + generate_serialize_field(out, &efield, ""); } -void t_netcore_generator::generate_serialize_list_element(ostream& out, t_list* tlist, string iter) +void t_netstd_generator::generate_serialize_list_element(ostream& out, t_list* tlist, string iter) { t_field efield(tlist->get_elem_type(), iter); - generate_serialize_field(out, &efield, "", true); + generate_serialize_field(out, &efield, ""); } -void t_netcore_generator::generate_property(ostream& out, t_field* tfield, bool isPublic, bool generateIsset) +void t_netstd_generator::generate_property(ostream& out, t_field* tfield, bool isPublic, bool generateIsset) { - generate_netcore_property(out, tfield, isPublic, generateIsset, "_"); + generate_netstd_property(out, tfield, isPublic, generateIsset, "_"); } -void t_netcore_generator::generate_netcore_property(ostream& out, t_field* tfield, bool isPublic, bool generateIsset, string fieldPrefix) +void t_netstd_generator::generate_netstd_property(ostream& out, t_field* tfield, bool isPublic, bool generateIsset, string fieldPrefix) { - if ((serialize_ || wcf_) && isPublic) + if ((is_serialize_enabled() || is_wcf_enabled()) && isPublic) { out << indent() << "[DataMember(Order = 0)]" << endl; } - bool has_default = field_has_default(tfield); bool is_required = field_is_required(tfield); - if ((nullable_ && !has_default) || is_required) + if (is_required) { - out << indent() << (isPublic ? "public " : "private ") << type_name(tfield->get_type(), false, false, true, is_required) << " " << prop_name(tfield) << " { get; set; }" << endl; + out << indent() << (isPublic ? "public " : "private ") << type_name(tfield->get_type()) << " " << prop_name(tfield) << " { get; set; }" << endl; } else { - out << indent() << (isPublic ? "public " : "private ") << type_name(tfield->get_type(), false, false, true) << " " << prop_name(tfield) << endl + out << indent() << (isPublic ? "public " : "private ") << type_name(tfield->get_type()) << " " << prop_name(tfield) << endl << indent() << "{" << endl; indent_up(); @@ -2604,18 +2910,6 @@ void t_netcore_generator::generate_netcore_property(ostream& out, t_field* tfiel indent_up(); bool use_nullable = false; - if (nullable_) - { - t_type* ttype = tfield->get_type(); - while (ttype->is_typedef()) - { - ttype = static_cast(ttype)->get_type(); - } - if (ttype->is_base_type()) - { - use_nullable = static_cast(ttype)->get_base() != t_base_type::TYPE_STRING; - } - } out << indent() << "return " << fieldPrefix + tfield->get_name() << ";" << endl; indent_down(); @@ -2628,7 +2922,7 @@ void t_netcore_generator::generate_netcore_property(ostream& out, t_field* tfiel { if (generateIsset) { - out << indent() << "__isset." << normalize_name(tfield->get_name()) << " = value.HasValue;" << endl; + out << indent() << "__isset." << get_isset_name(normalize_name(tfield->get_name())) << " = value.HasValue;" << endl; } out << indent() << "if (value.HasValue) this." << fieldPrefix + tfield->get_name() << " = value.Value;" << endl; } @@ -2636,7 +2930,7 @@ void t_netcore_generator::generate_netcore_property(ostream& out, t_field* tfiel { if (generateIsset) { - out << indent() << "__isset." << normalize_name(tfield->get_name()) << " = true;" << endl; + out << indent() << "__isset." << get_isset_name(normalize_name(tfield->get_name())) << " = true;" << endl; } out << indent() << "this." << fieldPrefix + tfield->get_name() << " = value;" << endl; } @@ -2649,7 +2943,7 @@ void t_netcore_generator::generate_netcore_property(ostream& out, t_field* tfiel out << endl; } -string t_netcore_generator::make_valid_csharp_identifier(string const& fromName) +string t_netstd_generator::make_valid_csharp_identifier(string const& fromName) { string str = fromName; if (str.empty()) @@ -2680,7 +2974,7 @@ string t_netcore_generator::make_valid_csharp_identifier(string const& fromName) return str; } -void t_netcore_generator::cleanup_member_name_mapping(void* scope) +void t_netstd_generator::cleanup_member_name_mapping(void* scope) { if (member_mapping_scopes.empty()) { @@ -2696,7 +2990,7 @@ void t_netcore_generator::cleanup_member_name_mapping(void* scope) member_mapping_scopes.pop_back(); } -string t_netcore_generator::get_mapped_member_name(string name) +string t_netstd_generator::get_mapped_member_name(string name) { if (!member_mapping_scopes.empty()) { @@ -2712,15 +3006,15 @@ string t_netcore_generator::get_mapped_member_name(string name) return name; } -void t_netcore_generator::prepare_member_name_mapping(t_struct* tstruct) +void t_netstd_generator::prepare_member_name_mapping(t_struct* tstruct) { prepare_member_name_mapping(tstruct, tstruct->get_members(), tstruct->get_name()); } -void t_netcore_generator::prepare_member_name_mapping(void* scope, const vector& members, const string& structname) +void t_netstd_generator::prepare_member_name_mapping(void* scope, const vector& members, const string& structname) { // begin new scope - member_mapping_scopes.push_back(member_mapping_scope()); + member_mapping_scopes.emplace_back(); member_mapping_scope& active = member_mapping_scopes.back(); active.scope_member = scope; @@ -2732,6 +3026,7 @@ void t_netcore_generator::prepare_member_name_mapping(void* scope, const vector< // prevent name conflicts with struct (CS0542 error) used_member_names.insert(structname); + used_member_names.insert("Isset"); // prevent name conflicts with known methods (THRIFT-2942) used_member_names.insert("Read"); @@ -2761,109 +3056,160 @@ void t_netcore_generator::prepare_member_name_mapping(void* scope, const vector< } } -string t_netcore_generator::prop_name(t_field* tfield, bool suppress_mapping) -{ - string name(tfield->get_name()); - if (suppress_mapping) - { - name[0] = toupper(name[0]); - } - else - { - name = get_mapped_member_name(name); + +string t_netstd_generator::convert_to_pascal_case(const string& str) { + string out; + bool must_capitalize = true; + bool first_character = true; + for (auto it = str.begin(); it != str.end(); ++it) { + if (std::isalnum(*it)) { + if (must_capitalize) { + out.append(1, (char)::toupper(*it)); + must_capitalize = false; + } else { + out.append(1, *it); + } + } else { + if (first_character) //this is a private variable and should not be PascalCased + return str; + must_capitalize = true; } - return name; + first_character = false; + } + return out; } -string t_netcore_generator::type_name(t_type* ttype, bool in_container, bool in_init, bool in_param, bool is_required) -{ - (void)in_init; - while (ttype->is_typedef()) - { - ttype = static_cast(ttype)->get_type(); - } +string t_netstd_generator::get_isset_name(const string& str) { + return ("Isset" != str) ? str : str + "_"; +} + + +string t_netstd_generator::prop_name(t_field* tfield, bool suppress_mapping) { + string name(tfield->get_name()); + if (suppress_mapping) { + name[0] = toupper(name[0]); + if (use_pascal_case_properties) + name = t_netstd_generator::convert_to_pascal_case(name); + } else { + name = get_mapped_member_name(name); + } + + return name; +} + +string t_netstd_generator::type_name(t_type* ttype) +{ + ttype = resolve_typedef(ttype); if (ttype->is_base_type()) { - return base_type_name(static_cast(ttype), in_container, in_param, is_required); + return base_type_name(static_cast(ttype)); } if (ttype->is_map()) { t_map* tmap = static_cast(ttype); - return "Dictionary<" + type_name(tmap->get_key_type(), true) + ", " + type_name(tmap->get_val_type(), true) + ">"; + return "Dictionary<" + type_name(tmap->get_key_type()) + ", " + type_name(tmap->get_val_type()) + ">"; } if (ttype->is_set()) { t_set* tset = static_cast(ttype); - return "THashSet<" + type_name(tset->get_elem_type(), true) + ">"; + return "THashSet<" + type_name(tset->get_elem_type()) + ">"; } if (ttype->is_list()) { t_list* tlist = static_cast(ttype); - return "List<" + type_name(tlist->get_elem_type(), true) + ">"; + return "List<" + type_name(tlist->get_elem_type()) + ">"; } + string the_name = check_and_correct_struct_name(normalize_name(ttype->get_name())); + t_program* program = ttype->get_program(); - string postfix = (!is_required && nullable_ && in_param && ttype->is_enum()) ? "?" : ""; - if (program != NULL && program != program_) + if (program != nullptr)// && program != program_) { - string ns = program->get_namespace("netcore"); + string ns = program->get_namespace("netstd"); if (!ns.empty()) { - return ns + "." + normalize_name(ttype->get_name()) + postfix; + return "global::" + ns + "." + the_name; } } - return normalize_name(ttype->get_name()) + postfix; + return the_name; } -string t_netcore_generator::base_type_name(t_base_type* tbase, bool in_container, bool in_param, bool is_required) +string t_netstd_generator::base_type_name(t_base_type* tbase) { - (void)in_container; - string postfix = (!is_required && nullable_ && in_param) ? "?" : ""; switch (tbase->get_base()) { case t_base_type::TYPE_VOID: return "void"; case t_base_type::TYPE_STRING: + if (tbase->is_binary()) { - if (tbase->is_binary()) - { - return "byte[]"; - } + return "byte[]"; + } else { return "string"; } case t_base_type::TYPE_BOOL: - return "bool" + postfix; + return "bool"; case t_base_type::TYPE_I8: - return "sbyte" + postfix; + return "sbyte"; case t_base_type::TYPE_I16: - return "short" + postfix; + return "short"; case t_base_type::TYPE_I32: - return "int" + postfix; + return "int"; case t_base_type::TYPE_I64: - return "long" + postfix; + return "long"; case t_base_type::TYPE_DOUBLE: - return "double" + postfix; + return "double"; default: throw "compiler error: no C# name for base type " + t_base_type::t_base_name(tbase->get_base()); } } -string t_netcore_generator::declare_field(t_field* tfield, bool init, string prefix) +string t_netstd_generator::get_deep_copy_method_call(t_type* ttype, bool& needs_typecast) +{ + ttype = resolve_typedef(ttype); + + needs_typecast = false; + if (ttype->is_base_type()) + { + t_base_type::t_base tbase = static_cast(ttype)->get_base(); + switch (tbase) + { + case t_base_type::TYPE_STRING: + if (ttype->is_binary()) + { + return ".ToArray()"; + } else { + return ""; // simple assignment will do, strings are immutable in C# + } + break; + default: + return ""; // simple assignment will do + } + } + else if (ttype->is_enum()) + { + return ""; // simple assignment will do + } + else + { + needs_typecast = (! ttype->is_container()); + return "." + DEEP_COPY_METHOD_NAME + "()"; + } +} + +string t_netstd_generator::declare_field(t_field* tfield, bool init, string prefix) { string result = type_name(tfield->get_type()) + " " + prefix + tfield->get_name(); if (init) { t_type* ttype = tfield->get_type(); - while (ttype->is_typedef()) - { - ttype = static_cast(ttype)->get_type(); - } + ttype = resolve_typedef(ttype); if (ttype->is_base_type() && field_has_default(tfield)) { std::ofstream dummy; @@ -2895,30 +3241,30 @@ string t_netcore_generator::declare_field(t_field* tfield, bool init, string pre } else if (ttype->is_enum()) { - result += " = (" + type_name(ttype, false, true) + ")0"; + result += " = (" + type_name(ttype) + ")0"; } else if (ttype->is_container()) { - result += " = new " + type_name(ttype, false, true) + "()"; + result += " = new " + type_name(ttype) + "()"; } else { - result += " = new " + type_name(ttype, false, true) + "()"; + result += " = new " + type_name(ttype) + "()"; } } return result + ";"; } -string t_netcore_generator::function_signature(t_function* tfunction, string prefix) +string t_netstd_generator::function_signature(t_function* tfunction, string prefix) { t_type* ttype = tfunction->get_returntype(); return type_name(ttype) + " " + normalize_name(prefix + tfunction->get_name()) + "(" + argument_list(tfunction->get_arglist()) + ")"; } -string t_netcore_generator::function_signature_async(t_function* tfunction, string prefix) +string t_netstd_generator::function_signature_async(t_function* tfunction, string prefix) { t_type* ttype = tfunction->get_returntype(); - string task = "Task"; + string task = "global::System.Threading.Tasks.Task"; if (!ttype->is_void()) { task += "<" + type_name(ttype) + ">"; @@ -2931,12 +3277,12 @@ string t_netcore_generator::function_signature_async(t_function* tfunction, stri { result += ", "; } - result += "CancellationToken cancellationToken)"; + result += "CancellationToken cancellationToken = default)"; return result; } -string t_netcore_generator::argument_list(t_struct* tstruct) +string t_netstd_generator::argument_list(t_struct* tstruct) { string result = ""; const vector& fields = tstruct->get_members(); @@ -2957,12 +3303,9 @@ string t_netcore_generator::argument_list(t_struct* tstruct) return result; } -string t_netcore_generator::type_to_enum(t_type* type) +string t_netstd_generator::type_to_enum(t_type* type) { - while (type->is_typedef()) - { - type = static_cast(type)->get_type(); - } + type = resolve_typedef( type); if (type->is_base_type()) { @@ -3011,33 +3354,33 @@ string t_netcore_generator::type_to_enum(t_type* type) throw "INVALID TYPE IN type_to_enum: " + type->get_name(); } -void t_netcore_generator::generate_netcore_docstring_comment(ostream& out, string contents) +void t_netstd_generator::generate_netstd_docstring_comment(ostream& out, string contents) { docstring_comment(out, "/// " + endl, "/// ", contents, "/// " + endl); } -void t_netcore_generator::generate_netcore_doc(ostream& out, t_field* field) +void t_netstd_generator::generate_netstd_doc(ostream& out, t_field* field) { if (field->get_type()->is_enum()) { string combined_message = field->get_doc() + endl + "get_type()) + "\"/>"; - generate_netcore_docstring_comment(out, combined_message); + generate_netstd_docstring_comment(out, combined_message); } else { - generate_netcore_doc(out, static_cast(field)); + generate_netstd_doc(out, static_cast(field)); } } -void t_netcore_generator::generate_netcore_doc(ostream& out, t_doc* tdoc) +void t_netstd_generator::generate_netstd_doc(ostream& out, t_doc* tdoc) { if (tdoc->has_doc()) { - generate_netcore_docstring_comment(out, tdoc->get_doc()); + generate_netstd_docstring_comment(out, tdoc->get_doc()); } } -void t_netcore_generator::generate_netcore_doc(ostream& out, t_function* tfunction) +void t_netstd_generator::generate_netstd_doc(ostream& out, t_function* tfunction) { if (tfunction->has_doc()) { @@ -3065,7 +3408,7 @@ void t_netcore_generator::generate_netcore_doc(ostream& out, t_function* tfuncti } } -void t_netcore_generator::docstring_comment(ostream& out, const string& comment_start, const string& line_prefix, const string& contents, const string& comment_end) +void t_netstd_generator::docstring_comment(ostream& out, const string& comment_start, const string& line_prefix, const string& contents, const string& comment_end) { if (comment_start != "") { @@ -3095,23 +3438,23 @@ void t_netcore_generator::docstring_comment(ostream& out, const string& comment_ } } -string t_netcore_generator::get_enum_class_name(t_type* type) +string t_netstd_generator::get_enum_class_name(t_type* type) { string package = ""; t_program* program = type->get_program(); - if (program != NULL && program != program_) + if (program != nullptr) // && program != program_) { - package = program->get_namespace("netcore") + "."; + package = program->get_namespace("netstd") + "."; } - return package + type->get_name(); + return "global::" + package + type->get_name(); } THRIFT_REGISTER_GENERATOR( - netcore, + netstd, "C#", " wcf: Adds bindings for WCF to generated classes.\n" " serial: Add serialization support to generated classes.\n" - " nullable: Use nullable types for properties.\n" - " hashcode: Generate a hashcode and equals implementation for classes.\n" " union: Use new union typing, which includes a static read function for union types.\n" + " pascal: Generate Pascal Case property names according to Microsoft naming convention.\n" + " no_deepcopy: Suppress generation of DeepCopy() method.\n" ) diff --git a/compiler/cpp/src/thrift/generate/t_netstd_generator.h b/compiler/cpp/src/thrift/generate/t_netstd_generator.h new file mode 100644 index 00000000000..1217cc828c3 --- /dev/null +++ b/compiler/cpp/src/thrift/generate/t_netstd_generator.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. + * + * Contains some contributions under the Thrift Software License. + * Please see doc/old-thrift-license.txt in the Thrift distribution for + * details. + */ + +#include + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "thrift/platform.h" +#include "thrift/generate/t_oop_generator.h" + +using std::map; +using std::ostream; +using std::ostringstream; +using std::string; +using std::stringstream; +using std::vector; + +static const string endl = "\n"; // avoid ostream << std::endl flushes + +static const string DEEP_COPY_METHOD_NAME = "DeepCopy"; + +class t_netstd_generator : public t_oop_generator +{ + + struct member_mapping_scope + { + public: + member_mapping_scope() : scope_member(0) { } + void* scope_member; + map mapping_table; + }; + +public: + t_netstd_generator(t_program* program, const map& parsed_options, const string& option_string); + + bool is_wcf_enabled() const; + bool is_hashcode_enabled() const; + bool is_serialize_enabled() const; + bool is_union_enabled() const; + map get_keywords_list() const; + + // overrides + void init_generator(); + void close_generator(); + void generate_consts(vector consts); + void generate_consts(ostream& out, vector consts); + void generate_typedef(t_typedef* ttypedef); + void generate_enum(t_enum* tenum); + void generate_enum(ostream& out, t_enum* tenum); + void generate_struct(t_struct* tstruct); + void generate_xception(t_struct* txception); + void generate_service(t_service* tservice); + + // additional files + void generate_extensions_file(); + + void generate_property(ostream& out, t_field* tfield, bool isPublic, bool generateIsset); + void generate_netstd_property(ostream& out, t_field* tfield, bool isPublic, bool includeIsset = true, string fieldPrefix = ""); + bool print_const_value(ostream& out, string name, t_type* type, t_const_value* value, bool in_static, bool defval = false, bool needtype = false); + string render_const_value(ostream& out, string name, t_type* type, t_const_value* value); + void print_const_constructor(ostream& out, vector consts); + void print_const_def_value(ostream& out, string name, t_type* type, t_const_value* value); + void generate_netstd_struct(t_struct* tstruct, bool is_exception); + void generate_netstd_union(t_struct* tunion); + void generate_netstd_struct_definition(ostream& out, t_struct* tstruct, bool is_xception = false, bool in_class = false, bool is_result = false); + void generate_netstd_union_definition(ostream& out, t_struct* tunion); + void generate_netstd_union_class(ostream& out, t_struct* tunion, t_field* tfield); + void generate_netstd_wcffault(ostream& out, t_struct* tstruct); + void generate_netstd_deepcopy_method(ostream& out, t_struct* tstruct, std::string sharp_struct_name); + void generate_netstd_struct_reader(ostream& out, t_struct* tstruct); + void generate_netstd_struct_result_writer(ostream& out, t_struct* tstruct); + void generate_netstd_struct_writer(ostream& out, t_struct* tstruct); + void generate_netstd_struct_tostring(ostream& out, t_struct* tstruct); + void generate_netstd_struct_equals(ostream& out, t_struct* tstruct); + void generate_netstd_struct_hashcode(ostream& out, t_struct* tstruct); + void generate_netstd_union_reader(ostream& out, t_struct* tunion); + void generate_function_helpers(ostream& out, t_function* tfunction); + void generate_service_interface(ostream& out, t_service* tservice); + void generate_service_helpers(ostream& out, t_service* tservice); + void generate_service_client(ostream& out, t_service* tservice); + void generate_service_server(ostream& out, t_service* tservice); + void generate_process_function_async(ostream& out, t_service* tservice, t_function* function); + void generate_deserialize_field(ostream& out, t_field* tfield, string prefix = "", bool is_propertyless = false); + void generate_deserialize_struct(ostream& out, t_struct* tstruct, string prefix = ""); + void generate_deserialize_container(ostream& out, t_type* ttype, string prefix = ""); + void generate_deserialize_set_element(ostream& out, t_set* tset, string prefix = ""); + void generate_deserialize_map_element(ostream& out, t_map* tmap, string prefix = ""); + void generate_deserialize_list_element(ostream& out, t_list* list, string prefix = ""); + void generate_serialize_field(ostream& out, t_field* tfield, string prefix = "", bool is_propertyless = false); + void generate_serialize_struct(ostream& out, t_struct* tstruct, string prefix = ""); + void generate_serialize_container(ostream& out, t_type* ttype, string prefix = ""); + void generate_serialize_map_element(ostream& out, t_map* tmap, string iter, string map); + void generate_serialize_set_element(ostream& out, t_set* tmap, string iter); + void generate_serialize_list_element(ostream& out, t_list* tlist, string iter); + void generate_netstd_doc(ostream& out, t_field* field); + void generate_netstd_doc(ostream& out, t_doc* tdoc); + void generate_netstd_doc(ostream& out, t_function* tdoc); + void generate_netstd_docstring_comment(ostream& out, string contents); + void docstring_comment(ostream& out, const string& comment_start, const string& line_prefix, const string& contents, const string& comment_end); + void start_netstd_namespace(ostream& out); + void end_netstd_namespace(ostream& out); + + string netstd_type_usings() const; + string netstd_thrift_usings() const; + + string type_name(t_type* ttype); + string base_type_name(t_base_type* tbase); + string declare_field(t_field* tfield, bool init = false, string prefix = ""); + string function_signature_async(t_function* tfunction, string prefix = ""); + string function_signature(t_function* tfunction, string prefix = ""); + string argument_list(t_struct* tstruct); + string type_to_enum(t_type* ttype); + string prop_name(t_field* tfield, bool suppress_mapping = false); + string convert_to_pascal_case(const string& str); + string get_enum_class_name(t_type* type); + +private: + string namespace_name_; + string namespace_dir_; + + bool union_; + bool hashcode_; + bool serialize_; + bool wcf_; + bool use_pascal_case_properties; + bool suppress_deepcopy; + + string wcf_namespace_; + map netstd_keywords; + vector member_mapping_scopes; + map collected_extension_types; + map checked_extension_types; + + void init_keywords(); + string normalize_name(string name); + string make_valid_csharp_identifier(string const& fromName); + void prepare_member_name_mapping(t_struct* tstruct); + void prepare_member_name_mapping(void* scope, const vector& members, const string& structname); + void cleanup_member_name_mapping(void* scope); + string get_mapped_member_name(string oldname); + string get_isset_name(const string& str); + string get_deep_copy_method_call(t_type* ttype, bool& needs_typecast); + void collect_extensions_types(t_struct* tstruct); + void collect_extensions_types(t_type* ttype); + void generate_extensions(ostream& out, map types); + void reset_indent(); + void generate_null_check_begin(ostream& out, t_field* tfield); + void generate_null_check_end(ostream& out, t_field* tfield); +}; diff --git a/compiler/cpp/src/thrift/generate/t_ocaml_generator.cc b/compiler/cpp/src/thrift/generate/t_ocaml_generator.cc index 0ec81ba5eb5..0a961467ee3 100644 --- a/compiler/cpp/src/thrift/generate/t_ocaml_generator.cc +++ b/compiler/cpp/src/thrift/generate/t_ocaml_generator.cc @@ -65,19 +65,19 @@ class t_ocaml_generator : public t_oop_generator { * Init and close methods */ - void init_generator(); - void close_generator(); + void init_generator() override; + void close_generator() override; /** * Program-level generation functions */ - void generate_program(); - void generate_typedef(t_typedef* ttypedef); - void generate_enum(t_enum* tenum); - void generate_const(t_const* tconst); - void generate_struct(t_struct* tstruct); - void generate_xception(t_struct* txception); - void generate_service(t_service* tservice); + void generate_program() override; + void generate_typedef(t_typedef* ttypedef) override; + void generate_enum(t_enum* tenum) override; + void generate_const(t_const* tconst) override; + void generate_struct(t_struct* tstruct) override; + void generate_xception(t_struct* txception) override; + void generate_service(t_service* tservice) override; std::string render_const_value(t_type* type, t_const_value* value); bool struct_member_persistent(t_field* tmember); @@ -404,13 +404,13 @@ string t_ocaml_generator::render_const_value(t_type* type, t_const_value* value) const map& val = value->get_map(); map::const_iterator v_iter; for (v_iter = val.begin(); v_iter != val.end(); ++v_iter) { - t_type* field_type = NULL; + t_type* field_type = nullptr; for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { if ((*f_iter)->get_name() == v_iter->first->get_string()) { field_type = (*f_iter)->get_type(); } } - if (field_type == NULL) { + if (field_type == nullptr) { throw "type error: " + type->get_name() + " has no field " + v_iter->first->get_string(); } string fname = v_iter->first->get_string(); @@ -898,7 +898,7 @@ void t_ocaml_generator::generate_service(t_service* tservice) { f_service_ << ocaml_autogen_comment() << endl << ocaml_imports() << endl; f_service_i_ << ocaml_autogen_comment() << endl << ocaml_imports() << endl; - /* if (tservice->get_extends() != NULL) { + /* if (tservice->get_extends() != nullptr) { f_service_ << "open " << capitalize(tservice->get_extends()->get_name()) << endl; f_service_i_ << @@ -970,7 +970,7 @@ void t_ocaml_generator::generate_service_interface(t_service* tservice) { indent_up(); - if (tservice->get_extends() != NULL) { + if (tservice->get_extends() != nullptr) { string extends = type_name(tservice->get_extends()); indent(f_service_) << "inherit " << extends << ".iface" << endl; indent(f_service_i_) << "inherit " << extends << ".iface" << endl; @@ -1004,7 +1004,7 @@ void t_ocaml_generator::generate_service_client(t_service* tservice) { indent(f_service_i_) << "class client : Protocol.t -> Protocol.t -> " << endl << "object" << endl; indent_up(); - if (tservice->get_extends() != NULL) { + if (tservice->get_extends() != nullptr) { extends = type_name(tservice->get_extends()); indent(f_service_) << "inherit " << extends << ".client iprot oprot as super" << endl; indent(f_service_i_) << "inherit " << extends << ".client" << endl; @@ -1155,7 +1155,7 @@ void t_ocaml_generator::generate_service_server(t_service* tservice) { f_service_i_ << indent() << "inherit Processor.t" << endl << endl; string extends = ""; - if (tservice->get_extends() != NULL) { + if (tservice->get_extends() != nullptr) { extends = type_name(tservice->get_extends()); indent(f_service_) << "inherit " + extends + ".processor (handler :> " + extends + ".iface)" << endl; @@ -1377,7 +1377,7 @@ void t_ocaml_generator::generate_deserialize_type(ostream& out, t_type* type) { void t_ocaml_generator::generate_deserialize_struct(ostream& out, t_struct* tstruct) { string prefix = ""; t_program* program = tstruct->get_program(); - if (program != NULL && program != program_) { + if (program != nullptr && program != program_) { prefix = capitalize(program->get_name()) + "_types."; } string name = decapitalize(tstruct->get_name()); @@ -1658,7 +1658,7 @@ string t_ocaml_generator::argument_list(t_struct* tstruct) { string t_ocaml_generator::type_name(t_type* ttype) { string prefix = ""; t_program* program = ttype->get_program(); - if (program != NULL && program != program_) { + if (program != nullptr && program != program_) { if (!ttype->is_service()) { prefix = capitalize(program->get_name()) + "_types."; } diff --git a/compiler/cpp/src/thrift/generate/t_oop_generator.h b/compiler/cpp/src/thrift/generate/t_oop_generator.h index f8da5479202..2df1be41368 100644 --- a/compiler/cpp/src/thrift/generate/t_oop_generator.h +++ b/compiler/cpp/src/thrift/generate/t_oop_generator.h @@ -59,7 +59,7 @@ class t_oop_generator : public t_generator { virtual std::string get_enum_class_name(t_type* type) { std::string package = ""; t_program* program = type->get_program(); - if (program != NULL && program != program_) { + if (program != nullptr && program != program_) { package = program->get_namespace("java") + "."; } return package + type->get_name(); diff --git a/compiler/cpp/src/thrift/generate/t_perl_generator.cc b/compiler/cpp/src/thrift/generate/t_perl_generator.cc index 8924a76001b..2e687dd5ef6 100644 --- a/compiler/cpp/src/thrift/generate/t_perl_generator.cc +++ b/compiler/cpp/src/thrift/generate/t_perl_generator.cc @@ -66,19 +66,19 @@ class t_perl_generator : public t_oop_generator { * Init and close methods */ - void init_generator(); - void close_generator(); + void init_generator() override; + void close_generator() override; /** * Program-level generation functions */ - void generate_typedef(t_typedef* ttypedef); - void generate_enum(t_enum* tenum); - void generate_const(t_const* tconst); - void generate_struct(t_struct* tstruct); - void generate_xception(t_struct* txception); - void generate_service(t_service* tservice); + void generate_typedef(t_typedef* ttypedef) override; + void generate_enum(t_enum* tenum) override; + void generate_const(t_const* tconst) override; + void generate_struct(t_struct* tstruct) override; + void generate_xception(t_struct* txception) override; + void generate_service(t_service* tservice) override; std::string render_const_value(t_type* type, t_const_value* value); @@ -152,7 +152,7 @@ class t_perl_generator : public t_oop_generator { std::string argument_list(t_struct* tstruct); std::string type_to_enum(t_type* ttype); - std::string autogen_comment() { + std::string autogen_comment() override { return std::string("#\n") + "# Autogenerated by Thrift Compiler (" + THRIFT_VERSION + ")\n" + "#\n" + "# DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING\n" + "#\n"; } @@ -362,13 +362,13 @@ string t_perl_generator::render_const_value(t_type* type, t_const_value* value) const map& val = value->get_map(); map::const_iterator v_iter; for (v_iter = val.begin(); v_iter != val.end(); ++v_iter) { - t_type* field_type = NULL; + t_type* field_type = nullptr; for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { if ((*f_iter)->get_name() == v_iter->first->get_string()) { field_type = (*f_iter)->get_type(); } } - if (field_type == NULL) { + if (field_type == nullptr) { throw "type error: " + type->get_name() + " has no field " + v_iter->first->get_string(); } indent(out) << render_const_value(g_type_string, v_iter->first); @@ -489,7 +489,7 @@ void t_perl_generator::generate_perl_struct_definition(ostream& out, for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) { string dval = "undef"; t_type* t = get_true_type((*m_iter)->get_type()); - if ((*m_iter)->get_value() != NULL && !(t->is_struct() || t->is_xception())) { + if ((*m_iter)->get_value() != nullptr && !(t->is_struct() || t->is_xception())) { dval = render_const_value((*m_iter)->get_type(), (*m_iter)->get_value()); } out << indent() << "$self->{" << (*m_iter)->get_name() << "} = " << dval << ";" << endl; @@ -500,7 +500,7 @@ void t_perl_generator::generate_perl_struct_definition(ostream& out, for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) { t_type* t = get_true_type((*m_iter)->get_type()); - if ((*m_iter)->get_value() != NULL && (t->is_struct() || t->is_xception())) { + if ((*m_iter)->get_value() != nullptr && (t->is_struct() || t->is_xception())) { indent(out) << "$self->{" << (*m_iter)->get_name() << "} = " << render_const_value(t, (*m_iter)->get_value()) << ";" << endl; } @@ -680,7 +680,7 @@ void t_perl_generator::generate_service(t_service* tservice) { generate_use_includes(f_service_, done, tservice, true); t_service* extends_s = tservice->get_extends(); - if (extends_s != NULL) { + if (extends_s != nullptr) { f_service_ << "use " << perl_namespace(extends_s->get_program()) << extends_s->get_name() << ";" << endl; } @@ -712,7 +712,7 @@ void t_perl_generator::generate_service_processor(t_service* tservice) { string extends = ""; string extends_processor = ""; t_service* extends_s = tservice->get_extends(); - if (extends_s != NULL) { + if (extends_s != nullptr) { extends = perl_namespace(extends_s->get_program()) + extends_s->get_name(); extends_processor = "use base qw(" + extends + "Processor);"; } @@ -939,7 +939,7 @@ void t_perl_generator::generate_perl_function_helpers(t_function* tfunction) { void t_perl_generator::generate_service_interface(t_service* tservice) { string extends_if = ""; t_service* extends_s = tservice->get_extends(); - if (extends_s != NULL) { + if (extends_s != nullptr) { extends_if = "use base qw(" + perl_namespace(extends_s->get_program()) + extends_s->get_name() + "If);"; } @@ -964,7 +964,7 @@ void t_perl_generator::generate_service_rest(t_service* tservice) { string extends = ""; string extends_if = ""; t_service* extends_s = tservice->get_extends(); - if (extends_s != NULL) { + if (extends_s != nullptr) { extends = extends_s->get_name(); extends_if = "use base qw(" + perl_namespace(extends_s->get_program()) + extends_s->get_name() + "Rest);"; @@ -1024,7 +1024,7 @@ void t_perl_generator::generate_service_client(t_service* tservice) { string extends = ""; string extends_client = ""; t_service* extends_s = tservice->get_extends(); - if (extends_s != NULL) { + if (extends_s != nullptr) { extends = perl_namespace(extends_s->get_program()) + extends_s->get_name(); extends_client = "use base qw(" + extends + "Client);"; } diff --git a/compiler/cpp/src/thrift/generate/t_php_generator.cc b/compiler/cpp/src/thrift/generate/t_php_generator.cc index 50a44155173..96d5fbe67dc 100644 --- a/compiler/cpp/src/thrift/generate/t_php_generator.cc +++ b/compiler/cpp/src/thrift/generate/t_php_generator.cc @@ -61,6 +61,8 @@ class t_php_generator : public t_oop_generator { oop_ = false; validate_ = false; json_serializable_ = false; + getters_setters_ = false; + nsglobal_ = ""; // by default global namespace is empty classmap_ = false; for (iter = parsed_options.begin(); iter != parsed_options.end(); ++iter) { @@ -86,6 +88,8 @@ class t_php_generator : public t_oop_generator { } else { pwarning(0, "psr4 is default option! needn't add psr4 option!\n"); } + } else if (iter->first.compare("getters_setters") == 0) { + getters_setters_ = true; } else { throw "unknown option php:" + iter->first; } @@ -99,7 +103,7 @@ class t_php_generator : public t_oop_generator { escape_['$'] = "\\$"; } - virtual std::string indent_str() const { + std::string indent_str() const override { return " "; } @@ -109,19 +113,19 @@ class t_php_generator : public t_oop_generator { * Init and close methods */ - void init_generator(); - void close_generator(); + void init_generator() override; + void close_generator() override; /** * Program-level generation functions */ - void generate_typedef(t_typedef* ttypedef); - void generate_enum(t_enum* tenum); - void generate_consts(vector consts); - void generate_struct(t_struct* tstruct); - void generate_xception(t_struct* txception); - void generate_service(t_service* tservice); + void generate_typedef(t_typedef* ttypedef) override; + void generate_enum(t_enum* tenum) override; + void generate_consts(vector consts) override; + void generate_struct(t_struct* tstruct) override; + void generate_xception(t_struct* txception) override; + void generate_service(t_service* tservice) override; std::string render_const_value(t_type* type, t_const_value* value); @@ -150,6 +154,13 @@ class t_php_generator : public t_oop_generator { void generate_php_type_spec(std::ostream& out, t_type* t); void generate_php_struct_spec(std::ostream& out, t_struct* tstruct); + void generate_generic_field_getters_setters(std::ostream& out, t_struct* tstruct); + + void generate_reflection_setters(ostringstream& out, string field_name, string cap_name); + void generate_reflection_getters(ostringstream& out, string field_name, string cap_name); + + std::string get_cap_name(std::string name); + /** * Service-level generation functions @@ -284,9 +295,9 @@ class t_php_generator : public t_oop_generator { } // Transform the java-style namespace into a path. - for (std::string::iterator it = ns.begin(); it != ns.end(); ++it) { - if (*it == '.') { - *it = '/'; + for (char & n : ns) { + if (n == '.') { + n = '/'; } } @@ -304,8 +315,8 @@ class t_php_generator : public t_oop_generator { vector x = split(str, '_'); - for (size_t i = 0; i < x.size(); ++i) { - classe = classe + capitalize(x[i]); + for (const auto & i : x) { + classe = classe + capitalize(i); } return classe; @@ -400,6 +411,11 @@ class t_php_generator : public t_oop_generator { * Global namespace for PHP 5.3 */ std::string nsglobal_; + + /** + * Whether to generate getters and setters + */ + bool getters_setters_; }; bool t_php_generator::is_valid_namespace(const std::string& sub_namespace) { @@ -420,8 +436,8 @@ void t_php_generator::init_generator() { vector NSx = split(php_namespace_suffix(get_program()), '\\'); package_dir_ = get_out_dir(); - for (size_t i = 0; i < NSx.size(); ++i) { - package_dir_ = package_dir_ + "/" + NSx[i] + "/"; + for (const auto & i : NSx) { + package_dir_ = package_dir_ + "/" + i + "/"; MKDIR(package_dir_.c_str()); } @@ -651,13 +667,13 @@ string t_php_generator::render_const_value(t_type* type, t_const_value* value) { const map& val = value->get_map(); map::const_iterator v_iter; for (v_iter = val.begin(); v_iter != val.end(); ++v_iter) { - t_type* field_type = NULL; + t_type* field_type = nullptr; for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { if ((*f_iter)->get_name() == v_iter->first->get_string()) { field_type = (*f_iter)->get_type(); } } - if (field_type == NULL) { + if (field_type == nullptr) { throw "type error: " + type->get_name() + " has no field " + v_iter->first->get_string(); } out << indent(); @@ -746,9 +762,9 @@ void t_php_generator::generate_php_type_spec(ostream& out, t_type* t) { t = get_true_type(t); indent(out) << "'type' => " << type_to_enum(t) << "," << endl; - if (t->is_base_type() || t->is_enum()) { + if (t->is_base_type()) { // Noop, type is all we need - } else if (t->is_struct() || t->is_xception()) { + } else if (t->is_struct() || t->is_xception() || t->is_enum()) { indent(out) << "'class' => '" << php_namespace(t->get_program()) << t->get_name() << "'," << endl; } else if (t->is_map()) { @@ -808,7 +824,80 @@ void t_php_generator::generate_php_struct_spec(ostream& out, t_struct* tstruct) indent_down(); indent(out) << ");" << endl << endl; } +/** + * Generates necessary accessors and mutators for the fields + */ +void t_php_generator::generate_generic_field_getters_setters(std::ostream& out, + t_struct* tstruct) { + std::ostringstream getter_stream; + std::ostringstream setter_stream; + + // build up the bodies of both the getter and setter at once + const vector& fields = tstruct->get_members(); + vector::const_iterator f_iter; + for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { + t_field* field = *f_iter; + std::string field_name = field->get_name(); + std::string cap_name = get_cap_name(field_name); + + indent_up(); + generate_reflection_setters(setter_stream, field_name, cap_name); + generate_reflection_getters(getter_stream, field_name, cap_name); + indent_down(); + } + + indent(out) << endl; + out << getter_stream.str(); + out << setter_stream.str(); + indent(out) << endl; +} +/** + * Generates a getter for the generated private fields + */ +void t_php_generator::generate_reflection_getters(ostringstream& out, + string field_name, + string cap_name) { + + + out << indent() << "public function " << "get" << cap_name << "()" << endl + << indent() << "{" << endl; + + indent_up(); + + out << indent() << "return $this->" << field_name << ";" << endl; + + indent_down(); + out << indent() << "}" << endl; + out << endl; +} +/** + * Generates a setter for the generated private fields + */ +void t_php_generator::generate_reflection_setters(ostringstream& out, + string field_name, + string cap_name) { + + out << indent() << "public function set" << cap_name << "(" << "$" << field_name << ")" << endl + << indent() << "{" << endl; + + indent_up(); + + out << indent() << "$this->" << field_name << " = $" << field_name << ";" << endl; + + indent_down(); + out << indent() << "}" << endl; + out << endl; +} +/** + * Gets the first-letter capitalized name for the field + * + * @param std::string name of the field + */ +std::string t_php_generator::get_cap_name(std::string name) { + name[0] = toupper(name[0]); + return name; +} /** * Generates a struct definition for a thrift data type. This is nothing in PHP * where the objects are all just associative arrays (unless of course we @@ -846,11 +935,12 @@ void t_php_generator::generate_php_struct_definition(ostream& out, for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) { string dval = "null"; t_type* t = get_true_type((*m_iter)->get_type()); - if ((*m_iter)->get_value() != NULL && !(t->is_struct() || t->is_xception())) { + if ((*m_iter)->get_value() != nullptr && !(t->is_struct() || t->is_xception())) { dval = render_const_value((*m_iter)->get_type(), (*m_iter)->get_value()); } generate_php_doc(out, *m_iter); - indent(out) << "public $" << (*m_iter)->get_name() << " = " << dval << ";" << endl; + string access = (getters_setters_) ? "private" : "public"; + indent(out) << access << " $" << (*m_iter)->get_name() << " = " << dval << ";" << endl; } out << endl; @@ -864,7 +954,7 @@ void t_php_generator::generate_php_struct_definition(ostream& out, if (members.size() > 0) { for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) { t_type* t = get_true_type((*m_iter)->get_type()); - if ((*m_iter)->get_value() != NULL && (t->is_struct() || t->is_xception())) { + if ((*m_iter)->get_value() != nullptr && (t->is_struct() || t->is_xception())) { indent(out) << "$this->" << (*m_iter)->get_name() << " = " << render_const_value(t, (*m_iter)->get_value()) << ";" << endl; } @@ -901,6 +991,9 @@ void t_php_generator::generate_php_struct_definition(ostream& out, out << indent() << "}" << endl << endl; out << endl; + if (getters_setters_) { + generate_generic_field_getters_setters(out, tstruct); + } generate_php_struct_reader(out, tstruct, is_result); out << endl; generate_php_struct_writer(out, tstruct, is_result); @@ -1293,7 +1386,7 @@ void t_php_generator::generate_service_processor(t_service* tservice) { string extends = ""; string extends_processor = ""; - if (tservice->get_extends() != NULL) { + if (tservice->get_extends() != nullptr) { extends = tservice->get_extends()->get_name(); extends_processor = " extends " + php_namespace(tservice->get_extends()->get_program()) + extends + "Processor"; @@ -1618,7 +1711,7 @@ void t_php_generator::generate_service_interface(t_service* tservice) { string extends = ""; string extends_if = ""; - if (tservice->get_extends() != NULL) { + if (tservice->get_extends() != nullptr) { extends = " extends " + php_namespace(tservice->get_extends()->get_program()) + tservice->get_extends()->get_name(); extends_if = " extends " + php_namespace(tservice->get_extends()->get_program()) @@ -1657,7 +1750,7 @@ void t_php_generator::generate_service_rest(t_service* tservice) { string extends = ""; string extends_if = ""; - if (tservice->get_extends() != NULL) { + if (tservice->get_extends() != nullptr) { extends = " extends " + php_namespace(tservice->get_extends()->get_program()) + tservice->get_extends()->get_name(); extends_if = " extends " + php_namespace(tservice->get_extends()->get_program()) @@ -1738,7 +1831,7 @@ void t_php_generator::generate_service_client(t_service* tservice) { string extends = ""; string extends_client = ""; - if (tservice->get_extends() != NULL) { + if (tservice->get_extends() != nullptr) { extends = tservice->get_extends()->get_name(); extends_client = " extends " + php_namespace(tservice->get_extends()->get_program()) + extends + "Client"; @@ -2782,4 +2875,5 @@ THRIFT_REGISTER_GENERATOR( " rest: Generate PHP REST processors\n" " nsglobal=NAME: Set global namespace\n" " validate: Generate PHP validator methods\n" - " json: Generate JsonSerializable classes (requires PHP >= 5.4)\n") + " json: Generate JsonSerializable classes (requires PHP >= 5.4)\n" + " getters_setters: Generate Getters and Setters for struct variables\n") diff --git a/compiler/cpp/src/thrift/generate/t_py_generator.cc b/compiler/cpp/src/thrift/generate/t_py_generator.cc index f0a153ca8b9..fe40fc2ddcb 100644 --- a/compiler/cpp/src/thrift/generate/t_py_generator.cc +++ b/compiler/cpp/src/thrift/generate/t_py_generator.cc @@ -52,6 +52,8 @@ class t_py_generator : public t_generator { const std::map& parsed_options, const std::string& option_string) : t_generator (program) { + update_keywords(); + std::map::const_iterator iter; gen_newstyle_ = true; @@ -65,6 +67,7 @@ class t_py_generator : public t_generator { coding_ = ""; gen_dynbaseclass_ = ""; gen_dynbaseclass_exc_ = ""; + gen_dynbaseclass_frozen_exc_ = ""; gen_dynbaseclass_frozen_ = ""; import_dynbase_ = ""; package_prefix_ = ""; @@ -94,8 +97,11 @@ class t_py_generator : public t_generator { if( gen_dynbaseclass_exc_.empty()) { gen_dynbaseclass_exc_ = "TExceptionBase"; } + if( gen_dynbaseclass_frozen_exc_.empty()) { + gen_dynbaseclass_frozen_exc_ = "TFrozenExceptionBase"; + } if( import_dynbase_.empty()) { - import_dynbase_ = "from thrift.protocol.TBase import TBase, TFrozenBase, TExceptionBase, TTransport\n"; + import_dynbase_ = "from thrift.protocol.TBase import TBase, TFrozenBase, TExceptionBase, TFrozenExceptionBase, TTransport\n"; } } else if( iter->first.compare("dynbase") == 0) { gen_dynbase_ = true; @@ -104,6 +110,8 @@ class t_py_generator : public t_generator { gen_dynbaseclass_frozen_ = (iter->second); } else if( iter->first.compare("dynexc") == 0) { gen_dynbaseclass_exc_ = (iter->second); + } else if( iter->first.compare("dynfrozenexc") == 0) { + gen_dynbaseclass_frozen_exc_ = (iter->second); } else if( iter->first.compare("dynimport") == 0) { gen_dynbase_ = true; import_dynbase_ = (iter->second); @@ -136,7 +144,7 @@ class t_py_generator : public t_generator { } } - virtual std::string indent_str() const { + std::string indent_str() const override { return " "; } @@ -144,20 +152,20 @@ class t_py_generator : public t_generator { * Init and close methods */ - void init_generator(); - void close_generator(); + void init_generator() override; + void close_generator() override; /** * Program-level generation functions */ - void generate_typedef(t_typedef* ttypedef); - void generate_enum(t_enum* tenum); - void generate_const(t_const* tconst); - void generate_struct(t_struct* tstruct); - void generate_forward_declaration(t_struct* tstruct); - void generate_xception(t_struct* txception); - void generate_service(t_service* tservice); + void generate_typedef(t_typedef* ttypedef) override; + void generate_enum(t_enum* tenum) override; + void generate_const(t_const* tconst) override; + void generate_struct(t_struct* tstruct) override; + void generate_forward_declaration(t_struct* tstruct) override; + void generate_xception(t_struct* txception) override; + void generate_service(t_service* tservice) override; std::string render_const_value(t_type* type, t_const_value* value); @@ -244,8 +252,8 @@ class t_py_generator : public t_generator { std::string type_name(t_type* ttype); std::string function_signature(t_function* tfunction, bool interface = false); std::string argument_list(t_struct* tstruct, - std::vector* pre = NULL, - std::vector* post = NULL); + std::vector* pre = nullptr, + std::vector* post = nullptr); std::string type_to_enum(t_type* ttype); std::string type_to_spec_args(t_type* ttype); @@ -269,7 +277,16 @@ class t_py_generator : public t_generator { } static bool is_immutable(t_type* ttype) { - return ttype->annotations_.find("python.immutable") != ttype->annotations_.end(); + std::map::iterator it = ttype->annotations_.find("python.immutable"); + + if (it == ttype->annotations_.end()) { + // Exceptions are immutable by default. + return ttype->is_xception(); + } else if (it->second == "false") { + return false; + } else { + return true; + } } private: @@ -288,6 +305,7 @@ class t_py_generator : public t_generator { std::string gen_dynbaseclass_; std::string gen_dynbaseclass_frozen_; std::string gen_dynbaseclass_exc_; + std::string gen_dynbaseclass_frozen_exc_; std::string import_dynbase_; @@ -335,7 +353,7 @@ class t_py_generator : public t_generator { std::string module_; protected: - virtual std::set lang_keywords() const { + std::set lang_keywords() const override { std::string keywords[] = { "False", "None", "True", "and", "as", "assert", "break", "class", "continue", "def", "del", "elif", "else", "except", "exec", "finally", "for", "from", "global", "if", "import", "in", "is", "lambda", "nonlocal", "not", "or", "pass", "print", @@ -415,8 +433,8 @@ void t_py_generator::init_generator() { string t_py_generator::render_includes() { const vector& includes = program_->get_includes(); string result = ""; - for (size_t i = 0; i < includes.size(); ++i) { - result += "import " + get_real_py_module(includes[i], gen_twisted_, package_prefix_) + ".ttypes\n"; + for (auto include : includes) { + result += "import " + get_real_py_module(include, gen_twisted_, package_prefix_) + ".ttypes\n"; } return result; } @@ -538,6 +556,9 @@ string t_py_generator::render_const_value(t_type* type, t_const_value* value) { t_base_type::t_base tbase = ((t_base_type*)type)->get_base(); switch (tbase) { case t_base_type::TYPE_STRING: + if (((t_base_type*)type)->is_binary()) { + out << 'b'; + } out << '"' << get_escaped_string(value) << '"'; break; case t_base_type::TYPE_BOOL: @@ -569,13 +590,13 @@ string t_py_generator::render_const_value(t_type* type, t_const_value* value) { const map& val = value->get_map(); map::const_iterator v_iter; for (v_iter = val.begin(); v_iter != val.end(); ++v_iter) { - t_type* field_type = NULL; + t_type* field_type = nullptr; for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { if ((*f_iter)->get_name() == v_iter->first->get_string()) { field_type = (*f_iter)->get_type(); } } - if (field_type == NULL) { + if (field_type == nullptr) { throw "type error: " + type->get_name() + " has no field " + v_iter->first->get_string(); } indent(out) << render_const_value(g_type_string, v_iter->first) << ": " @@ -739,7 +760,11 @@ void t_py_generator::generate_py_struct_definition(ostream& out, out << endl << endl << "class " << tstruct->get_name(); if (is_exception) { if (gen_dynamic_) { - out << "(" << gen_dynbaseclass_exc_ << ")"; + if (is_immutable(tstruct)) { + out << "(" << gen_dynbaseclass_frozen_exc_ << ")"; + } else { + out << "(" << gen_dynbaseclass_exc_ << ")"; + } } else { out << "(TException)"; } @@ -807,7 +832,7 @@ void t_py_generator::generate_py_struct_definition(ostream& out, for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) { // Initialize fields t_type* type = (*m_iter)->get_type(); - if (!type->is_base_type() && !type->is_enum() && (*m_iter)->get_value() != NULL) { + if (!type->is_base_type() && !type->is_enum() && (*m_iter)->get_value() != nullptr) { indent(out) << "if " << (*m_iter)->get_name() << " is " << "self.thrift_spec[" << (*m_iter)->get_key() << "][4]:" << endl; indent_up(); @@ -962,7 +987,7 @@ void t_py_generator::generate_py_struct_reader(ostream& out, t_struct* tstruct) t_field* tfield = *f_iter; std::ostringstream result; result << tfield->get_name() << " = "; - if (tfield->get_value() != NULL) { + if (tfield->get_value() != nullptr) { result << render_field_default_value(tfield); } else { result << "None"; @@ -1113,7 +1138,7 @@ void t_py_generator::generate_service(t_service* tservice) { f_service_ << py_autogen_comment() << endl << py_imports() << endl; - if (tservice->get_extends() != NULL) { + if (tservice->get_extends() != nullptr) { f_service_ << "import " << get_real_py_module(tservice->get_extends()->get_program(), gen_twisted_, package_prefix_) << "." << tservice->get_extends()->get_name() << endl; @@ -1147,7 +1172,7 @@ void t_py_generator::generate_service(t_service* tservice) { // Close service file f_service_ << "fix_spec(all_structs)" << endl - << "del all_structs" << endl << endl; + << "del all_structs" << endl; f_service_.close(); } @@ -1202,7 +1227,7 @@ void t_py_generator::generate_py_function_helpers(t_function* tfunction) { void t_py_generator::generate_service_interface(t_service* tservice) { string extends = ""; string extends_if = ""; - if (tservice->get_extends() != NULL) { + if (tservice->get_extends() != nullptr) { extends = type_name(tservice->get_extends()); extends_if = "(" + extends + ".Iface)"; } else { @@ -1247,7 +1272,7 @@ void t_py_generator::generate_service_interface(t_service* tservice) { void t_py_generator::generate_service_client(t_service* tservice) { string extends = ""; string extends_client = ""; - if (tservice->get_extends() != NULL) { + if (tservice->get_extends() != nullptr) { extends = type_name(tservice->get_extends()); if (gen_zope_interface_) { extends_client = "(" + extends + ".Client)"; @@ -1584,7 +1609,7 @@ void t_py_generator::generate_service_remote(t_service* tservice) { vector functions = tservice->get_functions(); // Get all function from parents t_service* parent = tservice->get_extends(); - while (parent != NULL) { + while (parent != nullptr) { vector p_functions = parent->get_functions(); functions.insert(functions.end(), p_functions.begin(), p_functions.end()); parent = parent->get_extends(); @@ -1624,7 +1649,6 @@ void t_py_generator::generate_service_remote(t_service* tservice) { << (*f_iter)->get_name() << "("; t_struct* arg_struct = (*f_iter)->get_arglist(); const std::vector& args = arg_struct->get_members(); - vector::const_iterator a_iter; std::vector::size_type num_args = args.size(); bool first = true; for (std::vector::size_type i = 0; i < num_args; ++i) { @@ -1730,7 +1754,6 @@ void t_py_generator::generate_service_remote(t_service* tservice) { t_struct* arg_struct = (*f_iter)->get_arglist(); const std::vector& args = arg_struct->get_members(); - vector::const_iterator a_iter; std::vector::size_type num_args = args.size(); f_remote << "if cmd == '" << (*f_iter)->get_name() << "':" << endl; @@ -1795,7 +1818,7 @@ void t_py_generator::generate_service_server(t_service* tservice) { string extends = ""; string extends_processor = ""; - if (tservice->get_extends() != NULL) { + if (tservice->get_extends() != nullptr) { extends = type_name(tservice->get_extends()); extends_processor = extends + ".Processor, "; } @@ -1833,6 +1856,13 @@ void t_py_generator::generate_service_server(t_service* tservice) { f_service_ << indent() << "self._processMap[\"" << (*f_iter)->get_name() << "\"] = Processor.process_" << (*f_iter)->get_name() << endl; } + f_service_ << indent() << "self._on_message_begin = None" << endl; + indent_down(); + f_service_ << endl; + + f_service_ << indent() << "def on_message_begin(self, func):" << endl; + indent_up(); + f_service_ << indent() << "self._on_message_begin = func" << endl; indent_down(); f_service_ << endl; @@ -1841,6 +1871,10 @@ void t_py_generator::generate_service_server(t_service* tservice) { indent_up(); f_service_ << indent() << "(name, type, seqid) = iprot.readMessageBegin()" << endl; + f_service_ << indent() << "if self._on_message_begin:" << endl; + indent_up(); + f_service_ << indent() << "self._on_message_begin(name, type, seqid)" << endl; + indent_down(); // TODO(mcslee): validate message @@ -2196,7 +2230,7 @@ void t_py_generator::generate_deserialize_field(ostream& out, } else if(!gen_utf8strings_) { out << "readString()"; } else { - out << "readString().decode('utf-8') if sys.version_info[0] == 2 else iprot.readString()"; + out << "readString().decode('utf-8', errors='replace') if sys.version_info[0] == 2 else iprot.readString()"; } break; case t_base_type::TYPE_BOOL: @@ -2575,7 +2609,7 @@ void t_py_generator::generate_python_docstring(ostream& out, t_doc* tdoc) { string t_py_generator::declare_argument(t_field* tfield) { std::ostringstream result; result << tfield->get_name() << "="; - if (tfield->get_value() != NULL) { + if (tfield->get_value() != nullptr) { result << render_field_default_value(tfield); } else { result << "None"; @@ -2590,7 +2624,7 @@ string t_py_generator::declare_argument(t_field* tfield) { */ string t_py_generator::render_field_default_value(t_field* tfield) { t_type* type = get_true_type(tfield->get_type()); - if (tfield->get_value() != NULL) { + if (tfield->get_value() != nullptr) { return render_const_value(type, tfield->get_value()); } else { return "None"; @@ -2609,7 +2643,7 @@ string t_py_generator::function_signature(t_function* tfunction, bool interface) string signature = tfunction->get_name() + "("; if (!(gen_zope_interface_ && interface)) { - pre.push_back("self"); + pre.emplace_back("self"); } signature += argument_list(tfunction->get_arglist(), &pre, &post) + ")"; @@ -2666,7 +2700,7 @@ string t_py_generator::type_name(t_type* ttype) { if (ttype->is_service()) { return get_real_py_module(program, gen_twisted_, package_prefix_) + "." + ttype->get_name(); } - if (program != NULL && program != program_) { + if (program != nullptr && program != program_) { return get_real_py_module(program, gen_twisted_, package_prefix_) + ".ttypes." + ttype->get_name(); } return ttype->get_name(); @@ -2762,6 +2796,7 @@ THRIFT_REGISTER_GENERATOR( " dynbase=CLS Derive generated classes from class CLS instead of TBase.\n" " dynfrozen=CLS Derive generated immutable classes from class CLS instead of TFrozenBase.\n" " dynexc=CLS Derive generated exceptions from CLS instead of TExceptionBase.\n" + " dynfrozenexc=CLS Derive generated immutable exceptions from CLS instead of TFrozenExceptionBase.\n" " dynimport='from foo.bar import CLS'\n" " Add an import line to generated code to find the dynbase class.\n" " package_prefix='top.package.'\n" diff --git a/compiler/cpp/src/thrift/generate/t_rb_generator.cc b/compiler/cpp/src/thrift/generate/t_rb_generator.cc index 13ea2490ebd..116ccaaa7c6 100644 --- a/compiler/cpp/src/thrift/generate/t_rb_generator.cc +++ b/compiler/cpp/src/thrift/generate/t_rb_generator.cc @@ -102,21 +102,21 @@ class t_rb_generator : public t_oop_generator { * Init and close methods */ - void init_generator(); - void close_generator(); + void init_generator() override; + void close_generator() override; /** * Program-level generation functions */ - void generate_typedef(t_typedef* ttypedef); - void generate_enum(t_enum* tenum); - void generate_const(t_const* tconst); - void generate_struct(t_struct* tstruct); - void generate_forward_declaration(t_struct* tstruct); + void generate_typedef(t_typedef* ttypedef) override; + void generate_enum(t_enum* tenum) override; + void generate_const(t_const* tconst) override; + void generate_struct(t_struct* tstruct) override; + void generate_forward_declaration(t_struct* tstruct) override; void generate_union(t_struct* tunion); - void generate_xception(t_struct* txception); - void generate_service(t_service* tservice); + void generate_xception(t_struct* txception) override; + void generate_service(t_service* tservice) override; t_rb_ofstream& render_const_value(t_rb_ofstream& out, t_type* type, t_const_value* value); @@ -307,15 +307,15 @@ string t_rb_generator::render_require_thrift() { string t_rb_generator::render_includes() { const vector& includes = program_->get_includes(); string result = ""; - for (size_t i = 0; i < includes.size(); ++i) { + for (auto include : includes) { if (namespaced_) { - t_program* included = includes[i]; + t_program* included = include; std::string included_require_prefix = rb_namespace_to_path_prefix(included->get_namespace("rb")); std::string included_name = included->get_name(); result += "require '" + included_require_prefix + underscore(included_name) + "_types'\n"; } else { - result += "require '" + underscore(includes[i]->get_name()) + "_types'\n"; + result += "require '" + underscore(include->get_name()) + "_types'\n"; } } if (includes.size() > 0) { @@ -460,13 +460,13 @@ t_rb_ofstream& t_rb_generator::render_const_value(t_rb_ofstream& out, const map& val = value->get_map(); map::const_iterator v_iter; for (v_iter = val.begin(); v_iter != val.end(); ++v_iter) { - t_type* field_type = NULL; + t_type* field_type = nullptr; for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { if ((*f_iter)->get_name() == v_iter->first->get_string()) { field_type = (*f_iter)->get_type(); } } - if (field_type == NULL) { + if (field_type == nullptr) { throw "type error: " + type->get_name() + " has no field " + v_iter->first->get_string(); } out.indent(); @@ -713,7 +713,7 @@ void t_rb_generator::generate_field_defns(t_rb_ofstream& out, t_struct* tstruct) void t_rb_generator::generate_field_data(t_rb_ofstream& out, t_type* field_type, const std::string& field_name = "", - t_const_value* field_value = NULL, + t_const_value* field_value = nullptr, bool optional = false) { field_type = get_true_type(field_type); @@ -724,7 +724,7 @@ void t_rb_generator::generate_field_data(t_rb_ofstream& out, out << ", :name => '" << field_name << "'"; } - if (field_value != NULL) { + if (field_value != nullptr) { out << ", :default => "; render_const_value(out, field_type, field_value); } @@ -763,8 +763,8 @@ void t_rb_generator::generate_field_data(t_rb_ofstream& out, } void t_rb_generator::begin_namespace(t_rb_ofstream& out, vector modules) { - for (vector::iterator m_iter = modules.begin(); m_iter != modules.end(); ++m_iter) { - out.indent() << "module " << *m_iter << endl; + for (auto & module : modules) { + out.indent() << "module " << module << endl; out.indent_up(); } } @@ -788,7 +788,7 @@ void t_rb_generator::generate_service(t_service* tservice) { f_service_ << rb_autogen_comment() << endl << render_require_thrift(); - if (tservice->get_extends() != NULL) { + if (tservice->get_extends() != nullptr) { if (namespaced_) { f_service_ << "require '" << rb_namespace_to_path_prefix( tservice->get_extends()->get_program()->get_namespace("rb")) @@ -868,7 +868,7 @@ void t_rb_generator::generate_rb_function_helpers(t_function* tfunction) { void t_rb_generator::generate_service_client(t_service* tservice) { string extends = ""; string extends_client = ""; - if (tservice->get_extends() != NULL) { + if (tservice->get_extends() != nullptr) { extends = full_type_name(tservice->get_extends()); extends_client = " < " + extends + "::Client "; } @@ -992,7 +992,7 @@ void t_rb_generator::generate_service_server(t_service* tservice) { string extends = ""; string extends_processor = ""; - if (tservice->get_extends() != NULL) { + if (tservice->get_extends() != nullptr) { extends = full_type_name(tservice->get_extends()); extends_processor = " < " + extends + "::Processor "; } @@ -1140,8 +1140,8 @@ string t_rb_generator::type_name(const t_type* ttype) { string t_rb_generator::full_type_name(const t_type* ttype) { string prefix = "::"; vector modules = ruby_modules(ttype->get_program()); - for (vector::iterator m_iter = modules.begin(); m_iter != modules.end(); ++m_iter) { - prefix += *m_iter + "::"; + for (auto & module : modules) { + prefix += module + "::"; } return prefix + type_name(ttype); } diff --git a/compiler/cpp/src/thrift/generate/t_rs_generator.cc b/compiler/cpp/src/thrift/generate/t_rs_generator.cc index 9843d7acb9f..f55b7e0db6b 100644 --- a/compiler/cpp/src/thrift/generate/t_rs_generator.cc +++ b/compiler/cpp/src/thrift/generate/t_rs_generator.cc @@ -74,19 +74,19 @@ class t_rs_generator : public t_generator { * Init and close methods */ - void init_generator(); - void close_generator(); + void init_generator() override; + void close_generator() override; /** * Program-level generation functions */ - void generate_typedef(t_typedef* ttypedef); - void generate_enum(t_enum* tenum); - void generate_const(t_const* tconst); - void generate_struct(t_struct* tstruct); - void generate_xception(t_struct* txception); - void generate_service(t_service* tservice); + void generate_typedef(t_typedef* ttypedef) override; + void generate_enum(t_enum* tenum) override; + void generate_const(t_const* tconst) override; + void generate_struct(t_struct* tstruct) override; + void generate_xception(t_struct* txception) override; + void generate_service(t_service* tservice) override; private: // struct type @@ -516,6 +516,10 @@ class t_rs_generator : public t_generator { // Replace all instances of `search_string` with `replace_string` in `target`. void string_replace(string& target, const string& search_string, const string& replace_string); + + // Adjust field identifier to correctly handle unspecified field identifiers + // THRIFT-4953 + string rust_safe_field_id(int32_t id); }; void t_rs_generator::init_generator() { @@ -543,28 +547,23 @@ void t_rs_generator::render_attributes_and_includes() { f_gen_ << "#![allow(unused_extern_crates)]" << endl; // constructors take *all* struct parameters, which can trigger the "too many arguments" warning // some auto-gen'd types can be deeply nested. clippy recommends factoring them out which is hard to autogen - f_gen_ << "#![cfg_attr(feature = \"cargo-clippy\", allow(too_many_arguments, type_complexity))]" << endl; + f_gen_ << "#![allow(clippy::too_many_arguments, clippy::type_complexity)]" << endl; // prevent rustfmt from running against this file // lines are too long, code is (thankfully!) not visual-indented, etc. f_gen_ << "#![cfg_attr(rustfmt, rustfmt_skip)]" << endl; f_gen_ << endl; // add standard includes - f_gen_ << "extern crate ordered_float;" << endl; - f_gen_ << "extern crate thrift;" << endl; - f_gen_ << "extern crate try_from;" << endl; - f_gen_ << endl; - f_gen_ << "use ordered_float::OrderedFloat;" << endl; f_gen_ << "use std::cell::RefCell;" << endl; f_gen_ << "use std::collections::{BTreeMap, BTreeSet};" << endl; - f_gen_ << "use std::convert::From;" << endl; + f_gen_ << "use std::convert::{From, TryFrom};" << endl; f_gen_ << "use std::default::Default;" << endl; f_gen_ << "use std::error::Error;" << endl; f_gen_ << "use std::fmt;" << endl; f_gen_ << "use std::fmt::{Display, Formatter};" << endl; f_gen_ << "use std::rc::Rc;" << endl; - f_gen_ << "use try_from::TryFrom;" << endl; f_gen_ << endl; + f_gen_ << "use thrift::OrderedFloat;" << endl; f_gen_ << "use thrift::{ApplicationError, ApplicationErrorKind, ProtocolError, ProtocolErrorKind, TThriftClient};" << endl; f_gen_ << "use thrift::protocol::{TFieldIdentifier, TListIdentifier, TMapIdentifier, TMessageIdentifier, TMessageType, TInputProtocol, TOutputProtocol, TSetIdentifier, TStructIdentifier, TType};" << endl; f_gen_ << "use thrift::protocol::field_id;" << endl; @@ -599,7 +598,7 @@ void t_rs_generator::render_attributes_and_includes() { if (!referenced_modules.empty()) { set::iterator module_iter; for (module_iter = referenced_modules.begin(); module_iter != referenced_modules.end(); ++module_iter) { - f_gen_ << "use " << rust_snake_case(*module_iter) << ";" << endl; + f_gen_ << "use crate::" << rust_snake_case(*module_iter) << ";" << endl; } f_gen_ << endl; } @@ -707,7 +706,7 @@ void t_rs_generator::render_const_value(t_type* ttype, t_const_value* tvalue, bo f_gen_ << tvalue->get_integer(); break; case t_base_type::TYPE_DOUBLE: - f_gen_ << "OrderedFloat::from(" << tvalue->get_double() << " as f64)"; + f_gen_ << "OrderedFloat::from(" << tvalue->get_double() << "_f64)"; break; default: throw "cannot generate const value for " + t_base_type::t_base_name(tbase_type->get_base()); @@ -902,18 +901,22 @@ void t_rs_generator::render_enum_impl(const string& enum_name) { f_gen_ << "impl " << enum_name << " {" << endl; indent_up(); + // taking enum as 'self' here because Thrift enums + // are represented as Rust enums with integer values + // it's cheaper to copy the integer as opposed to + // taking a reference to the enum f_gen_ << indent() - << "pub fn write_to_out_protocol(&self, o_prot: &mut TOutputProtocol) -> thrift::Result<()> {" + << "pub fn write_to_out_protocol(self, o_prot: &mut dyn TOutputProtocol) -> thrift::Result<()> {" << endl; indent_up(); - f_gen_ << indent() << "o_prot.write_i32(*self as i32)" << endl; + f_gen_ << indent() << "o_prot.write_i32(self as i32)" << endl; indent_down(); f_gen_ << indent() << "}" << endl; f_gen_ << indent() - << "pub fn read_from_in_protocol(i_prot: &mut TInputProtocol) -> thrift::Result<" << enum_name << "> {" + << "pub fn read_from_in_protocol(i_prot: &mut dyn TInputProtocol) -> thrift::Result<" << enum_name << "> {" << endl; indent_up(); @@ -932,9 +935,9 @@ void t_rs_generator::render_enum_conversion(t_enum* tenum, const string& enum_na f_gen_ << "impl TryFrom for " << enum_name << " {" << endl; indent_up(); - f_gen_ << indent() << "type Err = thrift::Error;"; + f_gen_ << indent() << "type Error = thrift::Error;"; - f_gen_ << indent() << "fn try_from(i: i32) -> Result {" << endl; + f_gen_ << indent() << "fn try_from(i: i32) -> Result {" << endl; indent_up(); f_gen_ << indent() << "match i {" << endl; @@ -1175,8 +1178,8 @@ void t_rs_generator::render_struct_constructor( generic_type_parameters << ", "; generic_type_qualifiers << ", "; } - generic_type_parameters << "F" << member->get_key(); - generic_type_qualifiers << "F" << member->get_key() << ": Intoget_type()) << ">>"; + generic_type_parameters << "F" << rust_safe_field_id(member->get_key()); + generic_type_qualifiers << "F" << rust_safe_field_id(member->get_key()) << ": Intoget_type()) << ">>"; } } @@ -1207,7 +1210,7 @@ void t_rs_generator::render_struct_constructor( } if (is_optional(member_req)) { - args << member_name << ": " << "F" << member->get_key(); + args << member_name << ": " << "F" << rust_safe_field_id(member->get_key()); } else { args << member_name << ": " << to_rust_type(member->get_type()); } @@ -1231,7 +1234,7 @@ void t_rs_generator::render_struct_constructor( << endl; indent_up(); - if (members.size() == 0) { + if (members.empty()) { f_gen_ << indent() << struct_name << " {}" << endl; } else { f_gen_ << indent() << struct_name << " {" << endl; @@ -1245,7 +1248,7 @@ void t_rs_generator::render_struct_constructor( if (is_optional(member_req)) { f_gen_ << indent() << member_name << ": " << member_name << ".into()," << endl; } else { - f_gen_ << indent() << member_name << ": " << member_name << "," << endl; + f_gen_ << indent() << member_name << "," << endl; } } @@ -1404,7 +1407,7 @@ void t_rs_generator::render_struct_sync_write( f_gen_ << indent() << visibility_qualifier(struct_type) - << "fn write_to_out_protocol(&self, o_prot: &mut TOutputProtocol) -> thrift::Result<()> {" + << "fn write_to_out_protocol(&self, o_prot: &mut dyn TOutputProtocol) -> thrift::Result<()> {" << endl; indent_up(); @@ -1436,7 +1439,7 @@ void t_rs_generator::render_struct_sync_write( void t_rs_generator::render_union_sync_write(const string &union_name, t_struct *tstruct) { f_gen_ << indent() - << "pub fn write_to_out_protocol(&self, o_prot: &mut TOutputProtocol) -> thrift::Result<()> {" + << "pub fn write_to_out_protocol(&self, o_prot: &mut dyn TOutputProtocol) -> thrift::Result<()> {" << endl; indent_up(); @@ -1455,6 +1458,10 @@ void t_rs_generator::render_union_sync_write(const string &union_name, t_struct t_field* member = (*members_iter); t_field::e_req member_req = t_field::T_REQUIRED; t_type* ttype = member->get_type(); + if (ttype->is_typedef()) { + // get the actual type of typedef + ttype = ((t_typedef*)ttype)->get_type(); + } string match_var((ttype->is_base_type() && !ttype->is_string()) ? "f" : "ref f"); f_gen_ << indent() @@ -1501,19 +1508,14 @@ void t_rs_generator::render_struct_field_sync_write( indent_up(); f_gen_ << indent() << "o_prot.write_field_begin(&" << field_ident_string << ")?;" << endl; render_type_sync_write("fld_var", true, field_type); - f_gen_ << indent() << "o_prot.write_field_end()?;" << endl; - f_gen_ << indent() << "()" << endl; // FIXME: remove this extraneous '()' + f_gen_ << indent() << "o_prot.write_field_end()?" << endl; indent_down(); - f_gen_ << indent() << "} else {" << endl; // FIXME: remove else branch - indent_up(); /* FIXME: rethink how I deal with OPT_IN_REQ_OUT if (req == t_field::T_OPT_IN_REQ_OUT) { f_gen_ << indent() << "let field_ident = " << field_ident_string << ";" << endl; f_gen_ << indent() << "o_prot.write_field_begin(&field_ident)?;" << endl; f_gen_ << indent() << "o_prot.write_field_end()?;" << endl; }*/ - f_gen_ << indent() << "()" << endl; - indent_down(); f_gen_ << indent() << "}" << endl; } else { f_gen_ << indent() << "o_prot.write_field_begin(&" << field_ident_string << ")?;" << endl; @@ -1678,7 +1680,7 @@ void t_rs_generator::render_struct_sync_read( f_gen_ << indent() << visibility_qualifier(struct_type) - << "fn read_from_in_protocol(i_prot: &mut TInputProtocol) -> thrift::Result<" << struct_name << "> {" + << "fn read_from_in_protocol(i_prot: &mut dyn TInputProtocol) -> thrift::Result<" << struct_name << "> {" << endl; indent_up(); @@ -1723,7 +1725,7 @@ void t_rs_generator::render_struct_sync_read( for (members_iter = members.begin(); members_iter != members.end(); ++members_iter) { t_field* tfield = (*members_iter); - f_gen_ << indent() << tfield->get_key() << " => {" << endl; + f_gen_ << indent() << rust_safe_field_id(tfield->get_key()) << " => {" << endl; indent_up(); render_type_sync_read("val", tfield->get_type()); f_gen_ << indent() << struct_field_read_temp_variable(tfield) << " = Some(val);" << endl; @@ -1800,7 +1802,7 @@ void t_rs_generator::render_struct_sync_read( void t_rs_generator::render_union_sync_read(const string &union_name, t_struct *tstruct) { f_gen_ << indent() - << "pub fn read_from_in_protocol(i_prot: &mut TInputProtocol) -> thrift::Result<" << union_name << "> {" + << "pub fn read_from_in_protocol(i_prot: &mut dyn TInputProtocol) -> thrift::Result<" << union_name << "> {" << endl; indent_up(); @@ -1833,7 +1835,7 @@ void t_rs_generator::render_union_sync_read(const string &union_name, t_struct * vector::const_iterator members_iter; for (members_iter = members.begin(); members_iter != members.end(); ++members_iter) { t_field* member = (*members_iter); - f_gen_ << indent() << member->get_key() << " => {" << endl; + f_gen_ << indent() << rust_safe_field_id(member->get_key()) << " => {" << endl; indent_up(); render_type_sync_read("val", member->get_type()); f_gen_ << indent() << "if ret.is_none() {" << endl; @@ -2037,7 +2039,7 @@ void t_rs_generator::render_map_sync_read(t_map *tmap, const string &map_var) { string t_rs_generator::struct_field_read_temp_variable(t_field* tfield) { std::ostringstream foss; - foss << "f_" << tfield->get_key(); + foss << "f_" << rust_safe_field_id(tfield->get_key()); return foss.str(); } @@ -2209,8 +2211,8 @@ void t_rs_generator::render_sync_client_tthriftclient_impl(const string &client_ << " {" << endl; indent_up(); - f_gen_ << indent() << "fn i_prot_mut(&mut self) -> &mut TInputProtocol { &mut self._i_prot }" << endl; - f_gen_ << indent() << "fn o_prot_mut(&mut self) -> &mut TOutputProtocol { &mut self._o_prot }" << endl; + f_gen_ << indent() << "fn i_prot_mut(&mut self) -> &mut dyn TInputProtocol { &mut self._i_prot }" << endl; + f_gen_ << indent() << "fn o_prot_mut(&mut self) -> &mut dyn TOutputProtocol { &mut self._o_prot }" << endl; f_gen_ << indent() << "fn sequence_number(&self) -> i32 { self._sequence_number }" << endl; f_gen_ << indent() @@ -2305,7 +2307,7 @@ void t_rs_generator::render_sync_send(t_function* tfunc) { for (members_iter = members.begin(); members_iter != members.end(); ++members_iter) { t_field* member = (*members_iter); string member_name(rust_field_name(member)); - struct_definition << member_name << ": " << member_name << ", "; + struct_definition << member_name << ", "; } string struct_fields = struct_definition.str(); if (struct_fields.size() > 0) { @@ -2469,7 +2471,7 @@ void t_rs_generator::render_sync_processor(t_service *tservice) { void t_rs_generator::render_sync_handler_trait(t_service *tservice) { string extension = ""; - if (tservice->get_extends() != NULL) { + if (tservice->get_extends() != nullptr) { t_service* extends = tservice->get_extends(); extension = " : " + rust_namespace(extends) + rust_sync_handler_trait_name(extends); } @@ -2567,7 +2569,7 @@ void t_rs_generator::render_sync_processor_definition_and_impl(t_service *tservi f_gen_ << indent() - << "fn process(&self, i_prot: &mut TInputProtocol, o_prot: &mut TOutputProtocol) -> thrift::Result<()> {" + << "fn process(&self, i_prot: &mut dyn TInputProtocol, o_prot: &mut dyn TOutputProtocol) -> thrift::Result<()> {" << endl; indent_up(); @@ -2612,8 +2614,8 @@ void t_rs_generator::render_sync_process_delegation_functions(t_service *tservic << "fn " << function_name << "(&self, " << "incoming_sequence_number: i32, " - << "i_prot: &mut TInputProtocol, " - << "o_prot: &mut TOutputProtocol) " + << "i_prot: &mut dyn TInputProtocol, " + << "o_prot: &mut dyn TOutputProtocol) " << "-> thrift::Result<()> {" << endl; indent_up(); @@ -2677,8 +2679,8 @@ void t_rs_generator::render_sync_process_function(t_function *tfunc, const strin << "" << "(handler: &H, " << sequence_number_param << ": i32, " - << "i_prot: &mut TInputProtocol, " - << output_protocol_param << ": &mut TOutputProtocol) " + << "i_prot: &mut dyn TInputProtocol, " + << output_protocol_param << ": &mut dyn TOutputProtocol) " << "-> thrift::Result<()> {" << endl; @@ -2750,7 +2752,7 @@ void t_rs_generator::render_sync_handler_failed(t_function *tfunc) { indent_up(); // if there are any user-defined exceptions for this service call handle them first - if (tfunc->get_xceptions() != NULL && tfunc->get_xceptions()->get_sorted_members().size() > 0) { + if (tfunc->get_xceptions() != nullptr && tfunc->get_xceptions()->get_sorted_members().size() > 0) { string user_err_var("usr_err"); f_gen_ << indent() << "thrift::Error::User(" << user_err_var << ") => {" << endl; indent_up(); @@ -2779,7 +2781,7 @@ void t_rs_generator::render_sync_handler_failed(t_function *tfunc) { } void t_rs_generator::render_sync_handler_failed_user_exception_branch(t_function *tfunc) { - if (tfunc->get_xceptions() == NULL || tfunc->get_xceptions()->get_sorted_members().empty()) { + if (tfunc->get_xceptions() == nullptr || tfunc->get_xceptions()->get_sorted_members().empty()) { throw "cannot render user exception branches if no user exceptions defined"; } @@ -2917,7 +2919,7 @@ string t_rs_generator::handler_successful_return_struct(t_function* tfunc) { } // any user-defined exceptions - if (tfunc->get_xceptions() != NULL) { + if (tfunc->get_xceptions() != nullptr) { t_struct* txceptions = tfunc->get_xceptions(); const vector members = txceptions->get_sorted_members(); vector::const_iterator members_iter; @@ -3169,7 +3171,7 @@ t_field::e_req t_rs_generator::actual_field_req(t_field* tfield, t_rs_generator: } bool t_rs_generator::has_args(t_function* tfunc) { - return tfunc->get_arglist() != NULL && !tfunc->get_arglist()->get_sorted_members().empty(); + return tfunc->get_arglist() != nullptr && !tfunc->get_arglist()->get_sorted_members().empty(); } bool t_rs_generator::has_non_void_args(t_function* tfunc) { @@ -3285,8 +3287,8 @@ string t_rs_generator::rust_sync_processor_impl_name(t_service *tservice) { string t_rs_generator::rust_enum_variant_name(const string &name) { bool all_uppercase = true; - for (size_t i = 0; i < name.size(); i++) { - if (isalnum(name[i]) && islower(name[i])) { + for (char i : name) { + if (isalnum(i) && islower(i)) { all_uppercase = false; break; } @@ -3317,6 +3319,17 @@ string t_rs_generator::rust_camel_case(const string& name) { return str; } +string t_rs_generator::rust_safe_field_id(int32_t id) { + string id_str = std::to_string(abs(id)); + if (id >= 0) { + return id_str; + } else { + string str("neg"); + str += id_str; + return str; + } +} + void t_rs_generator::string_replace(string& target, const string& search_string, const string& replace_string) { if (target.empty()) { return; diff --git a/compiler/cpp/src/thrift/generate/t_st_generator.cc b/compiler/cpp/src/thrift/generate/t_st_generator.cc index 595a9497d59..109adc70550 100644 --- a/compiler/cpp/src/thrift/generate/t_st_generator.cc +++ b/compiler/cpp/src/thrift/generate/t_st_generator.cc @@ -71,19 +71,19 @@ class t_st_generator : public t_oop_generator { * Init and close methods */ - void init_generator(); - void close_generator(); + void init_generator() override; + void close_generator() override; /** * Program-level generation functions */ - void generate_typedef(t_typedef* ttypedef); - void generate_enum(t_enum* tenum); - void generate_const(t_const* tconst); - void generate_struct(t_struct* tstruct); - void generate_xception(t_struct* txception); - void generate_service(t_service* tservice); + void generate_typedef(t_typedef* ttypedef) override; + void generate_enum(t_enum* tenum) override; + void generate_const(t_const* tconst) override; + void generate_struct(t_struct* tstruct) override; + void generate_xception(t_struct* txception) override; + void generate_service(t_service* tservice) override; void generate_class_side_definition(); void generate_force_consts(); @@ -233,9 +233,9 @@ string t_st_generator::generated_category() { string cat = program_->get_namespace("smalltalk.category"); // For compatibility with the Thrift grammar, the category must // be punctuated by dots. Replaces them with dashes here. - for (string::iterator iter = cat.begin(); iter != cat.end(); ++iter) { - if (*iter == '.') { - *iter = '-'; + for (char & iter : cat) { + if (iter == '.') { + iter = '-'; } } return cat.size() ? cat : "Generated-" + class_name(); @@ -406,13 +406,13 @@ string t_st_generator::render_const_value(t_type* type, t_const_value* value) { map::const_iterator v_iter; for (v_iter = val.begin(); v_iter != val.end(); ++v_iter) { - t_type* field_type = NULL; + t_type* field_type = nullptr; for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { if ((*f_iter)->get_name() == v_iter->first->get_string()) { field_type = (*f_iter)->get_type(); } } - if (field_type == NULL) { + if (field_type == nullptr) { throw "type error: " + type->get_name() + " has no field " + v_iter->first->get_string(); } @@ -934,7 +934,7 @@ void t_st_generator::generate_service_client(t_service* tservice) { vector functions = tservice->get_functions(); vector::iterator f_iter; - if (tservice->get_extends() != NULL) { + if (tservice->get_extends() != nullptr) { extends = type_name(tservice->get_extends()); extends_client = extends + "Client"; } @@ -1000,7 +1000,7 @@ string t_st_generator::argument_list(t_struct* tstruct) { string t_st_generator::type_name(t_type* ttype) { string prefix = ""; t_program* program = ttype->get_program(); - if (program != NULL && program != program_) { + if (program != nullptr && program != program_) { if (!ttype->is_service()) { prefix = program->get_name() + "_types."; } diff --git a/compiler/cpp/src/thrift/generate/t_swift_generator.cc b/compiler/cpp/src/thrift/generate/t_swift_generator.cc index 31db04dbabd..d8eb733106e 100644 --- a/compiler/cpp/src/thrift/generate/t_swift_generator.cc +++ b/compiler/cpp/src/thrift/generate/t_swift_generator.cc @@ -50,6 +50,8 @@ class t_swift_generator : public t_oop_generator { const map& parsed_options, const string& option_string) : t_oop_generator(program) { + update_keywords(); + (void)option_string; map::const_iterator iter; @@ -94,20 +96,20 @@ class t_swift_generator : public t_oop_generator { * Init and close methods */ - void init_generator(); - void close_generator(); + void init_generator() override; + void close_generator() override; - void generate_consts(vector consts); + void generate_consts(vector consts) override; /** * Program-level generation functions */ - void generate_typedef(t_typedef* ttypedef); - void generate_enum(t_enum* tenum); - void generate_struct(t_struct* tstruct); - void generate_xception(t_struct* txception); - void generate_service(t_service* tservice); + void generate_typedef(t_typedef* ttypedef) override; + void generate_enum(t_enum* tenum) override; + void generate_struct(t_struct* tstruct) override; + void generate_xception(t_struct* txception) override; + void generate_service(t_service* tservice) override; void render_const_value(ostream& out, @@ -288,6 +290,10 @@ class t_swift_generator : public t_oop_generator { bool gen_cocoa_; bool promise_kit_; +protected: + std::set lang_keywords() const override { + return {}; + } }; /** @@ -336,7 +342,7 @@ void t_swift_generator::init_generator() { string t_swift_generator::swift_imports() { vector includes_list; - includes_list.push_back("Foundation"); + includes_list.emplace_back("Foundation"); ostringstream includes; @@ -347,8 +353,8 @@ string t_swift_generator::swift_imports() { if (namespaced_) { const vector& program_includes = program_->get_includes(); - for (size_t i = 0; i < program_includes.size(); ++i) { - includes << ("import " + get_real_swift_module(program_includes[i])) << endl; + for (auto program_include : program_includes) { + includes << ("import " + get_real_swift_module(program_include)) << endl; } } includes << endl; @@ -364,10 +370,10 @@ string t_swift_generator::swift_imports() { string t_swift_generator::swift_thrift_imports() { vector includes_list; - includes_list.push_back("Thrift"); + includes_list.emplace_back("Thrift"); if (gen_cocoa_ && promise_kit_) { - includes_list.push_back("PromiseKit"); + includes_list.emplace_back("PromiseKit"); } ostringstream includes; @@ -697,7 +703,9 @@ void t_swift_generator::generate_swift_struct(ostream& out, } block_open(out); - for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) { + vector sorted = members; + sort(sorted.begin(), sorted.end(), [](t_field *a, t_field *b) { return (a->get_key() < b->get_key()); } ); + for (m_iter = sorted.begin(); m_iter != sorted.end(); ++m_iter) { out << endl; // TODO: Defaults @@ -853,37 +861,27 @@ void t_swift_generator::generate_swift_struct_hashable_extension(ostream& out, indent(out) << "extension " << tstruct->get_name() << " : Hashable"; block_open(out); out << endl; - indent(out) << visibility << " var hashValue : Int"; + indent(out) << visibility << " func hash(into hasher: inout Hasher)"; block_open(out); const vector& members = tstruct->get_members(); vector::const_iterator m_iter; if (!members.empty()) { - indent(out) << "let prime = 31" << endl; - indent(out) << "var result = 1" << endl; if (!tstruct->is_union()) { for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) { t_field* tfield = *m_iter; - string accessor = field_is_optional(tfield) ? "?." : "."; - string defaultor = field_is_optional(tfield) ? " ?? 0" : ""; - indent(out) << "result = prime &* result &+ (" << maybe_escape_identifier(tfield->get_name()) << accessor - << "hashValue" << defaultor << ")" << endl; + indent(out) << "hasher.combine(" << maybe_escape_identifier(tfield->get_name()) << ")" << endl; } } else { indent(out) << "switch self {" << endl; for (m_iter = members.begin(); m_iter != members.end(); m_iter++) { t_field *tfield = *m_iter; - indent(out) << "case ." << tfield->get_name() << "(let val): result = prime &* val.hashValue" << endl; + indent(out) << "case ." << tfield->get_name() << "(let val): hasher.combine(val)" << endl; } indent(out) << "}" << endl << endl; } - indent(out) << "return result" << endl; - } - else { - indent(out) << "return 31" << endl; } - block_close(out); out << endl; block_close(out); @@ -1218,7 +1216,7 @@ void t_swift_generator::generate_swift_struct_reader(ostream& out, if (field_is_optional(*f_iter)) { continue; } - indent(out) << "try proto.validateValue(" << (*f_iter)->get_name() << ", " + indent(out) << "try proto.validateValue(" << maybe_escape_identifier((*f_iter)->get_name()) << ", " << "named: \"" << (*f_iter)->get_name() << "\")" << endl; } } @@ -1601,7 +1599,7 @@ void t_swift_generator::generate_swift_service_protocol(ostream& out, t_service* indent(out) << "public protocol " << tservice->get_name(); t_service* parent = tservice->get_extends(); - if (parent != NULL) { + if (parent != nullptr) { out << " : " << parent->get_name(); } block_open(out); @@ -1686,7 +1684,7 @@ void t_swift_generator::generate_swift_service_client(ostream& out, t_service* t // Inherit from ParentClient t_service* parent = tservice->get_extends(); - out << " : " << ((parent == NULL) ? "TClient" : parent->get_name() + "Client"); + out << " : " << ((parent == nullptr) ? "TClient" : parent->get_name() + "Client"); out << " /* , " << tservice->get_name() << " */"; block_open(out); out << endl; @@ -1731,7 +1729,7 @@ void t_swift_generator::generate_swift_service_client_async(ostream& out, t_serv // Inherit from ParentClient t_service* parent = tservice->get_extends(); - out << " : " << ((parent == NULL) ? "T" : parent->get_name()) + "AsyncClient"; + out << " : " << ((parent == nullptr) ? "T" : parent->get_name()) + "AsyncClient"; out << " /* , " << tservice->get_name() << " */"; block_open(out); @@ -2431,7 +2429,8 @@ void t_swift_generator::generate_swift_service_server_implementation(ostream& ou if (!tfunction->is_oneway()) { out << indent() << "try outProtocol.writeMessageBegin(name: \"" << tfunction->get_name() << "\", type: .reply, sequenceID: sequenceID)" << endl << indent() << "try result.write(to: outProtocol)" << endl - << indent() << "try outProtocol.writeMessageEnd()" << endl; + << indent() << "try outProtocol.writeMessageEnd()" << endl + << indent() << "try outProtocol.transport.flush()" << endl; } } else { for (x_iter = xfields.begin(); x_iter != xfields.end(); ++x_iter) { @@ -2484,7 +2483,8 @@ void t_swift_generator::generate_swift_service_server_implementation(ostream& ou if (!gen_cocoa_) { out << indent() << "catch let error as TApplicationError"; block_open(out); - out << indent() << "try outProtocol.writeException(messageName: messageName, sequenceID: sequenceID, ex: error)" << endl; + out << indent() << "try outProtocol.writeException(messageName: messageName, sequenceID: sequenceID, ex: error)" << endl + << indent() << "try outProtocol.transport.flush()" << endl; block_close(out); block_close(out); out << indent() << "else"; @@ -2492,8 +2492,8 @@ void t_swift_generator::generate_swift_service_server_implementation(ostream& ou out << indent() << "try inProtocol.skip(type: .struct)" << endl << indent() << "try inProtocol.readMessageEnd()" << endl << indent() << "let ex = TApplicationError(error: .unknownMethod(methodName: messageName))" << endl - << indent() << "try outProtocol.writeException(messageName: messageName, " - << "sequenceID: sequenceID, ex: ex)" << endl; + << indent() << "try outProtocol.writeException(messageName: messageName, sequenceID: sequenceID, ex: ex)" << endl + << indent() << "try outProtocol.transport.flush()" << endl; } else { out << indent() << "catch let error as NSError"; block_open(out); @@ -2646,7 +2646,7 @@ void t_swift_generator::render_const_value(ostream& out, for (f_iter = fields.begin(); f_iter != fields.end();) { t_field* tfield = *f_iter; - t_const_value* value = NULL; + t_const_value* value = nullptr; for (v_iter = val.begin(); v_iter != val.end(); ++v_iter) { if (tfield->get_name() == v_iter->first->get_string()) { value = v_iter->second; @@ -2875,8 +2875,8 @@ void t_swift_generator::async_function_docstring(ostream& out, t_function* tfunc } // completion - indent(out) << "/// - completion: TAsyncResult<" << type_name(tfunction->get_returntype()) - << "> wrapping return and following Exceptions: "; + indent(out) << "/// - completion: Result<" << type_name(tfunction->get_returntype()) + << ", Error> wrapping return and following Exceptions: "; t_struct* xs = tfunction->get_xceptions(); const vector& xceptions = xs->get_members(); vector::const_iterator x_iter; @@ -2901,9 +2901,9 @@ string t_swift_generator::async_function_signature(t_function* tfunction) { string result = "func " + (gen_cocoa_ ? function_name(tfunction) : tfunction->get_name()); if (!gen_cocoa_) { - string response_string = "(TAsyncResult<"; + string response_string = "(Result<"; response_string += (ttype->is_void()) ? "Void" : type_name(ttype); - response_string += ">) -> Void"; + response_string += ", Error>) -> Void"; result += "(" + argument_list(tfunction->get_arglist(), "", false) + (targlist->get_members().size() ? ", " : "") + "completion: @escaping " + response_string + ")"; diff --git a/compiler/cpp/src/thrift/generate/t_xml_generator.cc b/compiler/cpp/src/thrift/generate/t_xml_generator.cc index 35fed14d84c..b6692938f61 100644 --- a/compiler/cpp/src/thrift/generate/t_xml_generator.cc +++ b/compiler/cpp/src/thrift/generate/t_xml_generator.cc @@ -79,20 +79,20 @@ class t_xml_generator : public t_generator { out_dir_base_ = "gen-xml"; } - virtual ~t_xml_generator() {} + ~t_xml_generator() override = default; - void init_generator(); - void close_generator(); - void generate_program(); + void init_generator() override; + void close_generator() override; + void generate_program() override; void iterate_program(t_program* program); - void generate_typedef(t_typedef* ttypedef); - void generate_enum(t_enum* tenum); + void generate_typedef(t_typedef* ttypedef) override; + void generate_enum(t_enum* tenum) override; void generate_function(t_function* tfunc); void generate_field(t_field* field); - void generate_service(t_service* tservice); - void generate_struct(t_struct* tstruct); + void generate_service(t_service* tservice) override; + void generate_struct(t_struct* tstruct) override; void generate_annotations(std::map annotations); @@ -245,8 +245,8 @@ void t_xml_generator::write_element_string(string name, string val) { string t_xml_generator::escape_xml_string(const string& input) { std::ostringstream ss; - for (std::string::const_iterator iter = input.begin(); iter != input.end(); iter++) { - switch (*iter) { + for (char iter : input) { + switch (iter) { case '&': ss << "&"; break; @@ -263,7 +263,7 @@ string t_xml_generator::escape_xml_string(const string& input) { ss << ">"; break; default: - ss << *iter; + ss << iter; break; } } diff --git a/compiler/cpp/src/thrift/generate/t_xsd_generator.cc b/compiler/cpp/src/thrift/generate/t_xsd_generator.cc index e487ffca3bb..d7fb6cf2ac1 100644 --- a/compiler/cpp/src/thrift/generate/t_xsd_generator.cc +++ b/compiler/cpp/src/thrift/generate/t_xsd_generator.cc @@ -59,30 +59,30 @@ class t_xsd_generator : public t_generator { out_dir_base_ = "gen-xsd"; } - virtual ~t_xsd_generator() {} + ~t_xsd_generator() override = default; /** * Init and close methods */ - void init_generator(); - void close_generator(); + void init_generator() override; + void close_generator() override; /** * Program-level generation functions */ - void generate_typedef(t_typedef* ttypedef); - void generate_enum(t_enum* tenum) { (void)tenum; } + void generate_typedef(t_typedef* ttypedef) override; + void generate_enum(t_enum* tenum) override { (void)tenum; } - void generate_service(t_service* tservice); - void generate_struct(t_struct* tstruct); + void generate_service(t_service* tservice) override; + void generate_struct(t_struct* tstruct) override; private: void generate_element(std::ostream& out, std::string name, t_type* ttype, - t_struct* attrs = NULL, + t_struct* attrs = nullptr, bool optional = false, bool nillable = false, bool list_element = false); @@ -199,7 +199,7 @@ void t_xsd_generator::generate_element(ostream& out, if (ttype->is_void() || ttype->is_list()) { indent(out) << "" << endl; indent_up(); - if (attrs == NULL && ttype->is_void()) { + if (attrs == nullptr && ttype->is_void()) { indent(out) << "" << endl; } else { indent(out) << "" << endl; @@ -216,12 +216,12 @@ void t_xsd_generator::generate_element(ostream& out, } f_php_ << "$GLOBALS['" << program_->get_name() << "_xsd_elt_" << name << "'] = '" << subname << "';" << endl; - generate_element(out, subname, subtype, NULL, false, false, true); + generate_element(out, subname, subtype, nullptr, false, false, true); indent_down(); indent(out) << "" << endl; indent(out) << "" << endl; } - if (attrs != NULL) { + if (attrs != nullptr) { const vector& members = attrs->get_members(); vector::const_iterator a_iter; for (a_iter = members.begin(); a_iter != members.end(); ++a_iter) { @@ -235,7 +235,7 @@ void t_xsd_generator::generate_element(ostream& out, indent_down(); indent(out) << "" << endl; } else { - if (attrs == NULL) { + if (attrs == nullptr) { indent(out) << "" << endl; diff --git a/compiler/cpp/src/thrift/logging.cc b/compiler/cpp/src/thrift/logging.cc index f821f5fc563..4811c4ef179 100644 --- a/compiler/cpp/src/thrift/logging.cc +++ b/compiler/cpp/src/thrift/logging.cc @@ -17,17 +17,12 @@ * under the License. */ -/** - * Logging functions copied from main.cc to avoid link errors for plugins - */ - #include "thrift/logging.h" #include "thrift/globals.h" #include #include #include -// TODO: make plugins accept log options from main compiler int g_debug = 0; int g_warn = 1; int g_verbose = 0; diff --git a/compiler/cpp/src/thrift/main.cc b/compiler/cpp/src/thrift/main.cc index cdc171c7adf..50bdcea469e 100644 --- a/compiler/cpp/src/thrift/main.cc +++ b/compiler/cpp/src/thrift/main.cc @@ -53,9 +53,6 @@ #include "thrift/parse/t_scope.h" #include "thrift/generate/t_generator.h" #include "thrift/audit/t_audit.h" -#ifdef THRIFT_ENABLE_PLUGIN -#include "thrift/plugin/plugin_output.h" -#endif #include "thrift/version.h" @@ -171,7 +168,7 @@ char* saferealpath(const char* path, char* resolved_path) { #ifdef _WIN32 char buf[MAX_PATH]; char* basename; - DWORD len = GetFullPathName(path, MAX_PATH, buf, &basename); + DWORD len = GetFullPathNameA(path, MAX_PATH, buf, &basename); if (len == 0 || len > MAX_PATH - 1) { strcpy(resolved_path, path); } else { @@ -340,7 +337,7 @@ string include_file(string filename) { // Realpath! char rp[THRIFT_PATH_MAX]; // cppcheck-suppress uninitvar - if (saferealpath(filename.c_str(), rp) == NULL) { + if (saferealpath(filename.c_str(), rp) == nullptr) { pwarning(0, "Cannot open include file %s\n", filename.c_str()); return std::string(); } @@ -363,7 +360,7 @@ string include_file(string filename) { // Realpath! char rp[THRIFT_PATH_MAX]; // cppcheck-suppress uninitvar - if (saferealpath(sfilename.c_str(), rp) == NULL) { + if (saferealpath(sfilename.c_str(), rp) == nullptr) { continue; } @@ -385,20 +382,20 @@ string include_file(string filename) { * Also prints a warning if we are discarding information. */ void clear_doctext() { - if (g_doctext != NULL) { + if (g_doctext != nullptr) { pwarning(2, "Uncaptured doctext at on line %d.", g_doctext_lineno); } free(g_doctext); - g_doctext = NULL; + g_doctext = nullptr; } /** * Reset program doctext information after processing a file */ void reset_program_doctext_info() { - if (g_program_doctext_candidate != NULL) { + if (g_program_doctext_candidate != nullptr) { free(g_program_doctext_candidate); - g_program_doctext_candidate = NULL; + g_program_doctext_candidate = nullptr; } g_program_doctext_lineno = 0; g_program_doctext_status = INVALID; @@ -409,7 +406,7 @@ void reset_program_doctext_info() { * We are sure the program doctext candidate is really the program doctext. */ void declare_valid_program_doctext() { - if ((g_program_doctext_candidate != NULL) && (g_program_doctext_status == STILL_CANDIDATE)) { + if ((g_program_doctext_candidate != nullptr) && (g_program_doctext_status == STILL_CANDIDATE)) { g_program_doctext_status = ABSOLUTELY_SURE; pdebug("%s", "program doctext set to ABSOLUTELY_SURE"); } else { @@ -449,7 +446,7 @@ char* clean_up_doctext(char* doctext) { // A very profound docstring. if (lines.empty()) { - return NULL; + return nullptr; } // Clear leading whitespace from the first line. @@ -619,7 +616,7 @@ void dump_docstrings(t_program* program) { * Emits a warning on list, binary type is typically a much better choice. */ void check_for_list_of_bytes(t_type* list_elem_type) { - if ((g_parse_mode == PROGRAM) && (list_elem_type != NULL) && list_elem_type->is_base_type()) { + if ((g_parse_mode == PROGRAM) && (list_elem_type != nullptr) && list_elem_type->is_base_type()) { t_base_type* tbase = (t_base_type*)list_elem_type; if (tbase->get_base() == t_base_type::TYPE_I8) { pwarning(1, "Consider using the more efficient \"binary\" type instead of \"list\"."); @@ -643,12 +640,12 @@ void emit_byte_type_warning() { /** * Prints deprecation notice for old NS declarations that are no longer supported - * If new_form is NULL, old_form is assumed to be a language identifier, such as "cpp" - * If new_form is not NULL, both arguments are used exactly as given + * If new_form is nullptr, old_form is assumed to be a language identifier, such as "cpp" + * If new_form is not nullptr, both arguments are used exactly as given */ void error_unsupported_namespace_decl(const char* old_form, const char* new_form) { const char* remainder = ""; - if( new_form == NULL) { + if( new_form == nullptr) { new_form = old_form; remainder = "_namespace"; } @@ -811,13 +808,13 @@ void validate_const_rec(std::string name, t_type* type, t_const_value* value) { if (v_iter->first->get_type() != t_const_value::CV_STRING) { throw "type error: " + name + " struct key must be string"; } - t_type* field_type = NULL; + t_type* field_type = nullptr; for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { if ((*f_iter)->get_name() == v_iter->first->get_string()) { field_type = (*f_iter)->get_type(); } } - if (field_type == NULL) { + if (field_type == nullptr) { throw "type error: " + type->get_name() + " has no field " + v_iter->first->get_string(); } @@ -935,7 +932,7 @@ void parse(t_program* program, t_program* parent_program) { if (yyparse() != 0) { failure("Parser error during include pass."); } - } catch (string x) { + } catch (string &x) { failure(x.c_str()); } fclose(yyin); @@ -954,7 +951,7 @@ void parse(t_program* program, t_program* parent_program) { g_parse_mode = PROGRAM; g_program = program; g_scope = program->scope(); - g_parent_scope = (parent_program != NULL) ? parent_program->scope() : NULL; + g_parent_scope = (parent_program != nullptr) ? parent_program->scope() : nullptr; g_parent_prefix = program->get_name() + "."; g_curpath = path; @@ -973,7 +970,7 @@ void parse(t_program* program, t_program* parent_program) { if (yyparse() != 0) { failure("Parser error during types pass."); } - } catch (string x) { + } catch (string &x) { failure(x.c_str()); } fclose(yyin); @@ -985,12 +982,13 @@ void parse(t_program* program, t_program* parent_program) { void generate(t_program* program, const vector& generator_strings) { // Oooohh, recursive code generation, hot!! if (gen_recurse) { + program->set_recursive(true); const vector& includes = program->get_includes(); - for (size_t i = 0; i < includes.size(); ++i) { + for (auto include : includes) { // Propagate output path from parent to child programs - includes[i]->set_out_path(program->get_out_path(), program->is_out_path_absolute()); + include->set_out_path(program->get_out_path(), program->is_out_path_absolute()); - generate(includes[i], generator_strings); + generate(include, generator_strings); } } @@ -1006,27 +1004,9 @@ void generate(t_program* program, const vector& generator_strings) { for (iter = generator_strings.begin(); iter != generator_strings.end(); ++iter) { t_generator* generator = t_generator_registry::get_generator(program, *iter); - if (generator == NULL) { -#ifdef THRIFT_ENABLE_PLUGIN - switch (plugin_output::delegateToPlugin(program, *iter)) { - case plugin_output::PLUGIN_NOT_FOUND: - pwarning(1, "Unable to get a generator for \"%s\".\n", iter->c_str()); - g_generator_failure = true; - break; - case plugin_output::PLUGIN_FAILURE: - pwarning(1, "Plugin generator for \"%s\" failed.\n", iter->c_str()); - g_generator_failure = true; - break; - case plugin_output::PLUGIN_SUCCEESS: - break; - default: - assert(false); - break; - } -#else + if (generator == nullptr) { pwarning(1, "Unable to get a generator for \"%s\".\n", iter->c_str()); g_generator_failure = true; -#endif } else if (generator) { generator->validate_input(); pverbose("Generating \"%s\"\n", iter->c_str()); @@ -1034,10 +1014,12 @@ void generate(t_program* program, const vector& generator_strings) { delete generator; } } - } catch (string s) { + } catch (string &s) { failure("Error: %s\n", s.c_str()); } catch (const char* exc) { failure("Error: %s\n", exc); + } catch (const std::invalid_argument& invalid_argument_exception) { + failure("Error: %s\n", invalid_argument_exception.what()); } } @@ -1050,14 +1032,14 @@ void audit(t_program* new_program, g_incl_searchpath.push_back(old_thrift_include_path); } - parse(old_program, NULL); + parse(old_program, nullptr); g_incl_searchpath = temp_incl_searchpath; if (!new_thrift_include_path.empty()) { g_incl_searchpath.push_back(new_thrift_include_path); } - parse(new_program, NULL); + parse(new_program, nullptr); compare_namespace(new_program, old_program); compare_services(new_program->get_services(), old_program->get_services()); @@ -1077,7 +1059,7 @@ int main(int argc, char** argv) { bool out_path_is_absolute = false; // Setup time string - time_t now = time(NULL); + time_t now = time(nullptr); g_time_str = ctime(&now); // Check for necessary arguments, you gotta have at least a filename and @@ -1099,7 +1081,7 @@ int main(int argc, char** argv) { char* arg; arg = strtok(argv[i], " "); - while (arg != NULL) { + while (arg != nullptr) { // Treat double dashes as single dashes if (arg[0] == '-' && arg[1] == '-') { ++arg; @@ -1127,24 +1109,24 @@ int main(int argc, char** argv) { g_allow_64bit_consts = true; } else if (strcmp(arg, "-gen") == 0) { arg = argv[++i]; - if (arg == NULL) { + if (arg == nullptr) { fprintf(stderr, "Missing generator specification\n"); usage(); } - generator_strings.push_back(arg); + generator_strings.emplace_back(arg); } else if (strcmp(arg, "-I") == 0) { // An argument of "-I\ asdf" is invalid and has unknown results arg = argv[++i]; - if (arg == NULL) { + if (arg == nullptr) { fprintf(stderr, "Missing Include directory\n"); usage(); } - g_incl_searchpath.push_back(arg); + g_incl_searchpath.emplace_back(arg); } else if ((strcmp(arg, "-o") == 0) || (strcmp(arg, "-out") == 0)) { out_path_is_absolute = (strcmp(arg, "-out") == 0) ? true : false; arg = argv[++i]; - if (arg == NULL) { + if (arg == nullptr) { fprintf(stderr, "-o: missing output directory\n"); usage(); } @@ -1162,14 +1144,14 @@ int main(int argc, char** argv) { } else if (strcmp(arg, "-audit") == 0) { g_audit = true; arg = argv[++i]; - if (arg == NULL) { + if (arg == nullptr) { fprintf(stderr, "Missing old thrift file name for audit operation\n"); usage(); } char old_thrift_file_rp[THRIFT_PATH_MAX]; // cppcheck-suppress uninitvar - if (saferealpath(arg, old_thrift_file_rp) == NULL) { + if (saferealpath(arg, old_thrift_file_rp) == nullptr) { failure("Could not open input file with realpath: %s", arg); } old_input_file = string(old_thrift_file_rp); @@ -1177,14 +1159,14 @@ int main(int argc, char** argv) { g_audit_fatal = false; } else if (strcmp(arg, "-Iold") == 0) { arg = argv[++i]; - if (arg == NULL) { + if (arg == nullptr) { fprintf(stderr, "Missing Include directory for old thrift file\n"); usage(); } old_thrift_include_path = string(arg); } else if (strcmp(arg, "-Inew") == 0) { arg = argv[++i]; - if (arg == NULL) { + if (arg == nullptr) { fprintf(stderr, "Missing Include directory for new thrift file\n"); usage(); } @@ -1195,7 +1177,7 @@ int main(int argc, char** argv) { } // Tokenize more - arg = strtok(NULL, " "); + arg = strtok(nullptr, " "); } } @@ -1222,12 +1204,12 @@ int main(int argc, char** argv) { } char new_thrift_file_rp[THRIFT_PATH_MAX]; - if (argv[i] == NULL) { + if (argv[i] == nullptr) { fprintf(stderr, "Missing file name of new thrift file for audit\n"); usage(); } // cppcheck-suppress uninitvar - if (saferealpath(argv[i], new_thrift_file_rp) == NULL) { + if (saferealpath(argv[i], new_thrift_file_rp) == nullptr) { failure("Could not open input file with realpath: %s", argv[i]); } string new_input_file(new_thrift_file_rp); @@ -1248,12 +1230,12 @@ int main(int argc, char** argv) { // Real-pathify it char rp[THRIFT_PATH_MAX]; - if (argv[i] == NULL) { + if (argv[i] == nullptr) { fprintf(stderr, "Missing file name\n"); usage(); } // cppcheck-suppress uninitvar - if (saferealpath(argv[i], rp) == NULL) { + if (saferealpath(argv[i], rp) == nullptr) { failure("Could not open input file with realpath: %s", argv[i]); } string input_file(rp); @@ -1277,7 +1259,7 @@ int main(int argc, char** argv) { program->set_include_prefix(include_prefix); // Parse it! - parse(program, NULL); + parse(program, nullptr); // The current path is not really relevant when we are doing generation. // Reset the variable to make warning messages clearer. diff --git a/compiler/cpp/src/thrift/main.h b/compiler/cpp/src/thrift/main.h index 54abb03c351..4615ace2bf9 100644 --- a/compiler/cpp/src/thrift/main.h +++ b/compiler/cpp/src/thrift/main.h @@ -103,10 +103,10 @@ void emit_byte_type_warning(); /** * Prints deprecation notice for old NS declarations that are no longer supported - * If new_form is NULL, old_form is assumed to be a language identifier, such as "cpp" - * If new_form is not NULL, both arguments are used exactly as given + * If new_form is nullptr, old_form is assumed to be a language identifier, such as "cpp" + * If new_form is not nullptr, both arguments are used exactly as given */ -void error_unsupported_namespace_decl(const char* old_form, const char* new_form = NULL); +void error_unsupported_namespace_decl(const char* old_form, const char* new_form = nullptr); /** * Flex utilities diff --git a/compiler/cpp/src/thrift/parse/parse.cc b/compiler/cpp/src/thrift/parse/parse.cc index 01f763751dc..23db61ccb33 100644 --- a/compiler/cpp/src/thrift/parse/parse.cc +++ b/compiler/cpp/src/thrift/parse/parse.cc @@ -23,11 +23,7 @@ #include "thrift/main.h" t_type* t_type::get_true_type() { - t_type* type = this; - while (type->is_typedef()) { - type = ((t_typedef*)type)->get_type(); - } - return type; + return const_cast(const_cast(this)->get_true_type()); } const t_type* t_type::get_true_type() const { diff --git a/compiler/cpp/src/thrift/parse/t_base_type.h b/compiler/cpp/src/thrift/parse/t_base_type.h index 71398ba70a6..ca2b0f6efcb 100644 --- a/compiler/cpp/src/thrift/parse/t_base_type.h +++ b/compiler/cpp/src/thrift/parse/t_base_type.h @@ -49,11 +49,11 @@ class t_base_type : public t_type { t_base get_base() const { return base_; } - bool is_void() const { return base_ == TYPE_VOID; } + bool is_void() const override { return base_ == TYPE_VOID; } - bool is_string() const { return base_ == TYPE_STRING; } + bool is_string() const override { return base_ == TYPE_STRING; } - bool is_bool() const { return base_ == TYPE_BOOL; } + bool is_bool() const override { return base_ == TYPE_BOOL; } void set_string_list(bool val) { string_list_ = val; } @@ -61,7 +61,7 @@ class t_base_type : public t_type { void set_binary(bool val) { binary_ = val; } - bool is_binary() const { return binary_ && (base_ == TYPE_STRING); } + bool is_binary() const override { return binary_ && (base_ == TYPE_STRING); } void set_string_enum(bool val) { string_enum_ = val; } @@ -71,7 +71,7 @@ class t_base_type : public t_type { const std::vector& get_string_enum_vals() const { return string_enum_vals_; } - bool is_base_type() const { return true; } + bool is_base_type() const override { return true; } static std::string t_base_name(t_base tbase) { switch (tbase) { diff --git a/compiler/cpp/src/thrift/parse/t_const_value.h b/compiler/cpp/src/thrift/parse/t_const_value.h index 6a114cf169b..5b8156f1ada 100644 --- a/compiler/cpp/src/thrift/parse/t_const_value.h +++ b/compiler/cpp/src/thrift/parse/t_const_value.h @@ -26,11 +26,6 @@ #include #include -namespace plugin_output { -template -void convert(From*, To&); -} - /** * A const value is something parsed that could be a map, set, list, struct * or whatever. @@ -51,11 +46,11 @@ class t_const_value { enum t_const_value_type { CV_INTEGER, CV_DOUBLE, CV_STRING, CV_MAP, CV_LIST, CV_IDENTIFIER, CV_UNKNOWN }; - t_const_value() : intVal_(0), doubleVal_(0.0f), enum_((t_enum*)0), valType_(CV_UNKNOWN) {} + t_const_value() : intVal_(0), doubleVal_(0.0f), enum_((t_enum*)nullptr), valType_(CV_UNKNOWN) {} - t_const_value(int64_t val) : doubleVal_(0.0f), enum_((t_enum*)0), valType_(CV_UNKNOWN) { set_integer(val); } + t_const_value(int64_t val) : doubleVal_(0.0f), enum_((t_enum*)nullptr), valType_(CV_UNKNOWN) { set_integer(val); } - t_const_value(std::string val) : intVal_(0), doubleVal_(0.0f), enum_((t_enum*)0), valType_(CV_UNKNOWN) { set_string(val); } + t_const_value(std::string val) : intVal_(0), doubleVal_(0.0f), enum_((t_enum*)nullptr), valType_(CV_UNKNOWN) { set_string(val); } void set_string(std::string val) { valType_ = CV_STRING; @@ -71,7 +66,7 @@ class t_const_value { int64_t get_integer() const { if (valType_ == CV_IDENTIFIER) { - if (enum_ == NULL) { + if (enum_ == nullptr) { throw "have identifier \"" + get_identifier() + "\", but unset enum on line!"; } std::string identifier = get_identifier(); @@ -80,7 +75,7 @@ class t_const_value { identifier = identifier.substr(dot + 1); } t_enum_value* val = enum_->get_constant_by_name(identifier); - if (val == NULL) { + if (val == nullptr) { throw "Unable to find enum value \"" + identifier + "\" in enum \"" + enum_->get_name() + "\""; } @@ -204,10 +199,6 @@ class t_const_value { t_enum* enum_; t_const_value_type valType_; - - // to read enum_ - template - friend void plugin_output::convert(From*, To&); }; #endif diff --git a/compiler/cpp/src/thrift/parse/t_container.h b/compiler/cpp/src/thrift/parse/t_container.h index 5bdab70a7af..a124d31fdb5 100644 --- a/compiler/cpp/src/thrift/parse/t_container.h +++ b/compiler/cpp/src/thrift/parse/t_container.h @@ -26,7 +26,7 @@ class t_container : public t_type { public: t_container() : cpp_name_(), has_cpp_name_(false) {} - virtual ~t_container() {} + ~t_container() override = default; void set_cpp_name(std::string cpp_name) { cpp_name_ = cpp_name; @@ -37,7 +37,7 @@ class t_container : public t_type { std::string get_cpp_name() const { return cpp_name_; } - bool is_container() const { return true; } + bool is_container() const override { return true; } private: std::string cpp_name_; diff --git a/compiler/cpp/src/thrift/parse/t_doc.h b/compiler/cpp/src/thrift/parse/t_doc.h index 7bcb8f5e4b9..0df893eb263 100644 --- a/compiler/cpp/src/thrift/parse/t_doc.h +++ b/compiler/cpp/src/thrift/parse/t_doc.h @@ -31,7 +31,7 @@ class t_doc { public: t_doc() : has_doc_(false) {} - virtual ~t_doc() {} + virtual ~t_doc() = default; void set_doc(const std::string& doc) { doc_ = doc; diff --git a/compiler/cpp/src/thrift/parse/t_enum.h b/compiler/cpp/src/thrift/parse/t_enum.h index 9e23780cb84..3f013ee1f98 100644 --- a/compiler/cpp/src/thrift/parse/t_enum.h +++ b/compiler/cpp/src/thrift/parse/t_enum.h @@ -33,13 +33,13 @@ class t_enum : public t_type { public: t_enum(t_program* program) : t_type(program) {} - void set_name(const std::string& name) { name_ = name; } + void set_name(const std::string& name) override { name_ = name; } void append(t_enum_value* constant) { constants_.push_back(constant); } const std::vector& get_constants() const { return constants_; } - t_enum_value* get_constant_by_name(const std::string& name) { + t_enum_value* get_constant_by_name(const std::string& name) const { const std::vector& enum_values = get_constants(); std::vector::const_iterator c_iter; for (c_iter = enum_values.begin(); c_iter != enum_values.end(); ++c_iter) { @@ -47,10 +47,10 @@ class t_enum : public t_type { return *c_iter; } } - return NULL; + return nullptr; } - t_enum_value* get_constant_by_value(int64_t value) { + t_enum_value* get_constant_by_value(int64_t value) const { const std::vector& enum_values = get_constants(); std::vector::const_iterator c_iter; for (c_iter = enum_values.begin(); c_iter != enum_values.end(); ++c_iter) { @@ -58,15 +58,15 @@ class t_enum : public t_type { return *c_iter; } } - return NULL; + return nullptr; } - t_enum_value* get_min_value() { + t_enum_value* get_min_value() const { const std::vector& enum_values = get_constants(); std::vector::const_iterator c_iter; t_enum_value* min_value; if (enum_values.size() == 0) { - min_value = NULL; + min_value = nullptr; } else { int min_value_value; min_value = enum_values.front(); @@ -81,12 +81,12 @@ class t_enum : public t_type { return min_value; } - t_enum_value* get_max_value() { + t_enum_value* get_max_value() const { const std::vector& enum_values = get_constants(); std::vector::const_iterator c_iter; t_enum_value* max_value; if (enum_values.size() == 0) { - max_value = NULL; + max_value = nullptr; } else { int max_value_value; max_value = enum_values.back(); @@ -101,7 +101,7 @@ class t_enum : public t_type { return max_value; } - bool is_enum() const { return true; } + bool is_enum() const override { return true; } private: std::vector constants_; diff --git a/compiler/cpp/src/thrift/parse/t_enum_value.h b/compiler/cpp/src/thrift/parse/t_enum_value.h index c0bf3adfcb7..70eee861831 100644 --- a/compiler/cpp/src/thrift/parse/t_enum_value.h +++ b/compiler/cpp/src/thrift/parse/t_enum_value.h @@ -34,7 +34,7 @@ class t_enum_value : public t_doc { public: t_enum_value(std::string name, int value) : name_(name), value_(value) {} - ~t_enum_value() {} + ~t_enum_value() override = default; const std::string& get_name() const { return name_; } diff --git a/compiler/cpp/src/thrift/parse/t_field.h b/compiler/cpp/src/thrift/parse/t_field.h index c5f1f800c73..4be87706dd9 100644 --- a/compiler/cpp/src/thrift/parse/t_field.h +++ b/compiler/cpp/src/thrift/parse/t_field.h @@ -41,10 +41,10 @@ class t_field : public t_doc { : type_(type), name_(name), key_(0), - value_(NULL), + value_(nullptr), xsd_optional_(false), xsd_nillable_(false), - xsd_attrs_(NULL), + xsd_attrs_(nullptr), reference_(false) {} t_field(t_type* type, std::string name, int32_t key) @@ -52,13 +52,13 @@ class t_field : public t_doc { name_(name), key_(key), req_(T_OPT_IN_REQ_OUT), - value_(NULL), + value_(nullptr), xsd_optional_(false), xsd_nillable_(false), - xsd_attrs_(NULL), + xsd_attrs_(nullptr), reference_(false) {} - ~t_field() {} + ~t_field() override = default; t_type* get_type() { return type_; } @@ -92,6 +92,8 @@ class t_field : public t_doc { t_struct* get_xsd_attrs() { return xsd_attrs_; } + const t_struct* get_xsd_attrs() const { return xsd_attrs_; } + /** * Comparator to sort fields in ascending order by key. * Make this a functor instead of a function to help GCC inline it. @@ -105,7 +107,7 @@ class t_field : public t_doc { std::map annotations_; - bool get_reference() { return reference_; } + bool get_reference() const { return reference_; } void set_reference(bool reference) { reference_ = reference; } diff --git a/compiler/cpp/src/thrift/parse/t_function.h b/compiler/cpp/src/thrift/parse/t_function.h index 22d26091a04..d30c8a46e62 100644 --- a/compiler/cpp/src/thrift/parse/t_function.h +++ b/compiler/cpp/src/thrift/parse/t_function.h @@ -37,7 +37,7 @@ class t_function : public t_doc { : returntype_(returntype), name_(name), arglist_(arglist), - xceptions_(new t_struct(NULL)), + xceptions_(new t_struct(nullptr)), own_xceptions_(true), oneway_(oneway) { if (oneway_ && (!returntype_->is_void())) { @@ -64,7 +64,7 @@ class t_function : public t_doc { } } - ~t_function() { + ~t_function() override { if (own_xceptions_) delete xceptions_; } diff --git a/compiler/cpp/src/thrift/parse/t_list.h b/compiler/cpp/src/thrift/parse/t_list.h index acf68653b2c..f0b896d0cb0 100644 --- a/compiler/cpp/src/thrift/parse/t_list.h +++ b/compiler/cpp/src/thrift/parse/t_list.h @@ -32,7 +32,7 @@ class t_list : public t_container { t_type* get_elem_type() const { return elem_type_; } - bool is_list() const { return true; } + bool is_list() const override { return true; } private: t_type* elem_type_; diff --git a/compiler/cpp/src/thrift/parse/t_map.h b/compiler/cpp/src/thrift/parse/t_map.h index dd3f089c197..9614e6849c6 100644 --- a/compiler/cpp/src/thrift/parse/t_map.h +++ b/compiler/cpp/src/thrift/parse/t_map.h @@ -35,7 +35,7 @@ class t_map : public t_container { t_type* get_val_type() const { return val_type_; } - bool is_map() const { return true; } + bool is_map() const override { return true; } private: t_type* key_type_; diff --git a/compiler/cpp/src/thrift/parse/t_program.h b/compiler/cpp/src/thrift/parse/t_program.h index 43dd45a6183..b6b1332c042 100644 --- a/compiler/cpp/src/thrift/parse/t_program.h +++ b/compiler/cpp/src/thrift/parse/t_program.h @@ -58,17 +58,17 @@ class t_program : public t_doc { public: t_program(std::string path, std::string name) - : path_(path), name_(name), out_path_("./"), out_path_is_absolute_(false), scope_(new t_scope) {} + : path_(path), name_(name), out_path_("./"), out_path_is_absolute_(false), scope_(new t_scope), recursive_(false) {} - t_program(std::string path) : path_(path), out_path_("./"), out_path_is_absolute_(false) { + t_program(std::string path) : path_(path), out_path_("./"), out_path_is_absolute_(false), recursive_(false) { name_ = program_name(path); scope_ = new t_scope(); } - ~t_program() { + ~t_program() override { if (scope_) { delete scope_; - scope_ = NULL; + scope_ = nullptr; } } @@ -112,9 +112,14 @@ class t_program : public t_doc { objects_.push_back(tx); xceptions_.push_back(tx); } - void add_service(t_service* ts) { services_.push_back(ts); } + void add_service(t_service* ts) { + ts->validate_unique_members(); + services_.push_back(ts); + } // Programs to include + std::vector& get_includes() { return includes_; } + const std::vector& get_includes() const { return includes_; } void set_out_path(std::string out_path, bool out_path_is_absolute) { @@ -133,9 +138,9 @@ class t_program : public t_doc { * @param t the type to test for collisions * @return true if a certain collision was found, otherwise false */ - bool is_unique_typename(t_type* t) { + bool is_unique_typename(const t_type* t) const { int occurrences = program_typename_count(this, t); - for (std::vector::iterator it = includes_.begin(); it != includes_.end(); ++it) { + for (auto it = includes_.cbegin(); it != includes_.cend(); ++it) { occurrences += program_typename_count(*it, t); } return 0 == occurrences; @@ -147,7 +152,7 @@ class t_program : public t_doc { * @param t the type to test for collisions * @return the number of certain typename collisions */ - int program_typename_count(t_program* prog, t_type* t) { + int program_typename_count(const t_program* prog, const t_type* t) const { int occurrences = 0; occurrences += collection_typename_count(prog, prog->typedefs_, t); occurrences += collection_typename_count(prog, prog->enums_, t); @@ -164,9 +169,9 @@ class t_program : public t_doc { * @return the number of certain typename collisions */ template - int collection_typename_count(t_program* prog, T type_collection, t_type* t) { + int collection_typename_count(const t_program* prog, const T type_collection, const t_type* t) const { int occurrences = 0; - for (typename T::iterator it = type_collection.begin(); it != type_collection.end(); ++it) + for (auto it = type_collection.cbegin(); it != type_collection.cend(); ++it) if (t != *it && 0 == t->get_name().compare((*it)->get_name()) && is_common_namespace(prog, t)) ++occurrences; return occurrences; @@ -184,7 +189,7 @@ class t_program : public t_doc { * @param t the type containing the typename match * @return true if a collision within namespaces is found, otherwise false */ - bool is_common_namespace(t_program* prog, t_type* t) { + bool is_common_namespace(const t_program* prog, const t_type* t) const { // Case 1: Typenames are in the same program [collision] if (prog == t->get_program()) { pwarning(1, @@ -196,8 +201,8 @@ class t_program : public t_doc { // Case 2: Both programs have identical namespace scope/name declarations [collision] bool match = true; - for (std::map::iterator it = prog->namespaces_.begin(); - it != prog->namespaces_.end(); + for (auto it = prog->namespaces_.cbegin(); + it != prog->namespaces_.cend(); ++it) { if (0 == it->second.compare(t->get_program()->get_namespace(it->first))) { pwarning(1, @@ -213,8 +218,8 @@ class t_program : public t_doc { match = false; } } - for (std::map::iterator it = t->get_program()->namespaces_.begin(); - it != t->get_program()->namespaces_.end(); + for (auto it = t->get_program()->namespaces_.cbegin(); + it != t->get_program()->namespaces_.cend(); ++it) { if (0 == it->second.compare(prog->get_namespace(it->first))) { pwarning(1, @@ -244,7 +249,9 @@ class t_program : public t_doc { void set_namespace(std::string name) { namespace_ = name; } // Scope accessor - t_scope* scope() const { return scope_; } + t_scope* scope() { return scope_; } + + const t_scope* scope() const { return scope_; } // Includes @@ -267,8 +274,6 @@ class t_program : public t_doc { includes_.push_back(program); } - std::vector& get_includes() { return includes_; } - void set_include_prefix(std::string include_prefix) { include_prefix_ = include_prefix; @@ -284,7 +289,6 @@ class t_program : public t_doc { if (language != "*") { size_t sub_index = language.find('.'); std::string base_language = language.substr(0, sub_index); - std::string sub_namespace; if (base_language == "smalltalk") { pwarning(1, "Namespace 'smalltalk' is deprecated. Use 'st' instead"); @@ -323,7 +327,7 @@ class t_program : public t_doc { return std::string(); } - const std::map& get_all_namespaces(){ + const std::map& get_all_namespaces() const { return namespaces_; } @@ -331,7 +335,16 @@ class t_program : public t_doc { namespace_annotations_[language] = annotations; } - const std::map& get_namespace_annotations(std::string language) { + const std::map& get_namespace_annotations(const std::string& language) const { + auto it = namespace_annotations_.find(language); + if (namespace_annotations_.end() != it) { + return it->second; + } + static const std::map emptyMap; + return emptyMap; + } + + std::map& get_namespace_annotations(const std::string& language) { return namespace_annotations_[language]; } @@ -339,11 +352,15 @@ class t_program : public t_doc { void add_cpp_include(std::string path) { cpp_includes_.push_back(path); } - const std::vector& get_cpp_includes() { return cpp_includes_; } + const std::vector& get_cpp_includes() const { return cpp_includes_; } void add_c_include(std::string path) { c_includes_.push_back(path); } - const std::vector& get_c_includes() { return c_includes_; } + const std::vector& get_c_includes() const { return c_includes_; } + + void set_recursive(const bool recursive) { recursive_ = recursive; } + + bool get_recursive() const { return recursive_; } private: // File path @@ -390,6 +407,9 @@ class t_program : public t_doc { // C extra includes std::vector c_includes_; + + // Recursive code generation + bool recursive_; }; #endif diff --git a/compiler/cpp/src/thrift/parse/t_scope.h b/compiler/cpp/src/thrift/parse/t_scope.h index 6f160a5cc8b..a12c4df5e1d 100644 --- a/compiler/cpp/src/thrift/parse/t_scope.h +++ b/compiler/cpp/src/thrift/parse/t_scope.h @@ -33,11 +33,6 @@ #include "thrift/parse/t_list.h" #include "thrift/parse/t_set.h" -namespace plugin_output { -template -void convert(From*, To&); -} - /** * This represents a variable scope used for looking up predefined types and * services. Typically, a scope is associated with a t_program. Scopes are not @@ -47,16 +42,34 @@ void convert(From*, To&); */ class t_scope { public: - t_scope() {} + t_scope() = default; void add_type(std::string name, t_type* type) { types_[name] = type; } t_type* get_type(std::string name) { return types_[name]; } + const t_type* get_type(std::string name) const { + const auto it = types_.find(name); + if (types_.end() != it) + { + return it->second; + } + return nullptr; + } + void add_service(std::string name, t_service* service) { services_[name] = service; } t_service* get_service(std::string name) { return services_[name]; } + const t_service* get_service(std::string name) const { + const auto it = services_.find(name); + if (services_.end() != it) + { + return it->second; + } + return nullptr; + } + void add_constant(std::string name, t_const* constant) { if (constants_.find(name) != constants_.end()) { throw "Enum " + name + " is already defined!"; @@ -67,6 +80,15 @@ class t_scope { t_const* get_constant(std::string name) { return constants_[name]; } + const t_const* get_constant(std::string name) const { + const auto it = constants_.find(name); + if (constants_.end() != it) + { + return it->second; + } + return nullptr; + } + void print() { std::map::iterator iter; for (iter = types_.begin(); iter != types_.end(); ++iter) { @@ -95,12 +117,12 @@ class t_scope { resolve_const_value((*v_iter), ((t_set*)ttype)->get_elem_type()); } } else if (ttype->is_struct()) { - t_struct* tstruct = (t_struct*)ttype; + auto* tstruct = (t_struct*)ttype; const std::map& map = const_val->get_map(); std::map::const_iterator v_iter; for (v_iter = map.begin(); v_iter != map.end(); ++v_iter) { t_field* field = tstruct->get_field_by_name(v_iter->first->get_string()); - if (field == NULL) { + if (field == nullptr) { throw "No field named \"" + v_iter->first->get_string() + "\" was found in struct of type \"" + tstruct->get_name() + "\""; } @@ -111,7 +133,7 @@ class t_scope { const_val->set_enum((t_enum*)ttype); } else { t_const* constant = get_constant(const_val->get_identifier()); - if (constant == NULL) { + if (constant == nullptr) { throw "No enum value or constant found named \"" + const_val->get_identifier() + "\"!"; } @@ -157,9 +179,9 @@ class t_scope { } else if (ttype->is_enum()) { // enum constant with non-identifier value. set the enum and find the // value's name. - t_enum* tenum = (t_enum*)ttype; + auto* tenum = (t_enum*)ttype; t_enum_value* enum_value = tenum->get_constant_by_value(const_val->get_integer()); - if (enum_value == NULL) { + if (enum_value == nullptr) { std::ostringstream valstm; valstm << const_val->get_integer(); throw "Couldn't find a named value in enum " + tenum->get_name() + " for value " @@ -179,10 +201,6 @@ class t_scope { // Map of names to services std::map services_; - - // to list map entries - template - friend void plugin_output::convert(From*, To&); }; #endif diff --git a/compiler/cpp/src/thrift/parse/t_service.h b/compiler/cpp/src/thrift/parse/t_service.h index e2204caee9c..f405c159e8e 100644 --- a/compiler/cpp/src/thrift/parse/t_service.h +++ b/compiler/cpp/src/thrift/parse/t_service.h @@ -31,20 +31,63 @@ class t_program; */ class t_service : public t_type { public: - t_service(t_program* program) : t_type(program), extends_(NULL) {} + t_service(t_program* program) : t_type(program), extends_(nullptr) {} - bool is_service() const { return true; } + bool is_service() const override { return true; } void set_extends(t_service* extends) { extends_ = extends; } void add_function(t_function* func) { + if (get_function_by_name(func->get_name()) != NULL) { + throw "Function " + func->get_name() + " is already defined"; + } + functions_.push_back(func); + } + + void validate_unique_members() { std::vector::const_iterator iter; for (iter = functions_.begin(); iter != functions_.end(); ++iter) { - if (func->get_name() == (*iter)->get_name()) { - throw "Function " + func->get_name() + " is already defined"; + // throw exception when there is a conflict of names with super class + if (extends_ != NULL) { + if (extends_->get_function_by_name((*iter)->get_name()) != NULL) { + throw "Function " + (*iter)->get_name() + " is already defined in service " + name_; + } } } - functions_.push_back(func); + } + + t_function* get_function_by_name(std::string func_name) { + if (extends_ != NULL) { + t_function* func = NULL; + if ((func = extends_->get_function_by_name(func_name)) != NULL) { + return func; + } + } + + std::vector::const_iterator iter; + for (iter = functions_.begin(); iter != functions_.end(); ++iter) { + if ((*iter)->get_name() == func_name) { + return *iter; + } + } + return NULL; + } + + const t_function* get_function_by_name(std::string func_name) const { + if (extends_ != NULL) { + t_function* func = NULL; + if ((func = extends_->get_function_by_name(func_name)) != NULL) { + return func; + } + } + + std::vector::const_iterator iter; + for (iter = functions_.begin(); iter != functions_.end(); ++iter) { + if ((*iter)->get_name() == func_name) { + return *iter; + } + } + return NULL; } const std::vector& get_functions() const { return functions_; } diff --git a/compiler/cpp/src/thrift/parse/t_set.h b/compiler/cpp/src/thrift/parse/t_set.h index f913be4fa3c..c0d4a35c190 100644 --- a/compiler/cpp/src/thrift/parse/t_set.h +++ b/compiler/cpp/src/thrift/parse/t_set.h @@ -30,9 +30,11 @@ class t_set : public t_container { public: t_set(t_type* elem_type) : elem_type_(elem_type) {} - t_type* get_elem_type() const { return elem_type_; } + const t_type* get_elem_type() const { return elem_type_; } - bool is_set() const { return true; } + t_type* get_elem_type() { return elem_type_; } + + bool is_set() const override { return true; } private: t_type* elem_type_; diff --git a/compiler/cpp/src/thrift/parse/t_struct.h b/compiler/cpp/src/thrift/parse/t_struct.h index 4102da7be7f..7e1e6caf012 100644 --- a/compiler/cpp/src/thrift/parse/t_struct.h +++ b/compiler/cpp/src/thrift/parse/t_struct.h @@ -56,7 +56,7 @@ class t_struct : public t_type { members_with_value(0), xsd_all_(false) {} - void set_name(const std::string& name) { + void set_name(const std::string& name) override { name_ = name; validate_union_members(); } @@ -80,7 +80,7 @@ class t_struct : public t_type { } // unions may have up to one member defaulted, but not more - if (field->get_value() != NULL) { + if (field->get_value() != nullptr) { if (1 < ++members_with_value) { throw "Error: Field " + field->get_name() + " provides another default value for union " + name_; @@ -118,7 +118,7 @@ class t_struct : public t_type { return false; } // returns false when there is a conflict of field names - if (get_field_by_name(elem->get_name()) != NULL) { + if (get_field_by_name(elem->get_name()) != nullptr) { return false; } members_.push_back(elem); @@ -129,22 +129,16 @@ class t_struct : public t_type { const members_type& get_members() const { return members_; } - const members_type& get_sorted_members() { return members_in_id_order_; } + const members_type& get_sorted_members() const { return members_in_id_order_; } - bool is_struct() const { return !is_xception_; } + bool is_struct() const override { return !is_xception_; } - bool is_xception() const { return is_xception_; } + bool is_xception() const override { return is_xception_; } bool is_union() const { return is_union_; } t_field* get_field_by_name(std::string field_name) { - members_type::const_iterator m_iter; - for (m_iter = members_in_id_order_.begin(); m_iter != members_in_id_order_.end(); ++m_iter) { - if ((*m_iter)->get_name() == field_name) { - return *m_iter; - } - } - return NULL; + return const_cast(const_cast(*this).get_field_by_name(field_name)); } const t_field* get_field_by_name(std::string field_name) const { @@ -154,7 +148,7 @@ class t_struct : public t_type { return *m_iter; } } - return NULL; + return nullptr; } private: diff --git a/compiler/cpp/src/thrift/parse/t_type.h b/compiler/cpp/src/thrift/parse/t_type.h index 3a6d1e04484..63f99ed8774 100644 --- a/compiler/cpp/src/thrift/parse/t_type.h +++ b/compiler/cpp/src/thrift/parse/t_type.h @@ -38,7 +38,7 @@ class t_program; */ class t_type : public t_doc { public: - virtual ~t_type() {} + ~t_type() override = default; virtual void set_name(const std::string& name) { name_ = name; } @@ -85,13 +85,13 @@ class t_type : public t_doc { std::map annotations_; protected: - t_type() : program_(NULL) { ; } + t_type() : program_(nullptr) { ; } t_type(t_program* program) : program_(program) { ; } t_type(t_program* program, std::string name) : program_(program), name_(name) { ; } - t_type(std::string name) : program_(NULL), name_(name) { ; } + t_type(std::string name) : program_(nullptr), name_(name) { ; } t_program* program_; std::string name_; diff --git a/compiler/cpp/src/thrift/parse/t_typedef.cc b/compiler/cpp/src/thrift/parse/t_typedef.cc index 99ffdb8bdc6..48e861e8dce 100644 --- a/compiler/cpp/src/thrift/parse/t_typedef.cc +++ b/compiler/cpp/src/thrift/parse/t_typedef.cc @@ -21,10 +21,14 @@ #include "thrift/parse/t_typedef.h" #include "thrift/parse/t_program.h" -t_type* t_typedef::get_type() const { - if (type_ == NULL) { - t_type* type = get_program()->scope()->get_type(symbolic_); - if (type == NULL) { +t_type* t_typedef::get_type() { + return const_cast(const_cast(this)->get_type()); +} + +const t_type* t_typedef::get_type() const { + if (type_ == nullptr) { + const t_type* type = get_program()->scope()->get_type(symbolic_); + if (type == nullptr) { printf("Type \"%s\" not defined\n", symbolic_.c_str()); exit(1); } diff --git a/compiler/cpp/src/thrift/parse/t_typedef.h b/compiler/cpp/src/thrift/parse/t_typedef.h index aad3a50e8e9..d21d863a928 100644 --- a/compiler/cpp/src/thrift/parse/t_typedef.h +++ b/compiler/cpp/src/thrift/parse/t_typedef.h @@ -42,20 +42,22 @@ class t_typedef : public t_type { */ t_typedef(t_program* program, const std::string& symbolic, bool forward) : t_type(program, symbolic), - type_(NULL), + type_(nullptr), symbolic_(symbolic), forward_(forward) {} - ~t_typedef() {} + ~t_typedef() override = default; - t_type* get_type() const; + t_type* get_type(); + + const t_type* get_type() const; const std::string& get_symbolic() const { return symbolic_; } bool is_forward_typedef() const { return forward_; } - bool is_typedef() const { return true; } + bool is_typedef() const override { return true; } private: t_type* type_; diff --git a/compiler/cpp/src/thrift/plugin/plugin.cc b/compiler/cpp/src/thrift/plugin/plugin.cc deleted file mode 100644 index ca5d28771f1..00000000000 --- a/compiler/cpp/src/thrift/plugin/plugin.cc +++ /dev/null @@ -1,510 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -#include "thrift/plugin/plugin.h" - -#ifdef _WIN32 -#include -#include -#endif - -#include -#include - -#include -#include - -#include "thrift/generate/t_generator.h" -#include "thrift/plugin/type_util.h" -#include "thrift/protocol/TBinaryProtocol.h" -#include "thrift/transport/TBufferTransports.h" -#include "thrift/transport/TFDTransport.h" -#include "thrift/stdcxx.h" -#include "thrift/plugin/plugin_types.h" - -namespace apache { -namespace thrift { -namespace plugin { - -using apache::thrift::protocol::TBinaryProtocol; -using apache::thrift::transport::TFDTransport; -using apache::thrift::transport::TFramedTransport; - -#define THRIFT_CONVERT_FORWARD(from_type) \ - template <> \ - typename ToType::type* convert_forward(const from_type& from) - -#define THRIFT_CONVERT_COMPLETE_DECL(from_type) \ - template <> \ - void convert(const from_type& from, ToType::type* to) - -#define THRIFT_CONVERT_UNARY_DECL(from_type) \ - template <> \ - typename ToType::type* convert(const from_type& from) - -#define THRIFT_CONVERSION_DECL(from_type) \ - THRIFT_CONVERT_FORWARD(from_type); \ - THRIFT_CONVERT_COMPLETE_DECL(from_type); \ - THRIFT_CONVERT_UNARY_DECL(from_type) - -#define THRIFT_CONVERT_COMPLETE(from_type) \ - THRIFT_CONVERSION_DECL(from_type) { \ - ToType::type* to = convert_forward(from); \ - convert(from, to); \ - return to; \ - } \ - THRIFT_CONVERT_COMPLETE_DECL(from_type) - -#define THRIFT_CONVERSION(from_type, ...) \ - THRIFT_CONVERT_FORWARD(from_type) { \ - (void)from; \ - return new ToType::type(__VA_ARGS__); \ - } \ - THRIFT_CONVERT_COMPLETE(from_type) - -#define THRIFT_ASSIGN_DOC() \ - do { \ - if (from.__isset.doc) \ - to->set_doc(from.doc); \ - } while (0) - -#define THRIFT_ASSIGN_ANNOTATIONS() \ - THRIFT_ASSIGN_DOC(); \ - do { \ - if (from.__isset.annotations) \ - to->annotations_ = from.annotations; \ - } while (0) - -#define THRIFT_ASSIGN_METADATA() \ - do { \ - to->set_name(from.metadata.name); \ - if (from.metadata.__isset.doc) \ - to->set_doc(from.metadata.doc); \ - if (from.metadata.__isset.annotations) \ - to->annotations_ = from.metadata.annotations; \ - } while (0) - -::t_program* g_program = 0; - -template -struct TypeCache { - C* operator[](const int64_t& k) { - typename std::map::iterator it = cache.find(k); - if (it != cache.end()) { - return it->second; - } else { - typename std::map::const_iterator cit = source->find(k); - if (cit == source->end()) { - throw ThriftPluginError("Type not found"); - } - return (cache)[k] = convert_forward(cit->second); - } - } - - void compileAll() { - boost::for_each(*source | boost::adaptors::map_keys, - stdcxx::bind(&TypeCache::compile, this, stdcxx::placeholders::_1)); - } - - std::map const* source; - - void clear() { - source = nullptr ; - cache.clear() ; - } - -protected: - std::map cache; - -private: - void compile(const int64_t& k) { - typename std::map::const_iterator cit = source->find(k); - if (cit == source->end()) { - throw ThriftPluginError("Type not found "); - } - convert(cit->second, (*this)[k]); - } -}; -std::map g_program_cache; -TypeCache< ::t_type, t_type> g_type_cache; -TypeCache< ::t_const, t_const> g_const_cache; -TypeCache< ::t_service, t_service> g_service_cache; - -void clear_global_cache() { - g_type_cache.clear(); - g_const_cache.clear(); - g_service_cache.clear(); -} - -void set_global_cache(const TypeRegistry& from) { - g_type_cache.source = &from.types; - g_const_cache.source = &from.constants; - g_service_cache.source = &from.services; - - g_type_cache.compileAll(); - g_const_cache.compileAll(); - g_service_cache.compileAll(); -} - -template -T* resolve_type(int64_t name) { - return reinterpret_cast(g_type_cache[name]); -} - -::t_const* resolve_const(int64_t name) { - return g_const_cache[name]; -} - -::t_service* resolve_service(int64_t name) { - return g_service_cache[name]; -} - -THRIFT_CONVERT_FORWARD(t_base_type) { -#define T_BASETYPE_CASE(type) \ - case t_base::TYPE_##type: \ - t = ::t_base_type::TYPE_##type; \ - break - - ::t_base_type::t_base t = ::t_base_type::TYPE_VOID; - bool is_binary = false; - switch (from.value) { - T_BASETYPE_CASE(VOID); - T_BASETYPE_CASE(STRING); - T_BASETYPE_CASE(BOOL); - T_BASETYPE_CASE(I8); - T_BASETYPE_CASE(I16); - T_BASETYPE_CASE(I32); - T_BASETYPE_CASE(I64); - T_BASETYPE_CASE(DOUBLE); - case t_base::TYPE_BINARY: - t = ::t_base_type::TYPE_STRING; - is_binary = true; - break; - } - ::t_base_type* to = new ::t_base_type(from.metadata.name, t); - to->set_binary(is_binary); - return to; -#undef T_BASETYPE_CASE -} -THRIFT_CONVERT_COMPLETE(t_base_type) { - THRIFT_ASSIGN_METADATA(); -} - -THRIFT_CONVERT_FORWARD(t_typedef) { - ::t_typedef* to; - if (from.forward) { - to = new ::t_typedef(g_program_cache[from.metadata.program_id], from.symbolic, true); - } else { - to = new ::t_typedef(g_program_cache[from.metadata.program_id], - resolve_type< ::t_type>(from.type), from.symbolic); - } - return to; -} -THRIFT_CONVERT_COMPLETE(t_typedef) { - THRIFT_ASSIGN_METADATA(); -} -THRIFT_CONVERSION(t_enum_value, from.name, from.value) { - assert(to); - THRIFT_ASSIGN_ANNOTATIONS(); -} -THRIFT_CONVERSION(t_enum, g_program_cache[from.metadata.program_id]) { - assert(to); - THRIFT_ASSIGN_METADATA(); - boost::for_each(from.constants | boost::adaptors::transformed(convert), - stdcxx::bind(&::t_enum::append, to, stdcxx::placeholders::_1)); -} -THRIFT_CONVERSION(t_list, resolve_type< ::t_type>(from.elem_type)) { - assert(to); - THRIFT_ASSIGN_METADATA(); - if (from.__isset.cpp_name) - to->set_cpp_name(from.cpp_name); -} -THRIFT_CONVERSION(t_set, resolve_type< ::t_type>(from.elem_type)) { - assert(to); - THRIFT_ASSIGN_METADATA(); - if (from.__isset.cpp_name) - to->set_cpp_name(from.cpp_name); -} -THRIFT_CONVERSION(t_map, - resolve_type< ::t_type>(from.key_type), - resolve_type< ::t_type>(from.val_type)) { - assert(to); - THRIFT_ASSIGN_METADATA(); - if (from.__isset.cpp_name) - to->set_cpp_name(from.cpp_name); -} -THRIFT_CONVERSION(t_const_value, ) { -#define T_CONST_VALUE_CASE(type) \ - if (from.__isset.type##_val) \ - to->set_##type(from.type##_val) - - assert(to); - if (from.__isset.map_val) { - to->set_map(); - for (std::map::const_iterator it = from.map_val.begin(); - it != from.map_val.end(); it++) { - to->add_map(convert(it->first), convert(it->second)); - } - } else if (from.__isset.list_val) { - to->set_list(); - boost::for_each(from.list_val | boost::adaptors::transformed(&convert), - stdcxx::bind(&::t_const_value::add_list, to, stdcxx::placeholders::_1)); - } else - T_CONST_VALUE_CASE(string); - else T_CONST_VALUE_CASE(integer); - else T_CONST_VALUE_CASE(double); - else if (from.__isset.const_identifier_val) { - to->set_identifier(from.const_identifier_val.identifier_val) ; - to->set_enum(resolve_type< ::t_enum>(from.const_identifier_val.enum_val)) ; - } - -#undef T_CONST_VALUE_CASE -} -THRIFT_CONVERSION(t_field, resolve_type< ::t_type>(from.type), from.name, from.key) { - assert(to); - THRIFT_ASSIGN_ANNOTATIONS(); - to->set_reference(from.reference); - to->set_req(static_cast< ::t_field::e_req>(from.req)); - if (from.__isset.value) { - to->set_value(convert(from.value)); - } -} -THRIFT_CONVERSION(t_struct, g_program_cache[from.metadata.program_id]) { - assert(to); - THRIFT_ASSIGN_METADATA(); - to->set_union(from.is_union); - to->set_xception(from.is_xception); - boost::for_each(from.members | boost::adaptors::transformed(convert), - stdcxx::bind(&::t_struct::append, to, stdcxx::placeholders::_1)); -} -THRIFT_CONVERSION(t_const, - resolve_type< ::t_type>(from.type), - from.name, - convert(from.value)) { - assert(to); - THRIFT_ASSIGN_DOC(); -} - -THRIFT_CONVERSION(t_function, - resolve_type< ::t_type>(from.returntype), - from.name, - resolve_type< ::t_struct>(from.arglist), - resolve_type< ::t_struct>(from.xceptions), - from.is_oneway) { - assert(to); - THRIFT_ASSIGN_DOC(); -} - -THRIFT_CONVERSION(t_service, g_program_cache[from.metadata.program_id]) { - assert(to); - assert(from.metadata.program_id); - assert(g_program_cache[from.metadata.program_id]); - THRIFT_ASSIGN_METADATA(); - - boost::for_each(from.functions | boost::adaptors::transformed(convert), - stdcxx::bind(&::t_service::add_function, to, stdcxx::placeholders::_1)); - - if (from.__isset.extends_) - to->set_extends(resolve_service(from.extends_)); -} - -THRIFT_CONVERT_FORWARD(t_type) { -#define T_TYPE_CASE_FW_T(case, type) \ - if (from.__isset.case##_val) \ - return convert_forward(from.case##_val) -#define T_TYPE_CASE_FW(case) T_TYPE_CASE_FW_T(case, t_##case) - - T_TYPE_CASE_FW(base_type); - T_TYPE_CASE_FW(typedef); - T_TYPE_CASE_FW(enum); - T_TYPE_CASE_FW(struct); - T_TYPE_CASE_FW_T(xception, t_struct); - T_TYPE_CASE_FW(list); - T_TYPE_CASE_FW(set); - T_TYPE_CASE_FW(map); - T_TYPE_CASE_FW(service); - throw ThriftPluginError("Invalid data: Type union has no value."); -#undef T_TYPE_CASE_FW_T -#undef T_TYPE_CASE_FW -} -THRIFT_CONVERT_COMPLETE(t_type) { -#define T_TYPE_CASE_T(case, type) \ - else if (from.__isset.case##_val) \ - convert(from.case##_val, reinterpret_cast< ::type*>(to)) -#define T_TYPE_CASE(case) T_TYPE_CASE_T(case, t_##case) - - if (false) { - } - T_TYPE_CASE(base_type); - T_TYPE_CASE(typedef); - T_TYPE_CASE(enum); - T_TYPE_CASE(struct); - T_TYPE_CASE_T(xception, t_struct); - T_TYPE_CASE(list); - T_TYPE_CASE(set); - T_TYPE_CASE(map); - T_TYPE_CASE(service); - else { - throw ThriftPluginError("Invalid data: Type union has no value."); - } -#undef T_TYPE_CASE_T -#undef T_TYPE_CASE -} - -THRIFT_CONVERSION(t_scope, ) { - assert(to); -#define T_SCOPE_RESOLVE(type, name, a) \ - for (std::vector::const_iterator it = from.name##s.begin(); it != from.name##s.end(); \ - it++) { \ - ::t_##type* t = resolve_##type a(*it); \ - to->add_##name(t->get_name(), t); \ - } - T_SCOPE_RESOLVE(type, type, < ::t_type>); - T_SCOPE_RESOLVE(const, constant, ); - T_SCOPE_RESOLVE(service, service, ); -#undef T_SCOPE_RESOLVE -} - -THRIFT_CONVERT_FORWARD(t_program) { - ::t_program* to = new ::t_program(from.path, from.name); - for (std::vector::const_iterator it = from.includes.begin(); it != from.includes.end(); - it++) { - to->add_include(convert_forward(*it)); - } - g_program_cache[from.program_id] = to; - return to; -} -THRIFT_CONVERT_COMPLETE(t_program) { - assert(to); - g_program = to; - convert(from.scope, to->scope()); - THRIFT_ASSIGN_DOC(); - - to->set_out_path(from.out_path, from.out_path_is_absolute); - - boost::for_each(from.typedefs | boost::adaptors::transformed(&resolve_type< ::t_typedef>), - stdcxx::bind(&::t_program::add_typedef, to, stdcxx::placeholders::_1)); - boost::for_each(from.enums | boost::adaptors::transformed(&resolve_type< ::t_enum>), - stdcxx::bind(&::t_program::add_enum, to, stdcxx::placeholders::_1)); - for (std::vector::const_iterator it = from.objects.begin(); it != from.objects.end(); - it++) { - ::t_struct* t2 = resolve_type< ::t_struct>(*it); - if (t2->is_xception()) { - to->add_xception(t2); - } else { - to->add_struct(t2); - } - } - boost::for_each(from.consts | boost::adaptors::transformed(&resolve_const), - stdcxx::bind(&::t_program::add_const, to, stdcxx::placeholders::_1)); - boost::for_each(from.services | boost::adaptors::transformed(&resolve_service), - stdcxx::bind(&::t_program::add_service, to, stdcxx::placeholders::_1)); - - for (std::vector::const_iterator it = from.includes.begin(); it != from.includes.end(); - it++) { - convert(*it, g_program_cache[it->program_id]); - } - std::for_each(from.c_includes.begin(), from.c_includes.end(), - stdcxx::bind(&::t_program::add_c_include, to, stdcxx::placeholders::_1)); - std::for_each(from.cpp_includes.begin(), from.cpp_includes.end(), - stdcxx::bind(&::t_program::add_cpp_include, to, stdcxx::placeholders::_1)); - for (std::map::const_iterator it = from.namespaces.begin(); - it != from.namespaces.end(); it++) { - to->set_namespace(it->first, it->second); - } - - to->set_include_prefix(from.include_prefix); - to->set_namespace(from.namespace_); -} - -int GeneratorPlugin::exec(int, char* []) { -#ifdef _WIN32 - _setmode(fileno(stdin), _O_BINARY); -#endif - stdcxx::shared_ptr transport( - new TFramedTransport(stdcxx::make_shared(fileno(stdin)))); - TBinaryProtocol proto(transport); - GeneratorInput input; - try { - input.read(&proto); - } catch (std::exception& err) { - std::cerr << "Error while receiving plugin data: " << err.what() << std::endl; - return -1; - } - initGlobals(); - ::t_program* p = g_program = convert_forward(input.program); - set_global_cache(input.type_registry); - convert(input.program, p); - - int ret = generate(p, input.parsed_options); - clearGlobals(); - - return ret; -} - -::t_const_value::t_const_value_type const_value_case(const t_const_value& v) { - if (v.__isset.map_val) - return ::t_const_value::CV_MAP; - if (v.__isset.list_val) - return ::t_const_value::CV_LIST; - if (v.__isset.string_val) - return ::t_const_value::CV_STRING; - if (v.__isset.integer_val) - return ::t_const_value::CV_INTEGER; - if (v.__isset.double_val) - return ::t_const_value::CV_DOUBLE; - if (v.__isset.const_identifier_val) - return ::t_const_value::CV_IDENTIFIER; - throw ThriftPluginError("Unknown const value type"); -} - -bool t_const_value::operator<(const t_const_value& that) const { - ::t_const_value::t_const_value_type t1 = const_value_case(*this); - ::t_const_value::t_const_value_type t2 = const_value_case(that); - if (t1 != t2) - return t1 < t2; - switch (t1) { - case ::t_const_value::CV_INTEGER: - return integer_val < that.integer_val; - case ::t_const_value::CV_DOUBLE: - return double_val < that.double_val; - case ::t_const_value::CV_STRING: - return string_val < that.string_val; - case ::t_const_value::CV_MAP: - if (that.map_val.empty()) - return false; - else if (map_val.empty()) - return true; - else - return map_val.begin()->first < that.map_val.begin()->first; - case ::t_const_value::CV_LIST: - if (that.list_val.empty()) - return false; - else if (list_val.empty()) - return true; - else - return list_val.front() < that.list_val.front(); - case ::t_const_value::CV_IDENTIFIER: - return integer_val < that.integer_val; - } - throw ThriftPluginError("Unknown const value type"); -} -} -} -} diff --git a/compiler/cpp/src/thrift/plugin/plugin.thrift b/compiler/cpp/src/thrift/plugin/plugin.thrift deleted file mode 100644 index 6d98f99558c..00000000000 --- a/compiler/cpp/src/thrift/plugin/plugin.thrift +++ /dev/null @@ -1,207 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -namespace as3 org.apache.thrift.plugin -namespace cpp apache.thrift.plugin -namespace csharp Thrift.Plugin -namespace d thrift.plugin -namespace delphi Thrift.Plugin -namespace erl thrift.plugin -namespace go thrift -namespace haxe org.apache.thrift.plugin -namespace hs Thrift.Plugin -namespace java org.apache.thrift.plugin -namespace ocaml Thrift -namespace perl Thrift.Plugin -namespace php thrift.plugin -namespace py thrift.plugin -namespace rb Thrift - -typedef i64 t_program_id -typedef i64 t_type_id -typedef i64 t_const_id -typedef i64 t_service_id - -enum t_base { - TYPE_VOID - TYPE_STRING - TYPE_BOOL - TYPE_I8 - TYPE_I16 - TYPE_I32 - TYPE_I64 - TYPE_DOUBLE - TYPE_BINARY -} - -struct TypeMetadata { - 1: required string name - 2: required t_program_id program_id - 99: optional map annotations - 100: optional string doc -} - -struct t_base_type { - 1: required TypeMetadata metadata - 2: required t_base value -} - -struct t_list { - 1: required TypeMetadata metadata - 2: optional string cpp_name - 3: required t_type_id elem_type -} - -struct t_set { - 1: required TypeMetadata metadata - 2: optional string cpp_name - 3: required t_type_id elem_type -} - -struct t_map { - 1: required TypeMetadata metadata - 2: optional string cpp_name - 3: required t_type_id key_type - 4: required t_type_id val_type -} - -struct t_typedef { - 1: required TypeMetadata metadata - 2: required t_type_id type - 3: required string symbolic - 4: required bool forward -} - -struct t_enum_value { - 1: required string name - 2: required i32 value - 99: optional map annotations - 100: optional string doc -} -struct t_enum { - 1: required TypeMetadata metadata - 2: required list constants -} - -enum Requiredness { - T_REQUIRED = 0 - T_OPTIONAL = 1 - T_OPT_IN_REQ_OUT = 2 -} - -struct t_const_identifier_value { - 1: required string identifier_val - 2: required t_type_id enum_val -} - -union t_const_value { - 1: optional map map_val - 2: optional list list_val - 3: optional string string_val - 4: optional i64 integer_val - 5: optional double double_val - 8: optional t_const_identifier_value const_identifier_val -} - -struct t_const { - 1: required string name - 2: required t_type_id type - 3: required t_const_value value - 100: optional string doc -} -struct t_field { - 1: required string name - 2: required t_type_id type - 3: required i32 key - 4: required Requiredness req - 5: optional t_const_value value - 10: required bool reference - 99: optional map annotations - 100: optional string doc -} -struct t_struct { - 1: required TypeMetadata metadata - 2: required list members - 3: required bool is_union - 4: required bool is_xception -} -struct t_function { - 1: required string name - 2: required t_type_id returntype - 3: required t_type_id arglist - 4: required t_type_id xceptions - 5: required bool is_oneway - 100: optional string doc -} -struct t_service { - 1: required TypeMetadata metadata - 2: required list functions - 3: optional t_service_id extends_ -} -union t_type { - 1: optional t_base_type base_type_val - 2: optional t_typedef typedef_val - 3: optional t_enum enum_val - 4: optional t_struct struct_val - 5: optional t_struct xception_val - 6: optional t_list list_val - 7: optional t_set set_val - 8: optional t_map map_val - 9: optional t_service service_val -} -struct t_scope { - 1: required list types - 2: required list constants - 3: required list services -} - -struct TypeRegistry { - 1: required map types - 2: required map constants - 3: required map services -} - -struct t_program { - 1: required string name - 2: required t_program_id program_id - 3: required string path - 4: required string namespace_ - 5: required string out_path - 6: required bool out_path_is_absolute - 8: required list includes - 9: required string include_prefix - 10: required t_scope scope - - 11: required list typedefs - 12: required list enums - 13: required list consts - 14: required list objects - 15: required list services - - 16: required map namespaces - 17: required list cpp_includes - 18: required list c_includes - 100: optional string doc -} - -struct GeneratorInput { - 1: required t_program program - 2: required TypeRegistry type_registry - 3: required map parsed_options -} diff --git a/compiler/cpp/src/thrift/plugin/plugin_output.cc b/compiler/cpp/src/thrift/plugin/plugin_output.cc deleted file mode 100644 index 81b9a2aa688..00000000000 --- a/compiler/cpp/src/thrift/plugin/plugin_output.cc +++ /dev/null @@ -1,454 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -#ifdef _WIN32 -#include -#include -#include -#include -#define THRIFT_POPEN(cmd) _popen(cmd, "wb") -#define THRIFT_PCLOSE _pclose -#else -#define THRIFT_POPEN(cmd) popen(cmd, "w") -#define THRIFT_PCLOSE pclose -#endif - -#include "thrift/plugin/plugin_output.h" - -#include -#include -#include - -#include "thrift/generate/t_generator.h" -#include "thrift/plugin/plugin.h" -#include "thrift/plugin/type_util.h" -#include "thrift/protocol/TBinaryProtocol.h" -#include "thrift/stdcxx.h" -#include "thrift/transport/TBufferTransports.h" -#include "thrift/transport/TFDTransport.h" - -#include "thrift/plugin/plugin_types.h" - -namespace plugin_output { - -template -typename apache::thrift::plugin::ToType::type convert(From* from) { - typename apache::thrift::plugin::ToType::type to; - convert(from, to); - return to; -} - -using apache::thrift::protocol::TBinaryProtocol; -using apache::thrift::stdcxx::make_shared; -using apache::thrift::stdcxx::shared_ptr; -using apache::thrift::transport::TFDTransport; -using apache::thrift::transport::TFramedTransport; - -using namespace apache::thrift; - -#define THRIFT_CONVERSION_N(from_type, to_type) \ - template <> \ - void convert(from_type * from, to_type & to) -#define THRIFT_CONVERSION(type) THRIFT_CONVERSION_N(::type, plugin::type) - -#define THRIFT_ASSIGN_N(from_name, to_name, prefix) \ - do { \ - if (from) \ - to.__set_##to_name(prefix(from->from_name)); \ - } while (0) - -#define THRIFT_ASSIGN(name) THRIFT_ASSIGN_N(get_##name(), name, ) -#define THRIFT_ASSIGN_CONVERT(type, from_name, to_name) \ - do { \ - if (from && from->from_name) { \ - to.__set_##to_name(convert(from->from_name)); \ - } \ - } while (0) - -#define THRIFT_ASSIGN_OPT(name) \ - do { \ - if (from->has_##name()) \ - THRIFT_ASSIGN(name); \ - } while (0) - -#define THRIFT_ASSIGN_LIST_N(type, from_name, to_name) \ - do { \ - if (from && !from->from_name.empty()) { \ - std::transform(from->from_name.begin(), \ - from->from_name.end(), \ - std::back_inserter(to.to_name), \ - convert< ::type>); \ - } \ - } while (0) - -#define THRIFT_ASSIGN_METADATA() convert(reinterpret_cast(from), to.metadata) - -// a generator of sequential unique identifiers for addresses -- so -// that the TypeCache below can use those IDs instead of -// addresses. This allows GeneratorInput's various -// t_{program,type,etc}_id types to be dense consecutively-numbered -// integers, instead of large random-seeming integers. -// -// Furthermore, this allows GeneratorInput to be deterministic (no -// addresses, so no pseudo-randomness) and that means reproducibility -// of output. -const int64_t ONE_MILLION = 1000 * 1000; -class id_generator { -public: - id_generator() : addr2id_(), next_id_(ONE_MILLION) {} - - void clear() { - addr2id_.clear() ; - next_id_ = ONE_MILLION ; - } - - int64_t gensym(const int64_t addr) { - if (!addr) return 0L ; - std::map::iterator it = addr2id_.find(addr); - if (it != addr2id_.end()) return it->second ; - int64_t id = next_id_++ ; - addr2id_.insert(std::make_pair(addr, id)) ; - return id ; - } - - std::map addr2id_ ; - int64_t next_id_ ; -} ; - -// To avoid multiple instances of same type, t_type, t_const and t_service are stored in one place -// and referenced by ID. -template -struct TypeCache { - typedef typename plugin::ToType::type to_type; - id_generator idgen ; - std::map cache; - - template - int64_t store(T2* t) { - intptr_t addr = reinterpret_cast(t); - if (!addr) return 0L ; - - int64_t id = idgen.gensym(addr) ; - if (cache.end() != cache.find(id)) return id ; - - // HACK: fake resolve for recursive type - cache.insert(std::make_pair(id, to_type())); - // overwrite with true value - cache[id] = convert(t); - return id ; - } - - void clear() { cache.clear() ; idgen.clear(); } -}; - -template -int64_t store_type(T* t); - -#define T_STORE(type) \ - TypeCache type##_cache; \ - template <> \ - plugin::t_##type##_id store_type(t_##type * t) { \ - return type##_cache.store(t); \ - } -T_STORE(type) -T_STORE(const) -T_STORE(service) -#undef T_STORE -// this id_generator is for gensymm-ing t_program_id -id_generator program_cache ; - -#define THRIFT_ASSIGN_ID_N(t, from_name, to_name) \ - do { \ - if (from && from->from_name) \ - to.__set_##to_name(store_type(from->from_name)); \ - } while (0) - -#define THRIFT_ASSIGN_ID(name) THRIFT_ASSIGN_ID_N(t_type, get_##name(), name) - -#define THRIFT_ASSIGN_LIST_ID(t, name) \ - do { \ - if (from && !from->get_##name##s().empty()) { \ - std::transform(from->get_##name##s().begin(), \ - from->get_##name##s().end(), \ - std::back_inserter(to.name##s), \ - &store_type); \ - } \ - } while (0) - -THRIFT_CONVERSION_N(::t_type, plugin::TypeMetadata) { - to.program_id = program_cache.gensym(reinterpret_cast(from->get_program())); - THRIFT_ASSIGN_N(annotations_, annotations, ); - if (from->has_doc()) { - to.__set_doc(from->get_doc()); - } - THRIFT_ASSIGN(name); -} - -THRIFT_CONVERSION(t_typedef) { - THRIFT_ASSIGN_METADATA(); - THRIFT_ASSIGN_ID(type); - THRIFT_ASSIGN(symbolic); - THRIFT_ASSIGN_N(is_forward_typedef(), forward, ); -} - -THRIFT_CONVERSION(t_enum_value) { - THRIFT_ASSIGN_OPT(doc); - THRIFT_ASSIGN(name); - THRIFT_ASSIGN(value); -} - -THRIFT_CONVERSION(t_enum) { - THRIFT_ASSIGN_METADATA(); - THRIFT_ASSIGN_LIST_N(t_enum_value, get_constants(), constants); -} - -THRIFT_CONVERSION(t_const_value) { - switch (from->get_type()) { - case t_const_value::CV_INTEGER: - THRIFT_ASSIGN_N(get_integer(), integer_val, ); - break; - case t_const_value::CV_DOUBLE: - THRIFT_ASSIGN_N(get_double(), double_val, ); - break; - case t_const_value::CV_STRING: - THRIFT_ASSIGN_N(get_string(), string_val, ); - break; - case t_const_value::CV_IDENTIFIER: - if (from) { - apache::thrift::plugin::t_const_identifier_value cidval ; - if (from->enum_) - cidval.__set_enum_val(store_type(from->enum_)); - cidval.__set_identifier_val(from->get_identifier()); - to.__set_const_identifier_val(cidval) ; - } - break; - case t_const_value::CV_MAP: - to.__isset.map_val = true; - if (from && !from->get_map().empty()) { - for (std::map< ::t_const_value*, ::t_const_value*>::const_iterator it - = from->get_map().begin(); - it != from->get_map().end(); - it++) { - to.map_val.insert(std::make_pair(convert(it->first), convert(it->second))); - } - } - break; - case t_const_value::CV_LIST: - to.__isset.list_val = true; - THRIFT_ASSIGN_LIST_N(t_const_value, get_list(), list_val); - break; - default: - throw plugin::ThriftPluginError("const value has no value"); - } -} -THRIFT_CONVERSION(t_const) { - THRIFT_ASSIGN_OPT(doc); - THRIFT_ASSIGN(name); - THRIFT_ASSIGN_ID(type); - THRIFT_ASSIGN_CONVERT(t_const_value, get_value(), value); -} -THRIFT_CONVERSION(t_field) { - THRIFT_ASSIGN_OPT(doc); - THRIFT_ASSIGN(name); - THRIFT_ASSIGN(key); - THRIFT_ASSIGN_N(get_req(), req, (plugin::Requiredness::type)); - THRIFT_ASSIGN(reference); - THRIFT_ASSIGN_ID(type); - THRIFT_ASSIGN_CONVERT(t_const_value, get_value(), value); -} -THRIFT_CONVERSION(t_struct) { - THRIFT_ASSIGN_METADATA(); - THRIFT_ASSIGN_LIST_N(t_field, get_members(), members); - THRIFT_ASSIGN_N(is_union(), is_union, ); - THRIFT_ASSIGN_N(is_xception(), is_xception, ); -} -THRIFT_CONVERSION(t_function) { - THRIFT_ASSIGN_OPT(doc); - THRIFT_ASSIGN(name); - THRIFT_ASSIGN_ID(returntype); - THRIFT_ASSIGN_N(is_oneway(), is_oneway, ); - THRIFT_ASSIGN_ID(arglist); - THRIFT_ASSIGN_ID(xceptions); -} - -THRIFT_CONVERSION(t_list) { - THRIFT_ASSIGN_METADATA(); - THRIFT_ASSIGN_OPT(cpp_name); - THRIFT_ASSIGN_ID(elem_type); -} -THRIFT_CONVERSION(t_set) { - THRIFT_ASSIGN_METADATA(); - THRIFT_ASSIGN_OPT(cpp_name); - THRIFT_ASSIGN_ID(elem_type); -} -THRIFT_CONVERSION(t_map) { - THRIFT_ASSIGN_METADATA(); - THRIFT_ASSIGN_OPT(cpp_name); - THRIFT_ASSIGN_ID(key_type); - THRIFT_ASSIGN_ID(val_type); -} - -THRIFT_CONVERSION(t_service) { - THRIFT_ASSIGN_METADATA(); - THRIFT_ASSIGN_LIST_N(t_function, get_functions(), functions); - THRIFT_ASSIGN_ID_N(t_service, get_extends(), extends_); -} - -THRIFT_CONVERSION(t_base_type) { - THRIFT_ASSIGN_METADATA(); - if (from->is_binary()) { - to.value = plugin::t_base::TYPE_BINARY; - } else { - switch (from->get_base()) { -#define T_BASETYPE_CASE(name) \ - case t_base_type::TYPE_##name: \ - to.value = plugin::t_base::TYPE_##name; \ - break - T_BASETYPE_CASE(VOID); - T_BASETYPE_CASE(STRING); - T_BASETYPE_CASE(BOOL); - T_BASETYPE_CASE(I8); - T_BASETYPE_CASE(I16); - T_BASETYPE_CASE(I32); - T_BASETYPE_CASE(I64); - T_BASETYPE_CASE(DOUBLE); - default: - throw plugin::ThriftPluginError("Base type union has no value"); - break; -#undef T_BASETYPE_CASE - } - } -} -THRIFT_CONVERSION(t_type) { -#define T_CONVERT_UNION_N(name, type) \ - else if (from->is_##name()) { \ - to.__isset.name##_val = true; \ - convert(reinterpret_cast< ::type*>(from), to.name##_val); \ - } -#define T_CONVERT_UNION(name) T_CONVERT_UNION_N(name, t_##name) - if (false) { - } - T_CONVERT_UNION(base_type) - T_CONVERT_UNION(typedef) - T_CONVERT_UNION(enum) - T_CONVERT_UNION(struct) - T_CONVERT_UNION_N(xception, t_struct) - T_CONVERT_UNION(list) - T_CONVERT_UNION(set) - T_CONVERT_UNION(map) - T_CONVERT_UNION(service) - else { - throw plugin::ThriftPluginError("Type union has no value"); - } -#undef T_CONVERT_UNION_N -#undef T_CONVERT_UNION -} - -THRIFT_CONVERSION(t_scope) { -#define T_SCOPE_ASSIGN(name, type) \ - boost::copy(from->name##s_ | boost::adaptors::map_values \ - | boost::adaptors::transformed(&store_type), \ - std::back_inserter(to.name##s)) - T_SCOPE_ASSIGN(type, t_type); - T_SCOPE_ASSIGN(constant, t_const); - T_SCOPE_ASSIGN(service, t_service); -#undef T_SCOPE_ASSIGN -} - -void get_global_cache(plugin::TypeRegistry& reg) { - reg.types = type_cache.cache; - reg.constants = const_cache.cache; - reg.services = service_cache.cache; -} - -void clear_global_cache() { - type_cache.clear(); - const_cache.clear(); - service_cache.clear(); - program_cache.clear() ; -} - -THRIFT_CONVERSION(t_program) { - THRIFT_ASSIGN_CONVERT(t_scope, scope(), scope); - THRIFT_ASSIGN(path); - THRIFT_ASSIGN(out_path); - THRIFT_ASSIGN(name); - THRIFT_ASSIGN(include_prefix); - THRIFT_ASSIGN(cpp_includes); - THRIFT_ASSIGN(c_includes); - THRIFT_ASSIGN(namespaces); - THRIFT_ASSIGN_N(is_out_path_absolute(), out_path_is_absolute, ); - THRIFT_ASSIGN_N(get_namespace(), namespace_, ); - THRIFT_ASSIGN_LIST_ID(t_type, typedef); - THRIFT_ASSIGN_LIST_ID(t_type, enum); - THRIFT_ASSIGN_LIST_ID(t_type, object); - THRIFT_ASSIGN_LIST_ID(t_const, const); - THRIFT_ASSIGN_LIST_ID(t_service, service); - THRIFT_ASSIGN_LIST_N(t_program, get_includes(), includes); - to.program_id = program_cache.gensym(reinterpret_cast(from)); -} - -PluginDelegateResult delegateToPlugin(t_program* program, const std::string& options) { - std::string language; - std::map parsed_options; - t_generator::parse_options(options, language, parsed_options); - std::string cmd = "thrift-gen-"; - if (language.find_first_not_of("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_-0123456789") - != std::string::npos) { - std::cerr << "Invalid language name" << std::endl; - return PLUGIN_FAILURE; - } - cmd.append(language); - FILE* fd = THRIFT_POPEN(cmd.c_str()); - if (fd) { -#ifdef _WIN32 - _setmode(fileno(fd), _O_BINARY); -#endif - shared_ptr transport( - new TFramedTransport(make_shared(fileno(fd)))); - TBinaryProtocol proto(transport); - - plugin::GeneratorInput input; - input.__set_parsed_options(parsed_options); - clear_global_cache(); - convert(program, input.program); - get_global_cache(input.type_registry); - try { - input.write(&proto); - transport->flush(); - } catch (std::exception& err) { - std::cerr << "Error while sending data to plugin: " << err.what() << std::endl; - THRIFT_PCLOSE(fd); - return PLUGIN_FAILURE; - } - - // TODO: be prepared for hang or crash of child process - int ret = THRIFT_PCLOSE(fd); - if (!ret) { - return PLUGIN_SUCCEESS; - } else { - std::cerr << "plugin process returned non zero exit code: " << ret << std::endl; - return PLUGIN_FAILURE; - } - } - clear_global_cache(); - return PLUGIN_NOT_FOUND; -} -} - diff --git a/compiler/cpp/src/thrift/plugin/type_util.h b/compiler/cpp/src/thrift/plugin/type_util.h deleted file mode 100644 index 996b5c66670..00000000000 --- a/compiler/cpp/src/thrift/plugin/type_util.h +++ /dev/null @@ -1,95 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -#ifndef T_PLUGIN_TYPE_UTIL_H -#define T_PLUGIN_TYPE_UTIL_H - -namespace apache { -namespace thrift { -namespace plugin { - -template -struct ToType {}; - -template -typename ToType::type* convert_forward(const From&); - -template -void convert(const From&, To*); - -template -typename ToType::type* convert(const From& from); - -class TypeRegistry; -void set_global_cache(const TypeRegistry&); -void clear_global_cache(); -} -} -} - -// conversion from raw compiler types to plugin wire type -namespace plugin_output { - -template -void convert(From* from, To& to); - -template -typename apache::thrift::plugin::ToType::type convert(From* from); - -void get_global_cache(apache::thrift::plugin::TypeRegistry&); -void clear_global_cache(); -} - -#define THRIFT_TYPE_MAPPING(TYPE) \ - class TYPE; \ - namespace apache { \ - namespace thrift { \ - namespace plugin { \ - class TYPE; \ - template <> \ - struct ToType< ::TYPE> { \ - typedef TYPE type; \ - }; \ - template <> \ - struct ToType { \ - typedef ::TYPE type; \ - }; \ - } \ - } \ - } -THRIFT_TYPE_MAPPING(t_base_type) -THRIFT_TYPE_MAPPING(t_const) -THRIFT_TYPE_MAPPING(t_const_value) -THRIFT_TYPE_MAPPING(t_container) -THRIFT_TYPE_MAPPING(t_doc) -THRIFT_TYPE_MAPPING(t_enum) -THRIFT_TYPE_MAPPING(t_enum_value) -THRIFT_TYPE_MAPPING(t_field) -THRIFT_TYPE_MAPPING(t_function) -THRIFT_TYPE_MAPPING(t_list) -THRIFT_TYPE_MAPPING(t_map) -THRIFT_TYPE_MAPPING(t_program) -THRIFT_TYPE_MAPPING(t_scope) -THRIFT_TYPE_MAPPING(t_service) -THRIFT_TYPE_MAPPING(t_set) -THRIFT_TYPE_MAPPING(t_struct) -THRIFT_TYPE_MAPPING(t_type) -THRIFT_TYPE_MAPPING(t_typedef) -#undef THRIFT_TYPE_MAPPING -#endif diff --git a/compiler/cpp/src/thrift/thriftl.ll b/compiler/cpp/src/thrift/thriftl.ll index 4f783be96f8..37735164960 100644 --- a/compiler/cpp/src/thrift/thriftl.ll +++ b/compiler/cpp/src/thrift/thriftl.ll @@ -159,7 +159,7 @@ literal_begin (['\"]) g_doctext[strlen(g_doctext) - 1] = '\0'; g_doctext = clean_up_doctext(g_doctext); g_doctext_lineno = yylineno; - if( (g_program_doctext_candidate == NULL) && (g_program_doctext_status == INVALID)){ + if( (g_program_doctext_candidate == nullptr) && (g_program_doctext_status == INVALID)){ g_program_doctext_candidate = strdup(g_doctext); g_program_doctext_lineno = g_doctext_lineno; g_program_doctext_status = STILL_CANDIDATE; @@ -207,15 +207,13 @@ literal_begin (['\"]) "cpp_include" { return tok_cpp_include; } "cpp_type" { return tok_cpp_type; } "java_package" { error_unsupported_namespace_decl("java_package", "java"); /* do nothing */ } -"cocoa_prefix" { error_unsupported_namespace_decl("cocoa_prefix", "cocoa"); /* do nothing */ } -"csharp_namespace" { error_unsupported_namespace_decl("csharp"); /* do nothing */ } "delphi_namespace" { error_unsupported_namespace_decl("delphi"); /* do nothing */ } "php_namespace" { error_unsupported_namespace_decl("php"); /* do nothing */ } "py_module" { error_unsupported_namespace_decl("py_module", "py"); /* do nothing */ } "perl_package" { error_unsupported_namespace_decl("perl_package", "perl"); /* do nothing */ } "ruby_namespace" { error_unsupported_namespace_decl("ruby"); /* do nothing */ } -"smalltalk_category" { error_unsupported_namespace_decl("smalltalk_category", "smalltalk.category"); /* do nothing */ } -"smalltalk_prefix" { error_unsupported_namespace_decl("smalltalk_category", "smalltalk.category"); /* do nothing */ } +"smalltalk_category" { error_unsupported_namespace_decl("smalltalk_category", "st"); /* do nothing */ } +"smalltalk_prefix" { error_unsupported_namespace_decl("smalltalk_prefix", "st"); /* do nothing */ } "xsd_all" { return tok_xsd_all; } "xsd_optional" { return tok_xsd_optional; } "xsd_nillable" { return tok_xsd_nillable; } @@ -266,7 +264,7 @@ literal_begin (['\"]) {intconstant} { errno = 0; - yylval.iconst = strtoll(yytext, NULL, 10); + yylval.iconst = strtoll(yytext, nullptr, 10); if (errno == ERANGE) { integer_overflow(yytext); } @@ -277,7 +275,7 @@ literal_begin (['\"]) errno = 0; char sign = yytext[0]; int shift = sign == '0' ? 2 : 3; - yylval.iconst = strtoll(yytext+shift, NULL, 16); + yylval.iconst = strtoll(yytext+shift, nullptr, 16); if (sign == '-') { yylval.iconst = -yylval.iconst; } diff --git a/compiler/cpp/src/thrift/thrifty.yy b/compiler/cpp/src/thrift/thrifty.yy index df34adf0452..dc6838a5473 100644 --- a/compiler/cpp/src/thrift/thrifty.yy +++ b/compiler/cpp/src/thrift/thrifty.yy @@ -250,7 +250,7 @@ Program: HeaderList DefinitionList { pdebug("Program -> Headers DefinitionList"); - if((g_program_doctext_candidate != NULL) && (g_program_doctext_status != ALREADY_PROCESSED)) + if((g_program_doctext_candidate != nullptr) && (g_program_doctext_status != ALREADY_PROCESSED)) { g_program->set_doc(g_program_doctext_candidate); g_program_doctext_status = ALREADY_PROCESSED; @@ -262,9 +262,9 @@ CaptureDocText: { if (g_parse_mode == PROGRAM) { $$ = g_doctext; - g_doctext = NULL; + g_doctext = nullptr; } else { - $$ = NULL; + $$ = nullptr; } } @@ -300,7 +300,7 @@ Header: if (g_parse_mode == PROGRAM) { g_program->set_namespace($2, $3); } - if ($4 != NULL) { + if ($4 != nullptr) { g_program->set_namespace_annotations($2, $4->annotations_); delete $4; } @@ -339,7 +339,7 @@ DefinitionList: DefinitionList CaptureDocText Definition { pdebug("DefinitionList -> DefinitionList Definition"); - if ($2 != NULL && $3 != NULL) { + if ($2 != nullptr && $3 != nullptr) { $3->set_doc($2); } } @@ -362,7 +362,7 @@ Definition: pdebug("Definition -> TypeDefinition"); if (g_parse_mode == PROGRAM) { g_scope->add_type($1->get_name(), $1); - if (g_parent_scope != NULL) { + if (g_parent_scope != nullptr) { g_parent_scope->add_type(g_parent_prefix + $1->get_name(), $1); } if (! g_program->is_unique_typename($1)) { @@ -377,7 +377,7 @@ Definition: pdebug("Definition -> Service"); if (g_parse_mode == PROGRAM) { g_scope->add_service($1->get_name(), $1); - if (g_parent_scope != NULL) { + if (g_parent_scope != nullptr) { g_parent_scope->add_service(g_parent_prefix + $1->get_name(), $1); } g_program->add_service($1); @@ -441,7 +441,7 @@ Typedef: validate_simple_identifier( $3); t_typedef *td = new t_typedef(g_program, $2, $3); $$ = td; - if ($4 != NULL) { + if ($4 != nullptr) { $$->annotations_ = $4->annotations_; delete $4; } @@ -454,7 +454,7 @@ Enum: $$ = $4; validate_simple_identifier( $2); $$->set_name($2); - if ($6 != NULL) { + if ($6 != nullptr) { $$->annotations_ = $6->annotations_; delete $6; } @@ -468,7 +468,7 @@ Enum: t_const_value* const_val = new t_const_value((*c_iter)->get_value()); const_val->set_enum($$); g_scope->add_constant(const_name, new t_const(g_type_i32, (*c_iter)->get_name(), const_val)); - if (g_parent_scope != NULL) { + if (g_parent_scope != nullptr) { g_parent_scope->add_constant(g_parent_prefix + const_name, new t_const(g_type_i32, (*c_iter)->get_name(), const_val)); } } @@ -494,10 +494,10 @@ EnumDef: { pdebug("EnumDef -> EnumValue"); $$ = $2; - if ($1 != NULL) { + if ($1 != nullptr) { $$->set_doc($1); } - if ($3 != NULL) { + if ($3 != nullptr) { $$->annotations_ = $3->annotations_; delete $3; } @@ -536,7 +536,7 @@ Senum: pdebug("Senum -> tok_senum tok_identifier { SenumDefList }"); validate_simple_identifier( $2); $$ = new t_typedef(g_program, $4, $2); - if ($6 != NULL) { + if ($6 != nullptr) { $$->annotations_ = $6->annotations_; delete $6; } @@ -574,11 +574,11 @@ Const: validate_const_type($$); g_scope->add_constant($3, $$); - if (g_parent_scope != NULL) { + if (g_parent_scope != nullptr) { g_parent_scope->add_constant(g_parent_prefix + $3, $$); } } else { - $$ = NULL; + $$ = nullptr; } } @@ -681,7 +681,7 @@ Struct: $5->set_union($1 == struct_is_union); $$ = $5; $$->set_name($2); - if ($7 != NULL) { + if ($7 != nullptr) { $$->annotations_ = $7->annotations_; delete $7; } @@ -724,7 +724,7 @@ XsdAttributes: } | { - $$ = NULL; + $$ = nullptr; } Xception: @@ -735,7 +735,7 @@ Xception: $4->set_name($2); $4->set_xception(true); $$ = $4; - if ($6 != NULL) { + if ($6 != nullptr) { $$->annotations_ = $6->annotations_; delete $6; } @@ -749,7 +749,7 @@ Service: $$ = $6; $$->set_name($2); $$->set_extends($3); - if ($9 != NULL) { + if ($9 != nullptr) { $$->annotations_ = $9->annotations_; delete $9; } @@ -769,10 +769,10 @@ Extends: tok_extends tok_identifier { pdebug("Extends -> tok_extends tok_identifier"); - $$ = NULL; + $$ = nullptr; if (g_parse_mode == PROGRAM) { $$ = g_scope->get_service($2); - if ($$ == NULL) { + if ($$ == nullptr) { yyerror("Service \"%s\" has not been defined.", $2); exit(1); } @@ -780,7 +780,7 @@ Extends: } | { - $$ = NULL; + $$ = nullptr; } FunctionList: @@ -802,10 +802,10 @@ Function: validate_simple_identifier( $4); $6->set_name(std::string($4) + "_args"); $$ = new t_function($3, $4, $6, $8, $2); - if ($1 != NULL) { + if ($1 != nullptr) { $$->set_doc($1); } - if ($9 != NULL) { + if ($9 != nullptr) { $$->annotations_ = $9->annotations_; delete $9; } @@ -868,20 +868,20 @@ Field: $$ = new t_field($4, $6, $2.value); $$->set_reference($5); $$->set_req($3); - if ($7 != NULL) { + if ($7 != nullptr) { g_scope->resolve_const_value($7, $4); validate_field_value($$, $7); $$->set_value($7); } $$->set_xsd_optional($8); $$->set_xsd_nillable($9); - if ($1 != NULL) { + if ($1 != nullptr) { $$->set_doc($1); } - if ($10 != NULL) { + if ($10 != nullptr) { $$->set_xsd_attrs($10); } - if ($11 != NULL) { + if ($11 != nullptr) { $$->annotations_ = $11->annotations_; delete $11; } @@ -974,12 +974,12 @@ FieldValue: if (g_parse_mode == PROGRAM) { $$ = $2; } else { - $$ = NULL; + $$ = nullptr; } } | { - $$ = NULL; + $$ = nullptr; } FunctionType: @@ -1000,11 +1000,11 @@ FieldType: pdebug("FieldType -> tok_identifier"); if (g_parse_mode == INCLUDES) { // Ignore identifiers in include mode - $$ = NULL; + $$ = nullptr; } else { // Lookup the identifier in the current scope $$ = g_scope->get_type($1); - if ($$ == NULL) { + if ($$ == nullptr) { /* * Either this type isn't yet declared, or it's never declared. Either way allow it and we'll figure it out @@ -1028,7 +1028,7 @@ FieldType: BaseType: SimpleBaseType TypeAnnotations { pdebug("BaseType -> SimpleBaseType TypeAnnotations"); - if ($2 != NULL) { + if ($2 != nullptr) { $$ = new t_base_type(*static_cast($1)); $$->annotations_ = $2->annotations_; delete $2; @@ -1088,7 +1088,7 @@ ContainerType: SimpleContainerType TypeAnnotations { pdebug("ContainerType -> SimpleContainerType TypeAnnotations"); $$ = $1; - if ($2 != NULL) { + if ($2 != nullptr) { $$->annotations_ = $2->annotations_; delete $2; } @@ -1116,7 +1116,7 @@ MapType: { pdebug("MapType -> tok_map "); $$ = new t_map($4, $6); - if ($2 != NULL) { + if ($2 != nullptr) { ((t_container*)$$)->set_cpp_name(std::string($2)); } } @@ -1126,7 +1126,7 @@ SetType: { pdebug("SetType -> tok_set"); $$ = new t_set($4); - if ($2 != NULL) { + if ($2 != nullptr) { ((t_container*)$$)->set_cpp_name(std::string($2)); } } @@ -1137,7 +1137,7 @@ ListType: pdebug("ListType -> tok_list"); check_for_list_of_bytes($3); $$ = new t_list($3); - if ($5 != NULL) { + if ($5 != nullptr) { ((t_container*)$$)->set_cpp_name(std::string($5)); } } @@ -1149,7 +1149,7 @@ CppType: } | { - $$ = NULL; + $$ = nullptr; } TypeAnnotations: @@ -1160,7 +1160,7 @@ TypeAnnotations: } | { - $$ = NULL; + $$ = nullptr; } TypeAnnotationList: diff --git a/compiler/cpp/src/thrift/version.h.in b/compiler/cpp/src/thrift/version.h similarity index 95% rename from compiler/cpp/src/thrift/version.h.in rename to compiler/cpp/src/thrift/version.h index aef076f7f0c..3a7250d9d16 100644 --- a/compiler/cpp/src/thrift/version.h.in +++ b/compiler/cpp/src/thrift/version.h @@ -24,6 +24,6 @@ #pragma once #endif // _MSC_VER -#define THRIFT_VERSION "@PACKAGE_VERSION@" +#define THRIFT_VERSION "0.14.0" #endif // _THRIFT_VERSION_H_ diff --git a/compiler/cpp/src/thrift/windows/config.h b/compiler/cpp/src/thrift/windows/config.h index d2269cf7814..6ba4f9ae6c1 100644 --- a/compiler/cpp/src/thrift/windows/config.h +++ b/compiler/cpp/src/thrift/windows/config.h @@ -46,25 +46,6 @@ // squelch bool conversion performance warning #pragma warning(disable : 4800) -// MSVC10 (2010) or later has stdint.h -#if _MSC_VER >= 1600 -#define HAVE_STDINT_H 1 -#endif - -// Must be using VS2010 or later, or boost, so that C99 types are defined in the global namespace -#ifdef HAVE_STDINT_H -#include -#else -#include - -typedef boost::int64_t int64_t; -typedef boost::uint64_t uint64_t; -typedef boost::int32_t int32_t; -typedef boost::uint32_t uint32_t; -typedef boost::int16_t int16_t; -typedef boost::uint16_t uint16_t; -typedef boost::int8_t int8_t; -typedef boost::uint8_t uint8_t; -#endif +#include #endif // _THRIFT_WINDOWS_CONFIG_H_ diff --git a/compiler/cpp/test/CMakeLists.txt b/compiler/cpp/test/CMakeLists.txt index a09f23d7c24..b80f06000b4 100644 --- a/compiler/cpp/test/CMakeLists.txt +++ b/compiler/cpp/test/CMakeLists.txt @@ -17,67 +17,10 @@ # under the License. # - -if(${WITH_PLUGIN}) - include_directories(SYSTEM "${Boost_INCLUDE_DIRS}") - - # Make sure gen-cpp files can be included - include_directories("${CMAKE_CURRENT_BINARY_DIR}") - - set(plugintest_SOURCES - plugin/conversion_test.cc - ) - add_executable(plugintest ${plugintest_SOURCES}) - if(WITH_SHARED_LIB AND NOT MSVC) - target_link_libraries(plugintest - thriftc - ${Boost_LIBRARIES} - ) - else() - target_link_libraries(plugintest - thriftc_static - thrift_static - ${Boost_LIBRARIES} - ) - endif() - add_test(NAME PluginUnitTest COMMAND plugintest) - - set(thrift-gen-mycpp_SOURCES - ../src/thrift/generate/t_cpp_generator.cc - plugin/cpp_plugin.cc - ) - add_executable(thrift-gen-mycpp ${thrift-gen-mycpp_SOURCES}) - if(WITH_SHARED_LIB AND NOT MSVC) - target_link_libraries(thrift-gen-mycpp thriftc) - else() - target_link_libraries(thrift-gen-mycpp thriftc_static thrift_static) - endif() - - if(CMAKE_BUILD_TYPE STREQUAL "Debug") - set(BUILDTYPE "Debug") - else() - # RelWithDebInfo generates binaries in "Release" directory too - set(BUILDTYPE "Release") - endif() - - set_directory_properties(PROPERTIES - ADDITIONAL_MAKE_CLEAN_FILES gen-cpp - ADDITIONAL_MAKE_CLEAN_FILES gen-mycpp) - - file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/gen-cpp) - file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/gen-mycpp) - add_test(NAME PluginIntegrationTest - COMMAND ${CMAKE_COMMAND} - -DTHRIFT_COMPILER=${THRIFT_COMPILER} - -DBINDIR=${CMAKE_RUNTIME_OUTPUT_DIRECTORY} - -DBUILDTYPE=${BUILDTYPE} - -DCURDIR=${CMAKE_CURRENT_BINARY_DIR} - -DSRCDIR=${CMAKE_CURRENT_SOURCE_DIR} - -P ${CMAKE_CURRENT_SOURCE_DIR}/cpp_plugin_test.cmake) -endif() - file(GLOB KEYWORD_SAMPLES "${CMAKE_CURRENT_SOURCE_DIR}/keyword-samples/*.thrift") -foreach(LANG ${thrift_compiler_LANGS}) +set(KEYWORD_LANGS ${thrift_compiler_LANGS}) +LIST(REMOVE_ITEM KEYWORD_LANGS swift) # in Swift you can escape reserved words +foreach(LANG ${KEYWORD_LANGS}) foreach(SAMPLE ${KEYWORD_SAMPLES}) get_filename_component(FILENAME ${SAMPLE} NAME_WE) add_test(NAME "${LANG}_${FILENAME}" @@ -87,6 +30,9 @@ foreach(LANG ${thrift_compiler_LANGS}) endforeach() endforeach() - -find_package(PythonInterp REQUIRED) -add_test(NAME StalenessCheckTest COMMAND ${PYTHON_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/compiler/staleness_check.py ${THRIFT_COMPILER}) +find_package(PythonInterp QUIET) +if(PYTHONINTERP_FOUND) + add_test(NAME StalenessCheckTest COMMAND ${PYTHON_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/compiler/staleness_check.py ${THRIFT_COMPILER}) +else() + message(WARNING "Skipping StalenessCheckTest as there is no python interpreter available.") +endif() diff --git a/compiler/cpp/test/Makefile.am b/compiler/cpp/test/Makefile.am index b7fc91d359f..10efd073767 100644 --- a/compiler/cpp/test/Makefile.am +++ b/compiler/cpp/test/Makefile.am @@ -21,40 +21,7 @@ # Please see doc/old-thrift-license.txt in the Thrift distribution for # details. -AUTOMAKE_OPTIONS = subdir-objects serial-tests +AUTOMAKE_OPTIONS = subdir-objects serial-tests nostdinc -AM_CPPFLAGS = $(BOOST_CPPFLAGS) -I$(top_srcdir)/compiler/cpp/src -AM_LDFLAGS = $(BOOST_LDFLAGS) +AM_CPPFLAGS = -I$(top_srcdir)/compiler/cpp/src AM_CXXFLAGS = -Wall -Wextra -pedantic - -if WITH_PLUGIN -check_PROGRAMS = plugintest - -noinst_PROGRAMS = thrift-gen-mycpp - -all-local: thrift-gen-bincat - -AM_CPPFLAGS += -I$(top_srcdir)/lib/cpp/src -I$(top_builddir)/lib/cpp/src - -plugintest_SOURCES = plugin/conversion_test.cc -plugintest_LDADD = $(top_builddir)/compiler/cpp/libthriftc.la - -thrift_gen_mycpp_SOURCES = plugin/cpp_plugin.cc \ - plugin/t_cpp_generator.cc -thrift_gen_mycpp_CPPFLAGS = $(AM_CPPFLAGS) -I$(top_srcdir)/compiler/cpp -I$(top_srcdir)/compiler/cpp/src/generate -thrift_gen_mycpp_LDADD = $(top_builddir)/compiler/cpp/libthriftc.la - -cpp_plugin_test.sh: thrift-gen-mycpp - -thrift-gen-bincat: - cp bincat.sh $@ - chmod 755 $@ - -plugin_stability_test.sh: thrift-gen-bincat - -TESTS = $(check_PROGRAMS) cpp_plugin_test.sh plugin_stability_test.sh - -clean-local: - $(RM) -rf gen-cpp gen-mycpp gen-bincat thrift-gen-bincat - -endif diff --git a/compiler/cpp/test/bincat.sh b/compiler/cpp/test/bincat.sh deleted file mode 100755 index c7f90785e7e..00000000000 --- a/compiler/cpp/test/bincat.sh +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/bash - -exec /bin/cat diff --git a/compiler/cpp/test/compiler/staleness_check.py b/compiler/cpp/test/compiler/staleness_check.py index 5b11dff9e52..6b67798d6a4 100755 --- a/compiler/cpp/test/compiler/staleness_check.py +++ b/compiler/cpp/test/compiler/staleness_check.py @@ -46,7 +46,7 @@ def test_staleness_check_of_single_thrift_file_without_changed_output(self): first_modification_time = os.path.getmtime(os.path.join(used_file_path)) - time.sleep(0.1) + time.sleep(1.0) subprocess.call(command) @@ -74,7 +74,7 @@ def test_staleness_check_of_single_thrift_file_with_changed_output(self): used_file.write("\n/* This is a comment */\n") used_file.close() - time.sleep(0.1) + time.sleep(1.0) subprocess.call(command) @@ -112,7 +112,7 @@ def test_staleness_check_of_included_file(self): temp_included_file.write("\nconst i32 an_integer = 42\n") temp_included_file.close() - time.sleep(0.1) + time.sleep(1.0) subprocess.call(command) diff --git a/compiler/cpp/test/cpp_plugin_test.cmake b/compiler/cpp/test/cpp_plugin_test.cmake deleted file mode 100644 index fd182818d1b..00000000000 --- a/compiler/cpp/test/cpp_plugin_test.cmake +++ /dev/null @@ -1,45 +0,0 @@ -# -# Licensed to the Apache Software Foundation (ASF) under one -# or more contributor license agreements. See the NOTICE file -# distributed with this work for additional information -# regarding copyright ownership. The ASF licenses this file -# to you under the Apache License, Version 2.0 (the -# "License"); you may not use this file except in compliance -# with the License. You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, -# software distributed under the License is distributed on an -# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -# KIND, either express or implied. See the License for the -# specific language governing permissions and limitations -# under the License. -# - -file(MAKE_DIRECTORY ${CURDIR}/gen-cpp) -execute_process(COMMAND ${THRIFT_COMPILER} -r -out ${CURDIR}/gen-cpp -gen cpp ${SRCDIR}/../../../test/Include.thrift) -if(EXITCODE) - message(FATAL_ERROR "FAILED: \"${ARGV}\": \"${EXITCODE}\"") -endif() -if(WIN32) - set(ENV{PATH} "${BINDIR}/${BUILDTYPE};${BINDIR};$ENV{PATH}") -else() - set(ENV{PATH} "${BINDIR}:$ENV{PATH}") -endif() - -file(MAKE_DIRECTORY ${CURDIR}/gen-mycpp) -execute_process(COMMAND ${THRIFT_COMPILER} -r -out ${CURDIR}/gen-mycpp -gen mycpp ${SRCDIR}/../../../test/Include.thrift RESULT_VARIABLE EXITCODE) -if(EXITCODE) - message(FATAL_ERROR "FAILED: \"${EXITCODE}\"") -endif() - -find_program(DIFF diff) -if(DIFF) - execute_process(COMMAND ${DIFF} -urN gen-cpp gen-mycpp RESULT_VARIABLE EXITCODE) - if(EXITCODE) - message(FATAL_ERROR "FAILED: \"${EXITCODE}\"") - endif() -else() - message(WARNING "diff executable is not available. Not validating plugin-generated code.") -endif() diff --git a/compiler/cpp/test/plugin/conversion_test.cc b/compiler/cpp/test/plugin/conversion_test.cc deleted file mode 100644 index 3c8d812cfba..00000000000 --- a/compiler/cpp/test/plugin/conversion_test.cc +++ /dev/null @@ -1,498 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -#include "thrift/parse/t_program.h" -#include "thrift/plugin/type_util.h" -#include "thrift/plugin/plugin_types.h" - -#include -#include - -#include -#include -#include - -using namespace apache::thrift; -using namespace boost::unit_test; - -namespace test_data { -#define T_TEST_TYPES \ - BOOST_PP_TUPLE_TO_LIST(14, \ - (program, \ - base_type, \ - enum_value, \ - enum, \ - const_value, \ - const, \ - list, \ - set, \ - map, \ - field, \ - struct, \ - typedef, \ - function, \ - service)) -#define T_DELETE_TESTDATA(r, d, elem) \ - for (std::vector::reverse_iterator it = elem##s.rbegin(); it != elem##s.rend(); it++) \ - delete *it; -#define T_DECL_TESTDATA(r, d, elem) static std::vector< ::t_##elem*> elem##s; -BOOST_PP_LIST_FOR_EACH(T_DECL_TESTDATA, _, T_TEST_TYPES) -#undef T_DECL_TESTDATA - -bool has_data = false; -void cleanup() { - if (has_data) { - has_data = false; - BOOST_PP_LIST_FOR_EACH(T_DELETE_TESTDATA, _, T_TEST_TYPES) - } -} - -void init_programs() { - programs.push_back(new t_program("prog path", "prog_name")); -} - -void init_base_types() { - base_types.push_back(new ::t_base_type("name0", ::t_base_type::TYPE_VOID)); - base_types.push_back(new ::t_base_type("name1", ::t_base_type::TYPE_STRING)); - base_types.push_back(new ::t_base_type("name2", ::t_base_type::TYPE_BOOL)); - base_types.push_back(new ::t_base_type("name3", ::t_base_type::TYPE_I8)); - base_types.push_back(new ::t_base_type("name4", ::t_base_type::TYPE_I16)); - base_types.push_back(new ::t_base_type("name5", ::t_base_type::TYPE_I32)); - base_types.push_back(new ::t_base_type("name6", ::t_base_type::TYPE_I64)); - base_types.push_back(new ::t_base_type("name7", ::t_base_type::TYPE_DOUBLE)); -} - -void init_const_values() { - const_values.push_back(new t_const_value(42)); - const_values.push_back(new t_const_value("foo")); - { - t_const_value* x = new t_const_value; - x->set_double(3.1415); - const_values.push_back(x); - } - { - t_const_value* x = new t_const_value; - x->set_identifier("bar"); - x->set_enum(enums[0]); - const_values.push_back(x); - } - { - t_const_value* x = new t_const_value; - x->set_map(); - x->add_map(const_values[0], const_values[1]); - x->add_map(const_values[1], const_values[0]); - const_values.push_back(x); - } - { - t_const_value* x = new t_const_value; - x->set_list(); - x->add_list(const_values[0]); - x->add_list(const_values[1]); - const_values.push_back(x); - } -} - -void init_consts() { - // base_type/enum indexes for this and other tests are arbitrary - consts.push_back(new t_const(base_types[2], "aaa", const_values[0])); - consts.back()->set_doc("soem doc"); - consts.push_back(new t_const(base_types[3], "bbb", const_values[1])); -} - -void init_enum_values() { - enum_values.push_back(new t_enum_value("VAL1", 11)); - enum_values.back()->set_doc("enum doc 1"); - enum_values.back()->annotations_.insert(std::make_pair("anno1", "val1")); - - enum_values.push_back(new t_enum_value("VAL2", 22)); -} - -void init_enums() { - enums.push_back(new t_enum(programs[0])); - enums.back()->set_doc("enum doc 1"); - enums.back()->annotations_.insert(std::make_pair("anno1", "val1")); - enums.back()->set_name("fooo"); - enums.back()->append(enum_values[0]); - enums.back()->append(enum_values[1]); -} - -void init_lists() { - lists.push_back(new t_list(enums[0])); - lists.push_back(new t_list(base_types[5])); - lists.back()->set_cpp_name("list_cpp_name_1"); -} -void init_sets() { - sets.push_back(new t_set(base_types[4])); - sets.push_back(new t_set(enums[0])); - sets.back()->set_cpp_name("set_cpp_name_1"); -} -void init_maps() { - maps.push_back(new t_map(base_types[4], base_types[1])); - maps.push_back(new t_map(base_types[5], enums[0])); - maps.back()->set_cpp_name("map_cpp_name_1"); -} - -void init_typedefs() { - typedefs.push_back(new t_typedef(programs[0], base_types[3], "VAL1")); -} -void init_fields() { - fields.push_back(new t_field(base_types[1], "f1")); - fields.back()->set_reference(false); - fields.back()->set_req(t_field::T_OPTIONAL); - fields.push_back(new t_field(base_types[2], "f2", 9)); - fields.back()->set_reference(true); - fields.push_back(new t_field(base_types[3], "f3", 11)); - fields.back()->set_req(t_field::T_REQUIRED); - fields.back()->set_value(const_values[0]); -} -void init_structs() { - structs.push_back(new t_struct(programs[0], "struct1")); - structs.back()->append(fields[0]); - structs.back()->append(fields[1]); - structs.push_back(new t_struct(programs[0], "union1")); - structs.back()->append(fields[0]); - structs.back()->append(fields[1]); - structs.back()->set_union(true); - structs.push_back(new t_struct(programs[0], "xcept1")); - structs.back()->set_xception(true); -} -void init_functions() { - structs.push_back(new t_struct(programs[0], "errs1")); - t_struct* errors = structs.back(); - structs.push_back(new t_struct(programs[0], "args1")); - t_struct* arglist = structs.back(); - functions.push_back(new t_function(base_types[0], "func1", errors, arglist, false)); - functions.push_back(new t_function(base_types[0], "func2", errors, arglist, true)); -} -void init_services() { - services.push_back(new t_service(programs[0])); - services.back()->set_doc("srv1 doc"); - services.back()->set_name("srv1"); - services.back()->add_function(functions[0]); - services.back()->add_function(functions[1]); - - services.push_back(new t_service(programs[0])); - services.back()->set_name("srv2"); - services.back()->set_extends(services[0]); -} - -std::vector types; -void init_types() { -#define T_COPY_TYPES(type) std::copy(type##s.begin(), type##s.end(), std::back_inserter(types)) - T_COPY_TYPES(base_type); - T_COPY_TYPES(enum); - T_COPY_TYPES(typedef); - T_COPY_TYPES(struct); - T_COPY_TYPES(list); - T_COPY_TYPES(set); - T_COPY_TYPES(map); -// T_COPY_TYPES(service); -#undef T_COPY_TYPES -} - -void init() { - if (!has_data) { - has_data = true; -#define T_INIT_TESTDATA(r, d, elem) init_##elem##s(); - BOOST_PP_LIST_FOR_EACH(T_INIT_TESTDATA, _, T_TEST_TYPES) - init_types(); -#undef T_INIT_TESTDATA - } -} -} -struct GlobalFixture { - ~GlobalFixture() { test_data::cleanup(); } -}; -#if (BOOST_VERSION >= 105900) -BOOST_GLOBAL_FIXTURE(GlobalFixture); -#else -BOOST_GLOBAL_FIXTURE(GlobalFixture) -#endif - -void migrate_global_cache() { - plugin::TypeRegistry reg; - plugin_output::get_global_cache(reg); - plugin::set_global_cache(reg); - plugin_output::clear_global_cache(); -} -template -T* round_trip(T* t) { - typename plugin::ToType::type p; - plugin::clear_global_cache(); - plugin_output::clear_global_cache(); - plugin_output::convert(t, p); - migrate_global_cache(); - return plugin::convert(p); -} - -void test_base_type(::t_base_type* sut) { - plugin::t_base_type p; - plugin_output::convert(sut, p); - boost::scoped_ptr< ::t_base_type> sut2(plugin::convert(p)); - -#define THRIFT_CHECK(r, data, elem) BOOST_PP_EXPAND(BOOST_CHECK_EQUAL(data elem, sut2->elem)); - BOOST_PP_LIST_FOR_EACH(THRIFT_CHECK, - sut->, - BOOST_PP_TUPLE_TO_LIST(7, - (is_void(), - is_string(), - is_bool(), - is_string_list(), - is_binary(), - is_string_enum(), - is_base_type()))) -} - -void test_const_value(t_const_value* sut) { - boost::scoped_ptr sut2(round_trip(sut)); - - BOOST_CHECK_EQUAL(sut->get_type(), sut2->get_type()); - switch (sut->get_type()) { -#define T_CONST_VALUE_CASE(type, name) \ - case t_const_value::type: \ - BOOST_CHECK_EQUAL(sut->get_##name(), sut2->get_##name()); \ - break - T_CONST_VALUE_CASE(CV_INTEGER, integer); - T_CONST_VALUE_CASE(CV_DOUBLE, double); - T_CONST_VALUE_CASE(CV_STRING, string); - T_CONST_VALUE_CASE(CV_IDENTIFIER, identifier); -#undef T_CONST_VALUE_CASE - case t_const_value::CV_MAP: - BOOST_CHECK_EQUAL(sut->get_map().size(), sut2->get_map().size()); - { - std::map sut_values; - for (std::map::const_iterator it = sut->get_map().begin(); - it != sut->get_map().end(); it++) { - sut_values[it->first->get_type()] = it->second->get_type(); - } - std::map sut2_values; - for (std::map::const_iterator it = sut2->get_map().begin(); - it != sut2->get_map().end(); it++) { - sut2_values[it->first->get_type()] = it->second->get_type(); - } - BOOST_CHECK_EQUAL(sut_values.begin()->first, sut2_values.begin()->first); - BOOST_CHECK_EQUAL(sut_values.begin()->second, sut2_values.begin()->second); - } - break; - case t_const_value::CV_LIST: - BOOST_CHECK_EQUAL(sut->get_list().size(), sut2->get_list().size()); - BOOST_CHECK_EQUAL(sut->get_list().front()->get_type(), sut2->get_list().front()->get_type()); - break; - default: - BOOST_ASSERT(false); - break; - } -} - -void test_const(t_const* sut) { - boost::scoped_ptr< ::t_const> sut2(round_trip(sut)); - - BOOST_PP_LIST_FOR_EACH(THRIFT_CHECK, - sut->, - BOOST_PP_TUPLE_TO_LIST(4, - (get_type()->get_name(), - get_name(), - get_value()->get_type(), - get_doc()))) -} - -void test_enum_value(t_enum_value* sut) { - boost::scoped_ptr sut2(round_trip(sut)); - - BOOST_PP_LIST_FOR_EACH(THRIFT_CHECK, - sut->, - BOOST_PP_TUPLE_TO_LIST(3, (get_name(), get_value(), get_doc()))) -} - -void test_enum(t_enum* sut) { - boost::scoped_ptr< ::t_enum> sut2(round_trip(sut)); - - BOOST_PP_LIST_FOR_EACH(THRIFT_CHECK, - sut->, - BOOST_PP_TUPLE_TO_LIST(6, - (get_name(), - get_min_value()->get_value(), - get_max_value()->get_value(), - get_constant_by_value(11)->get_value(), - get_constant_by_name("VAL1")->get_value(), - get_doc()))) -} - -void test_list(t_list* sut) { - boost::scoped_ptr sut2(round_trip(sut)); - - BOOST_PP_LIST_FOR_EACH(THRIFT_CHECK, - sut->, - BOOST_PP_TUPLE_TO_LIST(4, - (get_elem_type()->get_name(), - has_cpp_name(), - get_doc(), - get_name()))) - if (sut->has_cpp_name()) - BOOST_CHECK_EQUAL(sut->get_cpp_name(), sut2->get_cpp_name()); -} -void test_set(t_set* sut) { - boost::scoped_ptr sut2(round_trip(sut)); - - BOOST_PP_LIST_FOR_EACH(THRIFT_CHECK, - sut->, - BOOST_PP_TUPLE_TO_LIST(4, - (get_elem_type()->get_name(), - has_cpp_name(), - get_doc(), - get_name()))) - if (sut->has_cpp_name()) - BOOST_CHECK_EQUAL(sut->get_cpp_name(), sut2->get_cpp_name()); -} -void test_map(t_map* sut) { - boost::scoped_ptr sut2(round_trip(sut)); - - BOOST_PP_LIST_FOR_EACH(THRIFT_CHECK, - sut->, - BOOST_PP_TUPLE_TO_LIST(5, - (get_key_type()->get_name(), - get_val_type()->get_name(), - has_cpp_name(), - get_doc(), - get_name()))) - if (sut->has_cpp_name()) - BOOST_CHECK_EQUAL(sut->get_cpp_name(), sut2->get_cpp_name()); -} - -void test_typedef(t_typedef* sut) { - boost::scoped_ptr sut2(round_trip(sut)); - - BOOST_PP_LIST_FOR_EACH(THRIFT_CHECK, - sut->, - BOOST_PP_TUPLE_TO_LIST(4, - (get_doc(), - get_name(), - get_symbolic(), - is_forward_typedef()))) -} - -void test_type(t_type* sut) { - boost::scoped_ptr sut2(round_trip(sut)); - - BOOST_PP_LIST_FOR_EACH(THRIFT_CHECK, - sut->, - BOOST_PP_TUPLE_TO_LIST(15, - (is_void(), - is_base_type(), - is_string(), - is_bool(), - is_typedef(), - is_enum(), - is_struct(), - is_xception(), - is_container(), - is_list(), - is_set(), - is_map(), - is_service(), - get_doc(), - get_name()))) -} - -void test_field(t_field* sut) { - boost::scoped_ptr sut2(round_trip(sut)); - - BOOST_PP_LIST_FOR_EACH(THRIFT_CHECK, - sut->, - BOOST_PP_TUPLE_TO_LIST(5, - (get_req(), - get_reference(), - get_key(), - get_doc(), - get_name()))) - if (sut->get_value()) { - THRIFT_CHECK(, sut->, get_value()->get_type()); - } else { - BOOST_CHECK(!sut2->get_value()); - } - if (sut->get_type()) { - THRIFT_CHECK(, sut->, get_type()->get_name()); - } else { - BOOST_CHECK(!sut2->get_type()); - } -} -void test_struct(t_struct* sut) { - boost::scoped_ptr sut2(round_trip(sut)); - - BOOST_PP_LIST_FOR_EACH(THRIFT_CHECK, - sut->, - BOOST_PP_TUPLE_TO_LIST(5, - (is_union(), - is_xception(), - is_struct(), - get_doc(), - get_name()))) -} - -void test_function(t_function* sut) { - boost::scoped_ptr sut2(round_trip(sut)); - - BOOST_PP_LIST_FOR_EACH( - THRIFT_CHECK, - sut->, - BOOST_PP_TUPLE_TO_LIST(4, (get_doc(), get_name(), get_returntype()->get_name(), is_oneway()))) -} -void test_service(t_service* sut) { - boost::scoped_ptr sut2(round_trip(sut)); - - BOOST_PP_LIST_FOR_EACH(THRIFT_CHECK, - sut->, - BOOST_PP_TUPLE_TO_LIST(3, (get_doc(), get_name(), get_functions().size()))) - if (sut->get_extends()) { - THRIFT_CHECK(, sut->, get_extends()->get_name()); - } else { - BOOST_CHECK(!sut2->get_extends()); - } -} - -void test_program(t_program* sut) { - boost::scoped_ptr sut2(round_trip(sut)); - - BOOST_PP_LIST_FOR_EACH(THRIFT_CHECK, sut->, BOOST_PP_TUPLE_TO_LIST(2, (get_doc(), get_name()))) -} -boost::unit_test::test_suite* do_init_unit_test_suite() { - test_data::init(); - test_suite* ts = BOOST_TEST_SUITE("PluginConversionTest"); - -#define T_TEST_CASE(r, d, type) \ - ts->add(BOOST_PARAM_TEST_CASE(test_##type, test_data::type##s.begin(), test_data::type##s.end())); - BOOST_PP_LIST_FOR_EACH(T_TEST_CASE, _, T_TEST_TYPES) - T_TEST_CASE(_, _, type) -#undef T_TEST_CASE - return ts; -} - -#ifdef BOOST_TEST_DYN_LINK -bool init_unit_test_suite() { - framework::master_test_suite().add(do_init_unit_test_suite()); - 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[]) { - return do_init_unit_test_suite(); -} -#endif diff --git a/compiler/cpp/tests/CMakeLists.txt b/compiler/cpp/tests/CMakeLists.txt index e2b100c7a4b..924e1675c26 100644 --- a/compiler/cpp/tests/CMakeLists.txt +++ b/compiler/cpp/tests/CMakeLists.txt @@ -27,7 +27,8 @@ set(THRIFT_COMPILER_SOURCE_DIR # don't generate ZERO_CHECK set(CMAKE_SUPPRESS_REGENERATION true) -configure_file(${THRIFT_COMPILER_SOURCE_DIR}/src/thrift/version.h.in ${CMAKE_CURRENT_BINARY_DIR}/thrift/version.h) +# version.h now handled via veralign.sh +#configure_file(${THRIFT_COMPILER_SOURCE_DIR}/src/thrift/version.h.in ${CMAKE_CURRENT_BINARY_DIR}/thrift/version.h) if(MSVC) # The winflexbison generator outputs some macros that conflict with the Visual Studio 2010 copy of stdint.h # This might be fixed in later versions of Visual Studio, but an easy solution is to include stdint.h first @@ -77,7 +78,7 @@ set(thrift_compiler_SOURCES ${THRIFT_COMPILER_SOURCE_DIR}/src/thrift/generate/t_generator.cc ${THRIFT_COMPILER_SOURCE_DIR}/src/thrift/parse/t_typedef.cc ${THRIFT_COMPILER_SOURCE_DIR}/src/thrift/parse/parse.cc - ${CMAKE_CURRENT_BINARY_DIR}/thrift/version.h + ${THRIFT_COMPILER_SOURCE_DIR}/thrift/version.h ) # This macro adds an option THRIFT_COMPILER_${NAME} @@ -96,36 +97,35 @@ macro(THRIFT_ADD_COMPILER name description initial) endmacro() # The following compiler with unit tests can be enabled or disabled +THRIFT_ADD_COMPILER(as3 "Enable compiler for ActionScript 3" OFF) THRIFT_ADD_COMPILER(c_glib "Enable compiler for C with Glib" OFF) +THRIFT_ADD_COMPILER(cl "Enable compiler for Common LISP" OFF) THRIFT_ADD_COMPILER(cpp "Enable compiler for C++" OFF) -THRIFT_ADD_COMPILER(java "Enable compiler for Java" OFF) -THRIFT_ADD_COMPILER(as3 "Enable compiler for ActionScript 3" OFF) +THRIFT_ADD_COMPILER(d "Enable compiler for D" OFF) THRIFT_ADD_COMPILER(dart "Enable compiler for Dart" OFF) -THRIFT_ADD_COMPILER(haxe "Enable compiler for Haxe" OFF) -THRIFT_ADD_COMPILER(csharp "Enable compiler for C#" OFF) -THRIFT_ADD_COMPILER(netcore "Enable compiler for .NET Core" ON) -THRIFT_ADD_COMPILER(py "Enable compiler for Python 2.0" OFF) -THRIFT_ADD_COMPILER(rb "Enable compiler for Ruby" OFF) -THRIFT_ADD_COMPILER(perl "Enable compiler for Perl" OFF) -THRIFT_ADD_COMPILER(php "Enable compiler for PHP" OFF) +THRIFT_ADD_COMPILER(delphi "Enable compiler for Delphi" OFF) THRIFT_ADD_COMPILER(erl "Enable compiler for Erlang" OFF) -THRIFT_ADD_COMPILER(cocoa "Enable compiler for Cocoa Objective-C" OFF) -THRIFT_ADD_COMPILER(swift "Enable compiler for Cocoa Swift" OFF) -THRIFT_ADD_COMPILER(st "Enable compiler for Smalltalk" OFF) -THRIFT_ADD_COMPILER(ocaml "Enable compiler for OCaml" OFF) +THRIFT_ADD_COMPILER(go "Enable compiler for Go" OFF) +THRIFT_ADD_COMPILER(gv "Enable compiler for GraphViz" OFF) +THRIFT_ADD_COMPILER(haxe "Enable compiler for Haxe" OFF) THRIFT_ADD_COMPILER(hs "Enable compiler for Haskell" OFF) -THRIFT_ADD_COMPILER(xsd "Enable compiler for XSD" OFF) THRIFT_ADD_COMPILER(html "Enable compiler for HTML Documentation" OFF) +THRIFT_ADD_COMPILER(java "Enable compiler for Java" OFF) +THRIFT_ADD_COMPILER(javame "Enable compiler for Java ME" OFF) THRIFT_ADD_COMPILER(js "Enable compiler for JavaScript" OFF) THRIFT_ADD_COMPILER(json "Enable compiler for JSON" OFF) -THRIFT_ADD_COMPILER(javame "Enable compiler for Java ME" OFF) -THRIFT_ADD_COMPILER(delphi "Enable compiler for Delphi" OFF) -THRIFT_ADD_COMPILER(go "Enable compiler for Go" OFF) -THRIFT_ADD_COMPILER(d "Enable compiler for D" OFF) THRIFT_ADD_COMPILER(lua "Enable compiler for Lua" OFF) -THRIFT_ADD_COMPILER(gv "Enable compiler for GraphViz" OFF) +THRIFT_ADD_COMPILER(netstd "Enable compiler for .NET Standard" ON) +THRIFT_ADD_COMPILER(ocaml "Enable compiler for OCaml" OFF) +THRIFT_ADD_COMPILER(perl "Enable compiler for Perl" OFF) +THRIFT_ADD_COMPILER(php "Enable compiler for PHP" OFF) +THRIFT_ADD_COMPILER(py "Enable compiler for Python 2.0" OFF) +THRIFT_ADD_COMPILER(rb "Enable compiler for Ruby" OFF) THRIFT_ADD_COMPILER(rs "Enable compiler for Rust" OFF) +THRIFT_ADD_COMPILER(st "Enable compiler for Smalltalk" OFF) +THRIFT_ADD_COMPILER(swift "Enable compiler for Swift" OFF) THRIFT_ADD_COMPILER(xml "Enable compiler for XML" OFF) +THRIFT_ADD_COMPILER(xsd "Enable compiler for XSD" OFF) # Thrift is looking for include files in the src directory # we also add the current binary directory for generated files @@ -150,4 +150,4 @@ set_target_properties(thrift_compiler_tests PROPERTIES OUTPUT_NAME thrift_compil target_link_libraries(thrift_compiler_tests thrift_compiler) enable_testing() -add_test(NAME ThriftTests COMMAND thrift_compiler_tests) \ No newline at end of file +add_test(NAME ThriftTests COMMAND thrift_compiler_tests) diff --git a/compiler/cpp/tests/README.md b/compiler/cpp/tests/README.md index 27be491cbcf..91c0625a340 100644 --- a/compiler/cpp/tests/README.md +++ b/compiler/cpp/tests/README.md @@ -16,7 +16,7 @@ ## General information -Added generic way to cover code by tests for many languages (you just need to make a correct header file for generator for your language - example in **netcore** implementation) +Added generic way to cover code by tests for many languages (you just need to make a correct header file for generator for your language - example in **netstd** implementation) At current moment these tests use free Catch library (https://github.com/catchorg/Catch2/tree/Catch1.x) for easy test creation and usage. Decision to use it was because of simplicity, easy usage, one header file to use, stable community and growing interest (https://cpp.libhunt.com/project/googletest-google/vs/catch?rel=cmp-cmp) @@ -29,7 +29,7 @@ Also, maybe, later it will be migrated to Catch2 (https://github.com/philsquared - Set **On** to call of **THRIFT_ADD_COMPILER** for your language ``` cmake -THRIFT_ADD_COMPILER(netcore "Enable compiler for .NET Core" ON) +THRIFT_ADD_COMPILER(netstd "Enable compiler for .NET Standard" ON) ``` - Create folder with name specified in list of languages in **CMakeLists.txt** diff --git a/compiler/cpp/tests/netcore/t_netcore_generator_functional_tests.cc b/compiler/cpp/tests/netcore/t_netcore_generator_functional_tests.cc index 0b8c8378e4d..a2f0a502d93 100644 --- a/compiler/cpp/tests/netcore/t_netcore_generator_functional_tests.cc +++ b/compiler/cpp/tests/netcore/t_netcore_generator_functional_tests.cc @@ -17,18 +17,18 @@ #include "../catch/catch.hpp" #include -#include -#include "t_netcore_generator_functional_tests_helpers.h" +#include +#include "t_netstd_generator_functional_tests_helpers.h" -TEST_CASE( "t_netcore_generator should generate valid enum", "[functional]" ) +TEST_CASE( "t_netstd_generator should generate valid enum", "[functional]" ) { string path = "CassandraTest.thrift"; - string name = "netcore"; + string name = "netstd"; map parsed_options = { { "wcf", "wcf" } }; string option_string = ""; t_program* program = new t_program(path, name); - t_netcore_generator* gen = new t_netcore_generator(program, parsed_options, option_string); + t_netstd_generator* gen = new t_netstd_generator(program, parsed_options, option_string); std::pair pair = TestDataGenerator::get_test_enum_data(program); string expected_result = pair.first; @@ -53,15 +53,15 @@ TEST_CASE( "t_netcore_generator should generate valid enum", "[functional]" ) delete program; } -TEST_CASE("t_netcore_generator should generate valid void", "[functional]") +TEST_CASE("t_netstd_generator should generate valid void", "[functional]") { string path = "CassandraTest.thrift"; - string name = "netcore"; + string name = "netstd"; map parsed_options = { { "wcf", "wcf" } }; string option_string = ""; t_program* program = new t_program(path, name); - t_netcore_generator* gen = new t_netcore_generator(program, parsed_options, option_string); + t_netstd_generator* gen = new t_netstd_generator(program, parsed_options, option_string); std::pair pair = TestDataGenerator::get_test_void_const_data(gen); string expected_result = pair.first; @@ -86,15 +86,15 @@ TEST_CASE("t_netcore_generator should generate valid void", "[functional]") delete program; } -TEST_CASE("t_netcore_generator should generate valid string with escaping keyword", "[functional]") +TEST_CASE("t_netstd_generator should generate valid string with escaping keyword", "[functional]") { string path = "CassandraTest.thrift"; - string name = "netcore"; + string name = "netstd"; map parsed_options = { { "wcf", "wcf" } }; string option_string = ""; t_program* program = new t_program(path, name); - t_netcore_generator* gen = new t_netcore_generator(program, parsed_options, option_string); + t_netstd_generator* gen = new t_netstd_generator(program, parsed_options, option_string); gen->init_generator(); std::pair pair = TestDataGenerator::get_test_string_const_data(gen); @@ -122,15 +122,15 @@ TEST_CASE("t_netcore_generator should generate valid string with escaping keywor delete program; } -TEST_CASE("t_netcore_generator should generate valid bool with escaping keyword", "[functional]") +TEST_CASE("t_netstd_generator should generate valid bool with escaping keyword", "[functional]") { string path = "CassandraTest.thrift"; - string name = "netcore"; + string name = "netstd"; map parsed_options = { { "wcf", "wcf" } }; string option_string = ""; t_program* program = new t_program(path, name); - t_netcore_generator* gen = new t_netcore_generator(program, parsed_options, option_string); + t_netstd_generator* gen = new t_netstd_generator(program, parsed_options, option_string); gen->init_generator(); std::pair pair = TestDataGenerator::get_test_bool_const_data(gen); @@ -158,15 +158,15 @@ TEST_CASE("t_netcore_generator should generate valid bool with escaping keyword" delete program; } -TEST_CASE("t_netcore_generator should generate valid sbyte (i8) with escaping keyword", "[functional]") +TEST_CASE("t_netstd_generator should generate valid sbyte (i8) with escaping keyword", "[functional]") { string path = "CassandraTest.thrift"; - string name = "netcore"; + string name = "netstd"; map parsed_options = { { "wcf", "wcf" } }; string option_string = ""; t_program* program = new t_program(path, name); - t_netcore_generator* gen = new t_netcore_generator(program, parsed_options, option_string); + t_netstd_generator* gen = new t_netstd_generator(program, parsed_options, option_string); gen->init_generator(); std::pair pair = TestDataGenerator::get_test_i8_const_data(gen); @@ -194,15 +194,15 @@ TEST_CASE("t_netcore_generator should generate valid sbyte (i8) with escaping ke delete program; } -TEST_CASE("t_netcore_generator should generate valid short (i16) with escaping keyword", "[functional]") +TEST_CASE("t_netstd_generator should generate valid short (i16) with escaping keyword", "[functional]") { string path = "CassandraTest.thrift"; - string name = "netcore"; + string name = "netstd"; map parsed_options = { { "wcf", "wcf" } }; string option_string = ""; t_program* program = new t_program(path, name); - t_netcore_generator* gen = new t_netcore_generator(program, parsed_options, option_string); + t_netstd_generator* gen = new t_netstd_generator(program, parsed_options, option_string); gen->init_generator(); std::pair pair = TestDataGenerator::get_test_i16_const_data(gen); @@ -230,15 +230,15 @@ TEST_CASE("t_netcore_generator should generate valid short (i16) with escaping k delete program; } -TEST_CASE("t_netcore_generator should generate valid integer (i32) with escaping keyword", "[functional]") +TEST_CASE("t_netstd_generator should generate valid integer (i32) with escaping keyword", "[functional]") { string path = "CassandraTest.thrift"; - string name = "netcore"; + string name = "netstd"; map parsed_options = { { "wcf", "wcf" } }; string option_string = ""; t_program* program = new t_program(path, name); - t_netcore_generator* gen = new t_netcore_generator(program, parsed_options, option_string); + t_netstd_generator* gen = new t_netstd_generator(program, parsed_options, option_string); gen->init_generator(); std::pair pair = TestDataGenerator::get_test_i32_const_data(gen); @@ -266,15 +266,15 @@ TEST_CASE("t_netcore_generator should generate valid integer (i32) with escaping delete program; } -TEST_CASE("t_netcore_generator should generate valid long (i64) with escaping keyword", "[functional]") +TEST_CASE("t_netstd_generator should generate valid long (i64) with escaping keyword", "[functional]") { string path = "CassandraTest.thrift"; - string name = "netcore"; + string name = "netstd"; map parsed_options = { { "wcf", "wcf" } }; string option_string = ""; t_program* program = new t_program(path, name); - t_netcore_generator* gen = new t_netcore_generator(program, parsed_options, option_string); + t_netstd_generator* gen = new t_netstd_generator(program, parsed_options, option_string); gen->init_generator(); std::pair pair = TestDataGenerator::get_test_i64_const_data(gen); @@ -302,15 +302,15 @@ TEST_CASE("t_netcore_generator should generate valid long (i64) with escaping ke delete program; } -TEST_CASE("t_netcore_generator should generate valid double with escaping keyword", "[functional]") +TEST_CASE("t_netstd_generator should generate valid double with escaping keyword", "[functional]") { string path = "CassandraTest.thrift"; - string name = "netcore"; + string name = "netstd"; map parsed_options = { { "wcf", "wcf" } }; string option_string = ""; t_program* program = new t_program(path, name); - t_netcore_generator* gen = new t_netcore_generator(program, parsed_options, option_string); + t_netstd_generator* gen = new t_netstd_generator(program, parsed_options, option_string); gen->init_generator(); std::pair pair = TestDataGenerator::get_test_double_const_data(gen); diff --git a/compiler/cpp/tests/netcore/t_netcore_generator_functional_tests_helpers.cc b/compiler/cpp/tests/netcore/t_netcore_generator_functional_tests_helpers.cc index 92c170bb947..db4ce78ae1b 100644 --- a/compiler/cpp/tests/netcore/t_netcore_generator_functional_tests_helpers.cc +++ b/compiler/cpp/tests/netcore/t_netcore_generator_functional_tests_helpers.cc @@ -17,8 +17,8 @@ #include #include "thrift/common.h" -#include -#include "t_netcore_generator_functional_tests_helpers.h" +#include +#include "t_netstd_generator_functional_tests_helpers.h" const string TestDataGenerator::DEFAULT_FILE_HEADER = "/**" "\n" " * Autogenerated by Thrift Compiler ()" "\n" @@ -52,7 +52,7 @@ std::pair TestDataGenerator::get_test_enum_data(t_program* prog return std::pair(expected_result, enum_); } -std::pair TestDataGenerator::get_test_void_const_data(t_netcore_generator* gen) +std::pair TestDataGenerator::get_test_void_const_data(t_netstd_generator* gen) { string expected_result = DEFAULT_FILE_HEADER; @@ -68,11 +68,11 @@ std::pair TestDataGenerator::get_test_void_const_data(t_netcor return std::pair(expected_result, const_); } -std::pair TestDataGenerator::get_test_string_const_data(t_netcore_generator* gen) +std::pair TestDataGenerator::get_test_string_const_data(t_netstd_generator* gen) { - string expected_result = DEFAULT_FILE_HEADER + "\n" +gen->netcore_type_usings() + + string expected_result = DEFAULT_FILE_HEADER + "\n" +gen->netstd_type_usings() + "\n" - "public static class netcoreConstants\n" + "public static class netstdConstants\n" "{\n" " /// \n" " /// TestDoc\n" @@ -92,11 +92,11 @@ std::pair TestDataGenerator::get_test_string_const_data(t_netc return std::pair(expected_result, const_); } -std::pair TestDataGenerator::get_test_bool_const_data(t_netcore_generator* gen) +std::pair TestDataGenerator::get_test_bool_const_data(t_netstd_generator* gen) { - string expected_result = DEFAULT_FILE_HEADER + "\n" +gen->netcore_type_usings() + + string expected_result = DEFAULT_FILE_HEADER + "\n" +gen->netstd_type_usings() + "\n" - "public static class netcoreConstants\n" + "public static class netstdConstants\n" "{\n" " /// \n" " /// TestDoc\n" @@ -116,11 +116,11 @@ std::pair TestDataGenerator::get_test_bool_const_data(t_netcor return std::pair(expected_result, const_); } -std::pair TestDataGenerator::get_test_i8_const_data(t_netcore_generator* gen) +std::pair TestDataGenerator::get_test_i8_const_data(t_netstd_generator* gen) { - string expected_result = DEFAULT_FILE_HEADER + "\n" +gen->netcore_type_usings() + + string expected_result = DEFAULT_FILE_HEADER + "\n" +gen->netstd_type_usings() + "\n" - "public static class netcoreConstants\n" + "public static class netstdConstants\n" "{\n" " /// \n" " /// TestDoc\n" @@ -140,11 +140,11 @@ std::pair TestDataGenerator::get_test_i8_const_data(t_netcore_ return std::pair(expected_result, const_); } -std::pair TestDataGenerator::get_test_i16_const_data(t_netcore_generator* gen) +std::pair TestDataGenerator::get_test_i16_const_data(t_netstd_generator* gen) { - string expected_result = DEFAULT_FILE_HEADER + "\n" +gen->netcore_type_usings() + + string expected_result = DEFAULT_FILE_HEADER + "\n" +gen->netstd_type_usings() + "\n" - "public static class netcoreConstants\n" + "public static class netstdConstants\n" "{\n" " /// \n" " /// TestDoc\n" @@ -164,11 +164,11 @@ std::pair TestDataGenerator::get_test_i16_const_data(t_netcore return std::pair(expected_result, const_); } -std::pair TestDataGenerator::get_test_i32_const_data(t_netcore_generator* gen) +std::pair TestDataGenerator::get_test_i32_const_data(t_netstd_generator* gen) { - string expected_result = DEFAULT_FILE_HEADER + "\n" +gen->netcore_type_usings() + + string expected_result = DEFAULT_FILE_HEADER + "\n" +gen->netstd_type_usings() + "\n" - "public static class netcoreConstants\n" + "public static class netstdConstants\n" "{\n" " /// \n" " /// TestDoc\n" @@ -188,11 +188,11 @@ std::pair TestDataGenerator::get_test_i32_const_data(t_netcore return std::pair(expected_result, const_); } -std::pair TestDataGenerator::get_test_i64_const_data(t_netcore_generator* gen) +std::pair TestDataGenerator::get_test_i64_const_data(t_netstd_generator* gen) { - string expected_result = DEFAULT_FILE_HEADER + "\n" +gen->netcore_type_usings() + + string expected_result = DEFAULT_FILE_HEADER + "\n" +gen->netstd_type_usings() + "\n" - "public static class netcoreConstants\n" + "public static class netstdConstants\n" "{\n" " /// \n" " /// TestDoc\n" @@ -212,11 +212,11 @@ std::pair TestDataGenerator::get_test_i64_const_data(t_netcore return std::pair(expected_result, const_); } -std::pair TestDataGenerator::get_test_double_const_data(t_netcore_generator* gen) +std::pair TestDataGenerator::get_test_double_const_data(t_netstd_generator* gen) { - string expected_result = DEFAULT_FILE_HEADER + "\n" +gen->netcore_type_usings() + + string expected_result = DEFAULT_FILE_HEADER + "\n" +gen->netstd_type_usings() + "\n" - "public static class netcoreConstants\n" + "public static class netstdConstants\n" "{\n" " /// \n" " /// TestDoc\n" diff --git a/compiler/cpp/tests/netcore/t_netcore_generator_functional_tests_helpers.h b/compiler/cpp/tests/netcore/t_netcore_generator_functional_tests_helpers.h index c6eaac22cb8..982919799d7 100644 --- a/compiler/cpp/tests/netcore/t_netcore_generator_functional_tests_helpers.h +++ b/compiler/cpp/tests/netcore/t_netcore_generator_functional_tests_helpers.h @@ -23,12 +23,12 @@ class TestDataGenerator static const string DEFAULT_FILE_HEADER; static std::pair get_test_enum_data(t_program* program); - static std::pair get_test_void_const_data(t_netcore_generator* gen); - static std::pair get_test_string_const_data(t_netcore_generator* gen); - static std::pair get_test_bool_const_data(t_netcore_generator* gen); - static std::pair get_test_i8_const_data(t_netcore_generator* gen); - static std::pair get_test_i16_const_data(t_netcore_generator* gen); - static std::pair get_test_i32_const_data(t_netcore_generator* gen); - static std::pair get_test_i64_const_data(t_netcore_generator* gen); - static std::pair get_test_double_const_data(t_netcore_generator* gen); + static std::pair get_test_void_const_data(t_netstd_generator* gen); + static std::pair get_test_string_const_data(t_netstd_generator* gen); + static std::pair get_test_bool_const_data(t_netstd_generator* gen); + static std::pair get_test_i8_const_data(t_netstd_generator* gen); + static std::pair get_test_i16_const_data(t_netstd_generator* gen); + static std::pair get_test_i32_const_data(t_netstd_generator* gen); + static std::pair get_test_i64_const_data(t_netstd_generator* gen); + static std::pair get_test_double_const_data(t_netstd_generator* gen); }; diff --git a/compiler/cpp/tests/netcore/t_netcore_generator_helpers_tests.cc b/compiler/cpp/tests/netcore/t_netcore_generator_helpers_tests.cc index 0bcbeed197d..6acedc0f345 100644 --- a/compiler/cpp/tests/netcore/t_netcore_generator_helpers_tests.cc +++ b/compiler/cpp/tests/netcore/t_netcore_generator_helpers_tests.cc @@ -17,14 +17,14 @@ #include "../catch/catch.hpp" #include -#include +#include using std::vector; -TEST_CASE("t_netcore_generator::netcore_type_usings() without option wcf should return valid namespaces", "[helpers]") +TEST_CASE("t_netstd_generator::netstd_type_usings() without option wcf should return valid namespaces", "[helpers]") { string path = "CassandraTest.thrift"; - string name = "netcore"; + string name = "netstd"; map parsed_options = { { "union", "union" } }; string option_string = ""; @@ -39,19 +39,19 @@ TEST_CASE("t_netcore_generator::netcore_type_usings() without option wcf should "using Thrift.Collections;\n" + endl; t_program* program = new t_program(path, name); - t_netcore_generator* gen = new t_netcore_generator(program, parsed_options, option_string); + t_netstd_generator* gen = new t_netstd_generator(program, parsed_options, option_string); REQUIRE_FALSE(gen->is_wcf_enabled()); - REQUIRE(gen->netcore_type_usings() == expected_namespaces); + REQUIRE(gen->netstd_type_usings() == expected_namespaces); delete gen; delete program; } -TEST_CASE("t_netcore_generator::netcore_type_usings() with option wcf should return valid namespaces", "[helpers]") +TEST_CASE("t_netstd_generator::netstd_type_usings() with option wcf should return valid namespaces", "[helpers]") { string path = "CassandraTest.thrift"; - string name = "netcore"; + string name = "netstd"; map parsed_options = { { "wcf", "wcf" } }; string option_string = ""; @@ -68,19 +68,19 @@ TEST_CASE("t_netcore_generator::netcore_type_usings() with option wcf should ret "using System.Runtime.Serialization;\n" + endl; t_program* program = new t_program(path, name); - t_netcore_generator* gen = new t_netcore_generator(program, parsed_options, option_string); + t_netstd_generator* gen = new t_netstd_generator(program, parsed_options, option_string); REQUIRE(gen->is_wcf_enabled()); - REQUIRE(gen->netcore_type_usings() == expected_namespaces_wcf); + REQUIRE(gen->netstd_type_usings() == expected_namespaces_wcf); delete gen; delete program; } -TEST_CASE("t_netcore_generator should contains latest C# keywords to normalize with @", "[helpers]") +TEST_CASE("t_netstd_generator should contains latest C# keywords to normalize with @", "[helpers]") { string path = "CassandraTest.thrift"; - string name = "netcore"; + string name = "netstd"; map parsed_options = { { "wcf", "wcf" } }; string option_string = ""; vector current_keywords = { @@ -190,7 +190,7 @@ TEST_CASE("t_netcore_generator should contains latest C# keywords to normalize w string missed_keywords = ""; t_program* program = new t_program(path, name); - t_netcore_generator* gen = new t_netcore_generator(program, parsed_options, option_string); + t_netstd_generator* gen = new t_netstd_generator(program, parsed_options, option_string); gen->init_generator(); map generators_keywords = gen->get_keywords_list(); diff --git a/compiler/cpp/tests/netcore/t_netcore_generator_initialization_tests.cc b/compiler/cpp/tests/netcore/t_netcore_generator_initialization_tests.cc index ec17733bd22..530e290ac60 100644 --- a/compiler/cpp/tests/netcore/t_netcore_generator_initialization_tests.cc +++ b/compiler/cpp/tests/netcore/t_netcore_generator_initialization_tests.cc @@ -17,35 +17,35 @@ #include "../catch/catch.hpp" #include -#include +#include -TEST_CASE( "t_netcore_generator should throw error with unknown options", "[initialization]" ) +TEST_CASE( "t_netstd_generator should throw error with unknown options", "[initialization]" ) { string path = "CassandraTest.thrift"; - string name = "netcore"; + string name = "netstd"; map parsed_options = { { "keys", "keys" } }; string option_string = ""; t_program* program = new t_program(path, name); - t_netcore_generator* gen = nullptr; + t_netstd_generator* gen = nullptr; - REQUIRE_THROWS(gen = new t_netcore_generator(program, parsed_options, option_string)); + REQUIRE_THROWS(gen = new t_netstd_generator(program, parsed_options, option_string)); delete gen; delete program; } -TEST_CASE("t_netcore_generator should create valid instance with valid options", "[initialization]") +TEST_CASE("t_netstd_generator should create valid instance with valid options", "[initialization]") { string path = "CassandraTest.thrift"; - string name = "netcore"; + string name = "netstd"; map parsed_options = { { "wcf", "wcf" }, { "nullable", "nullable"} }; string option_string = ""; t_program* program = new t_program(path, name); - t_netcore_generator* gen = nullptr; + t_netstd_generator* gen = nullptr; - REQUIRE_NOTHROW(gen = new t_netcore_generator(program, parsed_options, option_string)); + REQUIRE_NOTHROW(gen = new t_netstd_generator(program, parsed_options, option_string)); REQUIRE(gen != nullptr); REQUIRE(gen->is_wcf_enabled()); REQUIRE(gen->is_nullable_enabled()); @@ -57,15 +57,15 @@ TEST_CASE("t_netcore_generator should create valid instance with valid options", delete program; } -TEST_CASE("t_netcore_generator should pass init succesfully", "[initialization]") +TEST_CASE("t_netstd_generator should pass init succesfully", "[initialization]") { string path = "CassandraTest.thrift"; - string name = "netcore"; + string name = "netstd"; map parsed_options = { { "wcf", "wcf" },{ "nullable", "nullable" } }; string option_string = ""; t_program* program = new t_program(path, name); - t_netcore_generator* gen = new t_netcore_generator(program, parsed_options, option_string); + t_netstd_generator* gen = new t_netstd_generator(program, parsed_options, option_string); REQUIRE_NOTHROW(gen->init_generator()); diff --git a/composer.json b/composer.json index 3695b8c9d5a..1d3a6c0cc51 100644 --- a/composer.json +++ b/composer.json @@ -1,7 +1,7 @@ { "name": "apache/thrift", "description": "Apache Thrift RPC system", - "homepage": "https://thrift.apache.org/", + "homepage": "http://thrift.apache.org", "type": "library", "keywords": ["RPC"], "license": "Apache-2.0", diff --git a/configure.ac b/configure.ac index b07b5de5a90..35f43ee5f2c 100755 --- a/configure.ac +++ b/configure.ac @@ -20,7 +20,7 @@ AC_PREREQ(2.65) AC_CONFIG_MACRO_DIR([./aclocal]) -AC_INIT([thrift], [0.12.0]) +AC_INIT([thrift], [0.14.0]) AC_CONFIG_AUX_DIR([.]) @@ -102,10 +102,7 @@ AC_PROG_AWK AC_PROG_RANLIB AC_LANG([C++]) -AX_CXX_COMPILE_STDCXX_11([noext], [optional]) -if test "$ac_success" = "no"; then - CXXFLAGS="$CXXFLAGS -Wno-variadic-macros -Wno-long-long -Wno-c++11-long-long" -fi +AX_CXX_COMPILE_STDCXX_11([noext], [mandatory]) AM_EXTRA_RECURSIVE_TARGETS([style]) AC_SUBST(CPPSTYLE_CMD, 'find . -type f \( -iname "*.h" -or -iname "*.cpp" -or -iname "*.cc" -or -iname "*.tcc" \) -printf "Reformatting: %h/%f\n" -exec clang-format -i {} \;') @@ -119,17 +116,17 @@ AC_ARG_ENABLE([libs], have_libs=yes if test "$enable_libs" = "no"; then have_libs="no" + with_as3="no" with_cpp="no" with_c_glib="no" with_cl="no" with_java="no" - with_csharp="no" with_python="no" with_py3="no" with_ruby="no" with_haskell="no" with_haxe="no" - with_dotnetcore="no" + with_netstd="no" with_perl="no" with_php="no" with_php_extension="no" @@ -141,12 +138,25 @@ if test "$enable_libs" = "no"; then with_nodets="no" with_lua="no" with_rs="no" + with_swift="no" +fi + +AX_THRIFT_LIB(as3, [as3], yes) +have_as3=no +if test "$with_as3" = "yes"; then + if test "${FLEX_HOME+set}" = set; then + AC_PATH_PROGS([FLEX_COMPC], [compc], "fail", [$PATH$PATH_SEPARATOR$FLEX_HOME/bin]) + if test "$FLEX_COMPC" != "fail"; then + have_as3="yes" + fi + fi fi +AM_CONDITIONAL(WITH_AS3, [test "$have_as3" = "yes"]) AX_THRIFT_LIB(cpp, [C++], yes) have_cpp=no if test "$with_cpp" = "yes"; then - AX_BOOST_BASE([1.53.0]) + AX_BOOST_BASE([1.56.0]) if test "x$succeeded" = "xyes" ; then AC_SUBST([BOOST_LIB_DIR], [$(echo "$BOOST_LDFLAGS" | sed -e 's/^\-L//')]) AC_SUBST([BOOST_CHRONO_LDADD], [$(echo "$BOOST_LIB_DIR/libboost_chrono.a")]) @@ -157,24 +167,12 @@ if test "$with_cpp" = "yes"; then have_cpp="yes" fi - AX_LIB_EVENT([1.0]) + AX_LIB_EVENT([2.0]) have_libevent=$success AX_LIB_ZLIB([1.2.3]) have_zlib=$success - AX_THRIFT_LIB(qt4, [Qt], yes) - have_qt=no - if test "$with_qt4" = "yes"; then - PKG_CHECK_MODULES([QT], [QtCore >= 4.3, QtNetwork >= 4.3], have_qt=yes, have_qt=no) - fi - if test "$have_qt" = "yes"; then - AC_PATH_PROGS([QT_MOC], [moc-qt4 moc], "fail") - if test "$QT_MOC" = "fail"; then - have_qt=no - fi - fi - AX_THRIFT_LIB(qt5, [Qt5], yes) have_qt5=no qt_reduce_reloc="" @@ -193,7 +191,6 @@ fi AM_CONDITIONAL([WITH_CPP], [test "$have_cpp" = "yes"]) AM_CONDITIONAL([AMX_HAVE_LIBEVENT], [test "$have_libevent" = "yes"]) AM_CONDITIONAL([AMX_HAVE_ZLIB], [test "$have_zlib" = "yes"]) -AM_CONDITIONAL([AMX_HAVE_QT], [test "$have_qt" = "yes"]) AM_CONDITIONAL([AMX_HAVE_QT5], [test "$have_qt5" = "yes"]) AM_CONDITIONAL([QT5_REDUCE_RELOCATIONS], [test "x$qt_reduce_reloc" != "x"]) @@ -202,35 +199,18 @@ if test "$with_c_glib" = "yes"; then PKG_CHECK_MODULES([GLIB], [glib-2.0 >= 2.0], have_glib2=yes, have_glib2=no) PKG_CHECK_MODULES([GOBJECT], [gobject-2.0 >= 2.0], have_gobject2=yes, have_gobject2=no) if test "$have_glib2" = "yes" -a "$have_gobject2" = "yes" ; then + AC_PATH_PROG([GSETTINGS], [gsettings]) have_c_glib="yes" fi fi AM_CONDITIONAL(WITH_C_GLIB, [test "$have_glib2" = "yes" -a "$have_gobject2" = "yes"]) -echo "OpenSSL check" +# echo "OpenSSL check" if test "$have_cpp" = "yes" -o "$have_c_glib" = "yes"; then - echo "Have cpp or c so we check for OpenSSL" + # echo "Have cpp or c so we check for OpenSSL" AX_CHECK_OPENSSL() fi -AX_THRIFT_LIB(csharp, [C#], yes) -if test "$with_csharp" = "yes"; then - PKG_CHECK_MODULES(MONO, mono >= 2.11.0, mono_2_11=yes, mono_2_11=no) - if test "$mono_2_11" == "yes"; then - AC_PATH_PROG([MCS], [mcs]) - if test "x$MCS" != "x"; then - mono_mcs="yes" - fi - fi - PKG_CHECK_MODULES(MONO, mono >= 2.0.0, net_3_5=yes, net_3_5=no) - PKG_CHECK_MODULES(MONO, mono >= 1.2.4, have_mono=yes, have_mono=no) - if test "$have_mono" = "yes" ; then - have_csharp="yes" - fi -fi -AM_CONDITIONAL(WITH_MONO, [test "$have_csharp" = "yes"]) -AM_CONDITIONAL(NET_2_0, [test "$net_3_5" = "no"]) -AM_CONDITIONAL(MONO_MCS, [test "$mono_mcs" = "yes"]) AX_THRIFT_LIB(java, [Java], yes) if test "$with_java" = "yes"; then @@ -250,7 +230,7 @@ AX_THRIFT_LIB(erlang, [Erlang], yes) if test "$with_erlang" = "yes"; then AC_ERLANG_PATH_ERL AC_ERLANG_PATH_ERLC - AC_PATH_PROG([REBAR], [rebar]) + AC_PATH_PROG([REBAR], [rebar3]) if test -n "$ERLC" ; then AC_ERLANG_SUBST_LIB_DIR # Install into the detected Erlang directory instead of $libdir/erlang/lib @@ -324,10 +304,17 @@ AM_CONDITIONAL(WITH_TWISTED_TEST, [test "$have_trial" = "yes"]) # It's distro specific and far from ideal but needed to cross test py2-3 at once. # TODO: find "python2" if it's 3.x have_py3="no" -if python --version 2>&1 | grep -q "Python 2"; then - AC_PATH_PROGS([PYTHON3], [python3 python3.5 python35 python3.4 python34]) - if test -n "$PYTHON3"; then +AX_THRIFT_LIB(py3, [Py3], yes) +if test "$with_py3" = "yes"; then + # if $PYTHON is 2.x then search for python 3. otherwise, $PYTHON is already 3.x + if $PYTHON --version 2>&1 | grep -q "Python 2"; then + AC_PATH_PROGS([PYTHON3], [python3 python3.8 python38 python3.7 python37 python3.6 python36 python3.5 python35 python3.4 python34]) + if test -n "$PYTHON3"; then + have_py3="yes" + fi + elif $PYTHON --version 2>&1 | grep -q "Python 3"; then have_py3="yes" + PYTHON3=$PYTHON fi fi AM_CONDITIONAL(WITH_PY3, [test "$have_py3" = "yes"]) @@ -445,6 +432,16 @@ fi AM_CONDITIONAL(WITH_GO, [test "$have_go" = "yes"]) AM_CONDITIONAL([GOVERSION_LT_17], [test "$go_version_lt_17" = "yes"]) +AX_THRIFT_LIB(swift, [Swift], yes) +have_swift="no" +if test "$with_swift" = "yes"; then + AC_PATH_PROG([SWIFT], [swift]) + if test "x$SWIFT" != "x" -a "x$SWIFT" != "x"; then + have_swift="yes" + fi +fi +AM_CONDITIONAL([WITH_SWIFT], [test "$have_swift" = "yes"]) + AX_THRIFT_LIB(rs, [Rust], yes) have_rs="no" if test "$with_rs" = "yes"; then @@ -489,14 +486,14 @@ fi AM_CONDITIONAL(WITH_HAXE, [test "$have_haxe" = "yes"]) -AX_THRIFT_LIB(dotnetcore, [.NET Core], yes) -if test "$with_dotnetcore" = "yes"; then +AX_THRIFT_LIB(netstd, [.NET Core], yes) +if test "$with_netstd" = "yes"; then AC_PATH_PROG([DOTNETCORE], [dotnet]) if [[ -x "$DOTNETCORE" ]] ; then - AX_PROG_DOTNETCORE_VERSION( [2.0.0], have_dotnetcore="yes", have_dotnetcore="no") + AX_PROG_DOTNETCORE_VERSION( [3.1.0], have_netstd="yes", have_netstd="no") fi fi -AM_CONDITIONAL(WITH_DOTNETCORE, [test "$have_dotnetcore" = "yes"]) +AM_CONDITIONAL(WITH_DOTNET, [test "$have_netstd" = "yes"]) AX_THRIFT_LIB(d, [D], yes) @@ -595,20 +592,6 @@ if test "$enable_tests" = "no"; then fi AM_CONDITIONAL(WITH_TESTS, [test "$have_tests" = "yes"]) -AC_ARG_ENABLE([plugin], - AS_HELP_STRING([--enable-plugin], [build compiler plugin support [default=no]]), - [], enable_plugin=no -) -have_plugin=yes -if test "$have_cpp" = "no" ; then - have_plugin="no" -fi -if test "$enable_plugin" = "no"; then - have_plugin="no" -fi -AC_CONFIG_LINKS([compiler/cpp/test/plugin/t_cpp_generator.cc:compiler/cpp/src/thrift/generate/t_cpp_generator.cc]) -AM_CONDITIONAL(WITH_PLUGIN, [test "$have_plugin" = "yes"]) - AC_ARG_ENABLE([tutorial], AS_HELP_STRING([--enable-tutorial], [build tutorial [default=yes]]), [], enable_tutorial=yes @@ -774,28 +757,10 @@ AC_SUBST(GCOV_CFLAGS) AC_SUBST(GCOV_CXXFLAGS) AC_SUBST(GCOV_LDFLAGS) -AC_ARG_ENABLE(boostthreads, - [ --enable-boostthreads use boost threads, instead of POSIX pthread (experimental) ], - [case "${enableval}" in - yes) ENABLE_BOOSTTHREADS=1 ;; - no) ENABLE_BOOSTTHREADS=0 ;; - *) AC_MSG_ERROR(bad value ${enableval} for --enable-cov) ;; - esac], - [ENABLE_BOOSTTHREADS=2]) - - -if test "x[$]ENABLE_BOOSTTHREADS" = "x1"; then - AC_MSG_WARN(enable boostthreads) - AC_DEFINE([USE_BOOST_THREAD], [1], [experimental --enable-boostthreads that replaces POSIX pthread by boost::thread]) - LIBS="-lboost_thread $LIBS" -fi - -AM_CONDITIONAL([WITH_BOOSTTHREADS], [test "x[$]ENABLE_BOOSTTHREADS" = "x1"]) - AC_CONFIG_HEADERS(config.h:config.hin) AC_CONFIG_HEADERS(lib/cpp/src/thrift/config.h:config.hin) AC_CONFIG_HEADERS(lib/c_glib/src/thrift/config.h:config.hin) -# gruard against pre defined config.h +# guard against pre defined config.h AH_TOP([ #ifndef CONFIG_H #define CONFIG_H @@ -809,23 +774,19 @@ AC_CONFIG_FILES([ Makefile compiler/cpp/Makefile compiler/cpp/src/Makefile - compiler/cpp/src/thrift/plugin/Makefile compiler/cpp/test/Makefile - compiler/cpp/src/thrift/version.h lib/Makefile + lib/as3/Makefile lib/cl/Makefile lib/cpp/Makefile lib/cpp/test/Makefile lib/cpp/thrift-nb.pc lib/cpp/thrift-z.pc - lib/cpp/thrift-qt.pc lib/cpp/thrift-qt5.pc lib/cpp/thrift.pc lib/c_glib/Makefile lib/c_glib/thrift_c_glib.pc lib/c_glib/test/Makefile - lib/csharp/Makefile - lib/csharp/test/Multiplex/Makefile lib/d/Makefile lib/d/test/Makefile lib/erl/Makefile @@ -838,11 +799,11 @@ AC_CONFIG_FILES([ lib/js/test/Makefile lib/json/Makefile lib/json/test/Makefile - lib/netcore/Makefile + lib/netstd/Makefile lib/nodejs/Makefile lib/nodets/Makefile lib/perl/Makefile - lib/perl/test/Makefile + lib/perl/t/Makefile lib/php/Makefile lib/php/test/Makefile lib/dart/Makefile @@ -851,6 +812,8 @@ AC_CONFIG_FILES([ lib/rs/Makefile lib/rs/test/Makefile lib/lua/Makefile + lib/swift/Makefile + lib/ts/Makefile lib/xml/Makefile lib/xml/test/Makefile test/Makefile @@ -858,13 +821,12 @@ AC_CONFIG_FILES([ test/c_glib/Makefile test/cl/Makefile test/cpp/Makefile - test/csharp/Makefile test/erl/Makefile test/go/Makefile test/haxe/Makefile test/hs/Makefile test/lua/Makefile - test/netcore/Makefile + test/netstd/Makefile test/php/Makefile test/dart/Makefile test/perl/Makefile @@ -883,9 +845,11 @@ AC_CONFIG_FILES([ tutorial/hs/Makefile tutorial/java/Makefile tutorial/js/Makefile - tutorial/netcore/Makefile + tutorial/netstd/Makefile tutorial/nodejs/Makefile tutorial/dart/Makefile + tutorial/perl/Makefile + tutorial/php/Makefile tutorial/py/Makefile tutorial/py.twisted/Makefile tutorial/py.tornado/Makefile @@ -893,6 +857,8 @@ AC_CONFIG_FILES([ tutorial/rs/Makefile ]) +if test "$have_as3" = "yes" ; then MAYBE_AS3="as3" ; else MAYBE_AS3="" ; fi +AC_SUBST([MAYBE_AS3]) if test "$have_cpp" = "yes" ; then MAYBE_CPP="cpp" ; else MAYBE_CPP="" ; fi AC_SUBST([MAYBE_CPP]) if test "$have_c_glib" = "yes" ; then MAYBE_C_GLIB="c_glib" ; else MAYBE_C_GLIB="" ; fi @@ -901,8 +867,6 @@ if test "$have_d" = "yes" -a "$have_deimos_event2" = "yes" -a "$have_deimos_open AC_SUBST([MAYBE_D]) if test "$have_java" = "yes" ; then MAYBE_JAVA="java" ; else MAYBE_JAVA="" ; fi AC_SUBST([MAYBE_JAVA]) -if test "$have_csharp" = "yes" ; then MAYBE_CSHARP="csharp" ; else MAYBE_CSHARP="" ; fi -AC_SUBST([MAYBE_CSHARP]) if test "$have_python" = "yes" ; then MAYBE_PYTHON="py" ; else MAYBE_PYTHON="" ; fi AC_SUBST([MAYBE_PYTHON]) if test "$have_py3" = "yes" ; then MAYBE_PY3="py3" ; else MAYBE_PY3="" ; fi @@ -929,8 +893,10 @@ if test "$have_lua" = "yes" ; then MAYBE_LUA="lua" ; else MAYBE_LUA="" ; fi AC_SUBST([MAYBE_LUA]) if test "$have_rs" = "yes" ; then MAYBE_RS="rs" ; else MAYBE_RS="" ; fi AC_SUBST([MAYBE_RS]) -if test "$have_dotnetcore" = "yes" ; then MAYBE_DOTNETCORE="netcore" ; else MAYBE_DOTNETCORE="" ; fi -AC_SUBST([MAYBE_DOTNETCORE]) +if test "$have_swift" = "yes" ; then MAYBE_SWIFT="swift" ; else MAYBE_SWIFT="" ; fi +AC_SUBST([MAYBE_SWIFT]) +if test "$have_netstd" = "yes" ; then MAYBE_NETSTD="netstd" ; else MAYBE_NETSTD="" ; fi +AC_SUBST([MAYBE_NETSTD]) if test "$have_cl" = "yes" ; then MAYBE_CL="cl" ; else MAYBE_CL="" ; fi AC_SUBST([MAYBE_CL]) @@ -940,13 +906,13 @@ AC_OUTPUT echo echo "$PACKAGE $VERSION" echo +echo "Building ActionScript3 Library : $have_as3" echo "Building C (GLib) Library .... : $have_c_glib" -echo "Building C# (Mono) Library ... : $have_csharp" echo "Building C++ Library ......... : $have_cpp" echo "Building Common Lisp Library.. : $have_cl" echo "Building D Library ........... : $have_d" echo "Building Dart Library ........ : $have_dart" -echo "Building dotnetcore Library .. : $have_dotnetcore" +echo "Building .NET Standard Library : $have_netstd" echo "Building Erlang Library ...... : $have_erlang" echo "Building Go Library .......... : $have_go" echo "Building Haskell Library ..... : $have_haskell" @@ -956,17 +922,22 @@ echo "Building Lua Library ......... : $have_lua" echo "Building NodeJS Library ...... : $have_nodejs" echo "Building Perl Library ........ : $have_perl" echo "Building PHP Library ......... : $have_php" -echo "Building Plugin Support ...... : $have_plugin" echo "Building Python Library ...... : $have_python" echo "Building Py3 Library ......... : $have_py3" echo "Building Ruby Library ........ : $have_ruby" echo "Building Rust Library ........ : $have_rs" +echo "Building Swift Library ....... : $have_swift" -if test "$have_csharp" = "yes" ; then +if test "$have_as3" = "yes" ; then echo - echo "C# Library:" - echo " Using .NET 3.5 ............ : $net_3_5" - echo " Using mono version ........ : $($MCS --version | head -1)" + echo "ActionScript Library:" + echo " FLEX_HOME ................. : $FLEX_HOME" + echo " Using compc version ....... : $($FLEX_COMPC --version)" +fi +if test "$have_c_glib" = "yes" ; then + echo + echo "C (glib):" + echo " Using glib version ........ : $($GSETTINGS --version)" fi if test "$have_cpp" = "yes" ; then echo @@ -974,7 +945,6 @@ if test "$have_cpp" = "yes" ; then echo " C++ compiler .............. : $CXX" echo " Build TZlibTransport ...... : $have_zlib" echo " Build TNonblockingServer .. : $have_libevent" - echo " Build TQTcpServer (Qt4) ... : $have_qt" echo " Build TQTcpServer (Qt5) ... : $have_qt5" echo " C++ compiler version ...... : $($CXX --version | head -1)" fi @@ -999,11 +969,11 @@ if test "$have_dart" = "yes" ; then echo " Using Pub ................. : $DARTPUB" echo " Using Dart version ........ : $($DART --version 2>&1)" fi -if test "$have_dotnetcore" = "yes" ; then +if test "$have_netstd" = "yes" ; then echo - echo ".NET Core Library:" - echo " Using .NET Core ........... : $DOTNETCORE" - echo " Using .NET Core version ... : $DOTNETCORE_VERSION" + echo ".NET Standard Library:" + echo " Using dotnet .............. : $DOTNETCORE" + echo " Using dotnet version ...... : $DOTNETCORE_VERSION" fi if test "$have_erlang" = "yes" ; then echo @@ -1090,6 +1060,12 @@ if test "$have_rs" = "yes" ; then echo " Using rustc................ : $RUSTC" echo " Using Rust version......... : $($RUSTC --version)" fi +if test "$have_swift" = "yes" ; then + echo + echo "Swift Library:" + echo " Using Swift ............... : $SWIFT" + echo " Using Swift version ....... : $($SWIFT --version | head -1)" +fi echo echo "If something is missing that you think should be present," echo "please skim the output of configure to find the missing" diff --git a/contrib/Rebus/Properties/AssemblyInfo.cs b/contrib/Rebus/Properties/AssemblyInfo.cs index dde781324bd..e476eab7656 100644 --- a/contrib/Rebus/Properties/AssemblyInfo.cs +++ b/contrib/Rebus/Properties/AssemblyInfo.cs @@ -34,5 +34,5 @@ [assembly: Guid("0af10984-40d3-453d-b1e5-421529e8c7e2")] -[assembly: AssemblyVersion("0.12.0.0")] -[assembly: AssemblyFileVersion("0.12.0.0")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/contrib/Rebus/README.md b/contrib/Rebus/README.md index bbb9c496ea9..7b11bd0ddfc 100644 --- a/contrib/Rebus/README.md +++ b/contrib/Rebus/README.md @@ -19,3 +19,8 @@ refer to the Rebus docs on how to do that (it's not that hard, really). Additional requirements: - RabbitMQ .NET client (see nuget) + +Deprecation notice: +Csharp is not a supported Apache Thrift target anymore. Instead netstd is the +recommended replacement. This code is left "as is" for educational purposes +unless someone converts it to netstd diff --git a/contrib/Vagrantfile b/contrib/Vagrantfile index 3bcc46a96d7..6d2d4203288 100644 --- a/contrib/Vagrantfile +++ b/contrib/Vagrantfile @@ -37,13 +37,13 @@ sudo DEBIAN_FRONTEND=noninteractive apt-get upgrade -qq -y # Install Dependencies # --- # General dependencies -sudo apt-get install -qq automake libtool flex bison pkg-config g++ libssl-dev make libqt4-dev git debhelper +sudo apt-get install -qq automake libtool flex bison pkg-config g++ libssl-dev make git debhelper # C++ dependencies sudo apt-get install -qq libboost-dev libboost-test-dev libboost-program-options-dev libboost-filesystem-dev libboost-system-dev libevent-dev # Java dependencies -sudo apt-get install -qq ant openjdk-7-jdk maven +sudo apt-get install -qq ant openjdk-8-jdk maven # Python dependencies sudo apt-get install -qq python-all python-all-dev python-all-dbg python-setuptools python-support python-six python3-six @@ -78,10 +78,6 @@ sudo apt-get install -qq lua5.2 lua5.2-dev # Node.js dependencies sudo apt-get install -qq nodejs nodejs-dev nodejs-legacy npm -# CSharp -sudo apt-get install -qq mono-gmcs mono-devel mono-xbuild mono-complete libmono-system-web2.0-cil -sudo apt-get install -qq mingw32 mingw32-binutils mingw32-runtime nsis - # D dependencies sudo wget http://master.dl.sourceforge.net/project/d-apt/files/d-apt.list -O /etc/apt/sources.list.d/d-apt.list sudo apt-get update && sudo apt-get -y --allow-unauthenticated install --reinstall d-apt-keyring && sudo apt-get update @@ -89,8 +85,8 @@ sudo apt-get install -qq xdg-utils dmd-bin # Customize the system # --- -# Default java to latest 1.7 version -update-java-alternatives -s java-1.7.0-openjdk-amd64 +# Default java to latest 1.8 version +update-java-alternatives -s java-1.8.0-openjdk-amd64 # PHPUnit package broken in ubuntu. see https://bugs.launchpad.net/ubuntu/+source/phpunit/+bug/701544 sudo apt-get upgrade pear diff --git a/contrib/async-test/test-server.cpp b/contrib/async-test/test-server.cpp index b304e1bc9d8..9e696c92cfb 100644 --- a/contrib/async-test/test-server.cpp +++ b/contrib/async-test/test-server.cpp @@ -28,7 +28,7 @@ class AggrAsyncHandler : public AggrCobSvIf { public: AggrAsyncHandler() - : eb_(NULL) + : eb_(nullptr) , pfact_(new TBinaryProtocolFactory()) { leaf_ports_.push_back(8081); diff --git a/contrib/fb303/TClientInfo.cpp b/contrib/fb303/TClientInfo.cpp index 1fc66125152..a4b00cf2d58 100644 --- a/contrib/fb303/TClientInfo.cpp +++ b/contrib/fb303/TClientInfo.cpp @@ -34,7 +34,7 @@ void TClientInfoConnection::recordAddr(const sockaddr* addr) { eraseAddr(); initTime(); ncalls_ = 0; - if (addr != NULL) { + if (addr != nullptr) { if (addr->sa_family == AF_INET) { memcpy((void*)&addr_.ipv4, (const void *)addr, sizeof(sockaddr_in)); } @@ -55,7 +55,7 @@ const char* TClientInfoConnection::getAddr(char* buf, int len) const { case AF_INET6: return inet_ntop(AF_INET6, &addr_.ipv6.sin6_addr, buf, len); default: - return NULL; + return nullptr; } } @@ -70,7 +70,7 @@ void TClientInfoConnection::eraseCall() { const char* TClientInfoConnection::getCall() const { if (call_[0] == '\0') { - return NULL; + return nullptr; } return call_; } @@ -90,7 +90,7 @@ void TClientInfoConnection::initTime() { TClientInfoConnection* TClientInfo::getConnection(int fd, bool grow) { if (fd < 0 || (!grow && fd >= info_.size())) { - return NULL; + return nullptr; } return &info_[fd]; } @@ -119,7 +119,7 @@ void TClientInfoServerHandler::deleteContext(void* connectionContext, void TClientInfoServerHandler::processContext(void* connectionContext, shared_ptr transport) { Connect* call = static_cast(connectionContext); - if (call->callInfo_ == NULL) { + if (call->callInfo_ == nullptr) { if (typeid(*(transport.get())) == typeid(TSocket)) { TSocket* tsocket = static_cast(transport.get()); int fd = tsocket->getSocketFD(); @@ -127,7 +127,7 @@ void TClientInfoServerHandler::processContext(void* connectionContext, return; } call->callInfo_ = call->clientInfo_->getConnection(fd, true); - assert(call->callInfo_ != NULL); + assert(call->callInfo_ != nullptr); socklen_t len; call->callInfo_->recordAddr(tsocket->getCachedAddress(&len)); } @@ -142,13 +142,13 @@ void TClientInfoServerHandler::getStatsStrings(vector& result) { for (int i = 0; i < clientInfo_.size(); ++i) { TClientInfoConnection* info = clientInfo_.getConnection(i, false); const char* callStr = info->getCall(); - if (callStr == NULL) { + if (callStr == nullptr) { continue; } char addrBuf[INET6_ADDRSTRLEN]; const char* addrStr = info->getAddr(addrBuf, sizeof addrBuf); - if (addrStr == NULL) { + if (addrStr == nullptr) { // cerr << "no addr!" << endl; continue; } @@ -168,11 +168,11 @@ void TClientInfoServerHandler::getStatsStrings(vector& result) { void* TClientInfoCallHandler::getContext(const char* fn_name, void* serverContext) { if (serverContext) { TClientInfoConnection* callInfo = static_cast(serverContext)->callInfo_; - if (callInfo != NULL) { + if (callInfo != nullptr) { callInfo->recordCall(fn_name); } } - return NULL; + return nullptr; } } } } // namespace apache::thrift::server diff --git a/contrib/fb303/TClientInfo.h b/contrib/fb303/TClientInfo.h index 6668c1921ce..e3859a71e50 100644 --- a/contrib/fb303/TClientInfo.h +++ b/contrib/fb303/TClientInfo.h @@ -174,7 +174,7 @@ class TClientInfoConnection { void eraseAddr(); /** - * Return a string representing the present address, or NULL if none. + * Return a string representing the present address, or nullptr if none. * Copies the string into the buffer provided. */ const char* getAddr(char* buf, int len) const; @@ -193,7 +193,7 @@ class TClientInfoConnection { /** * Return as string the thrift call either currently being processed or * most recently processed if the connection is still open for additional - * calls. Returns NULL if a call hasn't been made yet or processing + * calls. Returns nullptr if a call hasn't been made yet or processing * has ended. */ const char* getCall() const; @@ -229,7 +229,7 @@ class TClientInfo { * Return the info object for a given file descriptor. If "grow" is true * extend the info vector if required (such as for a file descriptor not seen * before). If "grow" is false and the info vector isn't large enough, - * or if "fd" is negative, return NULL. + * or if "fd" is negative, return nullptr. */ TClientInfoConnection* getConnection(int fd, bool grow); @@ -258,7 +258,7 @@ class TClientInfoServerHandler : public TServerEventHandler { explicit Connect(TClientInfo* clientInfo) : clientInfo_(clientInfo) - , callInfo_(NULL) { + , callInfo_(nullptr) { } }; diff --git a/contrib/fb303/cpp/FacebookBase.cpp b/contrib/fb303/cpp/FacebookBase.cpp index 3c569759c0b..eb2e63cc514 100644 --- a/contrib/fb303/cpp/FacebookBase.cpp +++ b/contrib/fb303/cpp/FacebookBase.cpp @@ -24,7 +24,7 @@ using apache::thrift::concurrency::Guard; FacebookBase::FacebookBase(std::string name) : name_(name) { - aliveSince_ = (int64_t) time(NULL); + aliveSince_ = (int64_t) time(nullptr); } inline void FacebookBase::getName(std::string& _return) { diff --git a/contrib/fb303/cpp/FacebookBase.h b/contrib/fb303/cpp/FacebookBase.h index daa524644fd..e0be4bf2e2e 100644 --- a/contrib/fb303/cpp/FacebookBase.h +++ b/contrib/fb303/cpp/FacebookBase.h @@ -65,7 +65,7 @@ class FacebookBase : virtual public FacebookServiceIf { virtual void reinitialize() {} virtual void shutdown() { - if (server_.get() != NULL) { + if (server_.get() != nullptr) { server_->stop(); } } diff --git a/contrib/fb303/cpp/ServiceTracker.cpp b/contrib/fb303/cpp/ServiceTracker.cpp index 7a61b21a986..a2e670c768b 100644 --- a/contrib/fb303/cpp/ServiceTracker.cpp +++ b/contrib/fb303/cpp/ServiceTracker.cpp @@ -46,7 +46,7 @@ ServiceTracker::ServiceTracker(facebook::fb303::FacebookBase *handler, checkpointServices_(0) { if (featureCheckpoint_) { - time_t now = time(NULL); + time_t now = time(nullptr); checkpointTime_ = now; } else { checkpointTime_ = 0; @@ -105,7 +105,7 @@ ServiceTracker::startService(const ServiceMethod &serviceMethod) if (featureThreadCheck_ && !serviceMethod.featureLogOnly_) { // note: Might want to put these messages in reportCheckpoint() if // log is getting spammed. - if (threadManager_ != NULL) { + if (threadManager_ != nullptr) { size_t idle_count = threadManager_->idleWorkerCount(); if (idle_count == 0) { stringstream message; @@ -211,7 +211,7 @@ ServiceTracker::finishService(const ServiceMethod &serviceMethod) // maybe report checkpoint // note: ...if it's been long enough since the last report. - time_t now = time(NULL); + time_t now = time(nullptr); uint64_t check_interval = now - checkpointTime_; if (check_interval >= CHECKPOINT_MINIMUM_INTERVAL_SECONDS) { reportCheckpoint(); @@ -239,7 +239,7 @@ ServiceTracker::finishService(const ServiceMethod &serviceMethod) void ServiceTracker::reportCheckpoint() { - time_t now = time(NULL); + time_t now = time(nullptr); uint64_t check_count = checkpointServices_; uint64_t check_interval = now - checkpointTime_; @@ -282,7 +282,7 @@ ServiceTracker::reportCheckpoint() << " checkpoint_speed_sum:" << check_duration << " lifetime_time:" << life_interval << " lifetime_services:" << life_count; - if (featureThreadCheck_ && threadManager_ != NULL) { + if (featureThreadCheck_ && threadManager_ != nullptr) { size_t worker_count = threadManager_->workerCount(); size_t idle_count = threadManager_->idleWorkerCount(); message << " total_workers:" << worker_count @@ -321,7 +321,7 @@ ServiceTracker::defaultLogMethod(int level, const string &message) { if (level <= LOG_LEVEL) { string level_string; - time_t now = time(NULL); + time_t now = time(nullptr); char now_pretty[26]; ctime_r(&now, now_pretty); now_pretty[24] = '\0'; @@ -356,20 +356,20 @@ ServiceTracker::defaultLogMethod(int level, const string &message) */ Stopwatch::Stopwatch() { - gettimeofday(&startTime_, NULL); + gettimeofday(&startTime_, nullptr); } void Stopwatch::reset() { - gettimeofday(&startTime_, NULL); + gettimeofday(&startTime_, nullptr); } uint64_t Stopwatch::elapsedUnits(Stopwatch::Unit unit, string *label) const { timeval now_time; - gettimeofday(&now_time, NULL); + gettimeofday(&now_time, nullptr); time_t duration_secs = now_time.tv_sec - startTime_.tv_sec; uint64_t duration_units; @@ -377,7 +377,7 @@ Stopwatch::elapsedUnits(Stopwatch::Unit unit, string *label) const case UNIT_SECONDS: duration_units = duration_secs + (now_time.tv_usec - startTime_.tv_usec + 500000) / 1000000; - if (NULL != label) { + if (nullptr != label) { stringstream ss_label; ss_label << duration_units << " secs"; label->assign(ss_label.str()); @@ -386,7 +386,7 @@ Stopwatch::elapsedUnits(Stopwatch::Unit unit, string *label) const case UNIT_MICROSECONDS: duration_units = duration_secs * 1000000 + now_time.tv_usec - startTime_.tv_usec; - if (NULL != label) { + if (nullptr != label) { stringstream ss_label; ss_label << duration_units << " us"; label->assign(ss_label.str()); @@ -396,7 +396,7 @@ Stopwatch::elapsedUnits(Stopwatch::Unit unit, string *label) const default: duration_units = duration_secs * 1000 + (now_time.tv_usec - startTime_.tv_usec + 500) / 1000; - if (NULL != label) { + if (nullptr != label) { stringstream ss_label; ss_label << duration_units << " ms"; label->assign(ss_label.str()); diff --git a/contrib/fb303/cpp/ServiceTracker.h b/contrib/fb303/cpp/ServiceTracker.h index 9a3edd8f131..faaa4a72dd9 100644 --- a/contrib/fb303/cpp/ServiceTracker.h +++ b/contrib/fb303/cpp/ServiceTracker.h @@ -120,7 +120,7 @@ class Stopwatch public: enum Unit { UNIT_SECONDS, UNIT_MILLISECONDS, UNIT_MICROSECONDS }; Stopwatch(); - uint64_t elapsedUnits(Unit unit, std::string *label = NULL) const; + uint64_t elapsedUnits(Unit unit, std::string *label = nullptr) const; void reset(); private: timeval startTime_; diff --git a/contrib/fb303/if/fb303.thrift b/contrib/fb303/if/fb303.thrift index 89bd6eb7b41..f1ca982b8bc 100644 --- a/contrib/fb303/if/fb303.thrift +++ b/contrib/fb303/if/fb303.thrift @@ -24,7 +24,7 @@ namespace java com.facebook.fb303 namespace cpp facebook.fb303 namespace perl Facebook.FB303 -namespace netcore Facebook.FB303.Test +namespace netstd Facebook.FB303.Test /** * Common status reporting mechanism across all services diff --git a/contrib/fb303/java/build.properties b/contrib/fb303/java/build.properties index 84636683c94..845048cad50 100644 --- a/contrib/fb303/java/build.properties +++ b/contrib/fb303/java/build.properties @@ -1,5 +1,5 @@ # Maven Ant tasks Jar details mvn.ant.task.version=2.1.3 -mvn.repo=http://repo1.maven.org/maven2 +mvn.repo=https://repo1.maven.org/maven2 mvn.ant.task.url=${mvn.repo}/org/apache/maven/maven-ant-tasks/${mvn.ant.task.version} mvn.ant.task.jar=maven-ant-tasks-${mvn.ant.task.version}.jar diff --git a/contrib/fb303/java/build.xml b/contrib/fb303/java/build.xml index 591a4cbd0aa..7a1b8f10543 100755 --- a/contrib/fb303/java/build.xml +++ b/contrib/fb303/java/build.xml @@ -136,9 +136,9 @@ - diff --git a/contrib/fb303/py/setup.py b/contrib/fb303/py/setup.py index 12d2d5c620b..d27c2962f71 100644 --- a/contrib/fb303/py/setup.py +++ b/contrib/fb303/py/setup.py @@ -26,9 +26,9 @@ from distutils.core import setup, Extension, Command setup(name='thrift_fb303', - version='0.12.0', + version='1.0.0', description='Python bindings for the Apache Thrift FB303', - author=['Thrift Developers'], + author=['Apache Thrift Developers'], author_email=['dev@thrift.apache.org'], url='http://thrift.apache.org', license='Apache License 2.0', @@ -37,7 +37,7 @@ 'fb303_scripts', ], classifiers=[ - 'Development Status :: 5 - Production/Stable', + 'Development Status :: 7 - Inactive', 'Environment :: Console', 'Intended Audience :: Developers', 'Programming Language :: Python', diff --git a/contrib/thrift-maven-plugin/pom.xml b/contrib/thrift-maven-plugin/pom.xml index e11fbbf0f3f..b1816225740 100644 --- a/contrib/thrift-maven-plugin/pom.xml +++ b/contrib/thrift-maven-plugin/pom.xml @@ -7,9 +7,9 @@ to you under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at - + http://www.apache.org/licenses/LICENSE-2.0 - + Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY @@ -17,37 +17,64 @@ specific language governing permissions and limitations under the License. --> - 4.0.0 + + org.apache + apache + 23 + org.apache.thrift thrift-maven-plugin maven-plugin thrift-maven-plugin - 0.12.0 + 1.0.0 + + + 1.8 + 1.8 + ${basedir}/../.. + ${thrift.root}/compiler/cpp/thrift + ${thrift.root}/test + + org.apache.maven.plugins maven-compiler-plugin - - 1.7 - 1.7 - org.apache.maven.plugins maven-surefire-plugin - 2.14.1 ${thrift.compiler} + + org.apache.maven.plugins + maven-antrun-plugin + + + generate-jar-for-test + generate-test-resources + + + + + + + run + + + + @@ -84,9 +111,4 @@ test - - ${basedir}/../.. - ${thrift.root}/compiler/cpp/thrift - ${thrift.root}/test - diff --git a/contrib/thrift-maven-plugin/src/test/java/org/apache/thrift/maven/TestAbstractThriftMojo.java b/contrib/thrift-maven-plugin/src/test/java/org/apache/thrift/maven/TestAbstractThriftMojo.java index c1176712d19..7c90a990e39 100644 --- a/contrib/thrift-maven-plugin/src/test/java/org/apache/thrift/maven/TestAbstractThriftMojo.java +++ b/contrib/thrift-maven-plugin/src/test/java/org/apache/thrift/maven/TestAbstractThriftMojo.java @@ -59,7 +59,7 @@ public void testMakeThriftPathFromJars() throws Throwable { // used by other tests. It's used here to represent a dependency of the project maven is building, // one that is contributing .thrift IDL files as well as any other artifacts. final Iterable classpathElementFiles = Lists.newArrayList( - new File("src/test/resources/dependency-jar-test/SharedIdl.jar") + new File("target/SharedIdl.jar") ); final Set thriftDirectories = mojo.makeThriftPathFromJars(temporaryThriftFileDirectory, classpathElementFiles); @@ -70,7 +70,7 @@ public void testMakeThriftPathFromJars() throws Throwable { // means it points to the directory containing the "idl" hierarchy rather than to the idl directory // itself. final Set expected = Sets.newHashSet( - new File(testRootDir, "src/test/resources/dependency-jar-test/SharedIdl.jar") + new File(testRootDir, "target/SharedIdl.jar") ); assertEquals("makeThriftPathFromJars should return thrift IDL base path from within JAR", expected, thriftDirectories); diff --git a/contrib/thrift.spec b/contrib/thrift.spec index 1fe0f9c5979..4c678ba7f71 100644 --- a/contrib/thrift.spec +++ b/contrib/thrift.spec @@ -28,7 +28,7 @@ Name: thrift License: Apache License v2.0 Group: Development Summary: RPC and serialization framework -Version: 0.12.0 +Version: 0.14.0 Release: 0 URL: http://thrift.apache.org Packager: Thrift Developers @@ -58,8 +58,8 @@ BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n) Thrift is a software framework for scalable cross-language services development. It combines a powerful software stack with a code generation engine to build services that work efficiently and seamlessly between C++, -Java, C#, Python, Ruby, Perl, PHP, Objective C/Cocoa, Smalltalk, Erlang, -Objective Caml, and Haskell. +Java, C#, Python, Ruby, Perl, PHP, Smalltalk, Erlang, OCaml, Haskell, and +other languages. %files %defattr(-,root,root) @@ -175,9 +175,21 @@ export RUBYLIB=${PWD}/lib/rb/lib %{?without_ruby: --without-ruby } \ %{?without_php: --without-php } \ %{!?without_php: PHP_PREFIX=${RPM_BUILD_ROOT}/usr/lib/php } \ - --without-csharp \ --without-erlang \ +%if 0%{!?without_ruby:1} +eval $(grep "^WITH_RUBY_TRUE" config.log) +if [[ "${WITH_RUBY_TRUE}" != "" ]]; then + set +x + echo "" + echo "configure determined that ruby requirements are missing (bundler gem?), either install missing components" >&2 + echo "or disable the ruby sub-packages as follows:" >&2 + echo " rpmbuild -D'%without_ruby 1' ..." >&2 + echo "" + exit 1 +fi +%endif + make %{?_smp_mflags} %if 0%{!?without_java:1} @@ -234,5 +246,7 @@ umask 007 /sbin/ldconfig > /dev/null 2>&1 %changelog +* Wed Aug 21 2013 Thrift Dev +- Thrift 0.9.1 release. * Wed Oct 10 2012 Thrift Dev - Thrift 0.9.0 release. diff --git a/contrib/transport-sample/ThriftCommon.cpp b/contrib/transport-sample/ThriftCommon.cpp index 60ebf7a0026..2b676a88efb 100644 --- a/contrib/transport-sample/ThriftCommon.cpp +++ b/contrib/transport-sample/ThriftCommon.cpp @@ -23,7 +23,7 @@ namespace thriftcommon (int)boost::shared_dynamic_cast(transport)->getClientWrtPipeHandle()); //spawn the child process - if (!CreateProcessA(NULL, handles, NULL,NULL,TRUE,0,NULL,NULL,&si,&pi)) + if (!CreateProcessA(nullptr, handles, nullptr,nullptr,TRUE,0,nullptr,nullptr,&si,&pi)) { GlobalOutput.perror("TPipeServer CreateProcess failed, GLE=", GetLastError()); return false; diff --git a/contrib/transport-sample/ThriftCommon.h b/contrib/transport-sample/ThriftCommon.h index d24d1a7a749..078ad4474bc 100644 --- a/contrib/transport-sample/ThriftCommon.h +++ b/contrib/transport-sample/ThriftCommon.h @@ -16,8 +16,6 @@ // #ifdef _WIN32 //thrift is crashing when using boost threads on Mac OSX -# define USE_BOOST_THREAD 1 -# include #else # include # include @@ -27,7 +25,7 @@ // Required Includes //'server' side #includes #include -#include +#include #include #include //'client' side #includes @@ -89,7 +87,7 @@ namespace thriftcommon else { //Multi-threaded server boost::shared_ptr threadManager = ThreadManager::newSimpleThreadManager(NumThreads); - boost::shared_ptr threadFactory = boost::shared_ptr(new PlatformThreadFactory()); + boost::shared_ptr threadFactory = boost::shared_ptr(new ThreadFactory()); threadManager->threadFactory(threadFactory); threadManager->start(); server.reset(new TThreadPoolServer(processor, transport, tfactory, pfactory, threadManager)); diff --git a/contrib/vagrant/centos-6.5/README.md b/contrib/vagrant/centos-6.5/README.md index 91ae67f2f57..55583f90148 100644 --- a/contrib/vagrant/centos-6.5/README.md +++ b/contrib/vagrant/centos-6.5/README.md @@ -11,7 +11,7 @@ This will download and launch the base box VM under VirtualBox and run the Apach $ vagrant ssh [vagrant@thrift ~]$ cd /thrift [vagrant@thrift thrift]$ compiler/cpp/thrift --version - Thrift version 0.12.0 + Thrift version The provisioning script (inside the Vagrantfile) runs ./bootstrap.sh, ./configure, make and make check, but does not install thrift. To install thrift run "make install". diff --git a/contrib/vagrant/centos-6.5/Vagrantfile b/contrib/vagrant/centos-6.5/Vagrantfile index 6207958b35a..51a2239bc9f 100644 --- a/contrib/vagrant/centos-6.5/Vagrantfile +++ b/contrib/vagrant/centos-6.5/Vagrantfile @@ -87,7 +87,7 @@ sudo ./b2 install # Java LIB Dependencies ##################################### -sudo yum install -y ant junit ant-nodeps ant-junit java-1.7.0-openjdk-devel +sudo yum install -y ant junit ant-nodeps ant-junit java-1.8.0-openjdk-devel # Python LIB Dependencies ##################################### diff --git a/contrib/zeromq/README.md b/contrib/zeromq/README.md index 9e0b5bd3276..3a7379e5575 100644 --- a/contrib/zeromq/README.md +++ b/contrib/zeromq/README.md @@ -28,3 +28,8 @@ that amount. This code is not quite what I would consider production-ready. It doesn't support all of the normal hooks into Thrift, and its performance is sub-optimal because it does some unnecessary copying. + +Deprecation notice: +Csharp is not a supported Apache Thrift target anymore. Instead netstd is the +recommended replacement. This code is left "as is" for educational purposes +unless someone converts it to netstd diff --git a/contrib/zeromq/TZmqServer.cpp b/contrib/zeromq/TZmqServer.cpp index 4df6c923708..1ad7db80446 100644 --- a/contrib/zeromq/TZmqServer.cpp +++ b/contrib/zeromq/TZmqServer.cpp @@ -21,7 +21,7 @@ #include #include -using apache::thrift::stdcxx::shared_ptr; +using apache::thrift::std::shared_ptr; using apache::thrift::transport::TMemoryBuffer; using apache::thrift::protocol::TProtocol; @@ -41,7 +41,7 @@ bool TZmqServer::serveOne(int recv_flags) { outputProtocolFactory_->getProtocol(outputTransport)); shared_ptr transport(new TMemoryBuffer); - processor_->process(inputProtocol, outputProtocol, NULL); + processor_->process(inputProtocol, outputProtocol, nullptr); if (zmq_type_ == ZMQ_REP) { uint8_t* buf; diff --git a/contrib/zeromq/TZmqServer.h b/contrib/zeromq/TZmqServer.h index 43f86c084fc..ecd13b4243f 100644 --- a/contrib/zeromq/TZmqServer.h +++ b/contrib/zeromq/TZmqServer.h @@ -20,6 +20,7 @@ #ifndef _THRIFT_SERVER_TZMQSERVER_H_ #define _THRIFT_SERVER_TZMQSERVER_H_ 1 +#include #include #include @@ -28,7 +29,7 @@ namespace apache { namespace thrift { namespace server { class TZmqServer : public TServer { public: TZmqServer( - apache::thrift::stdcxx::shared_ptr processor, + std::shared_ptr processor, zmq::context_t& ctx, const std::string& endpoint, int type) : TServer(processor) , processor_(processor) @@ -56,7 +57,7 @@ class TZmqServer : public TServer { } private: - apache::thrift::stdcxx::shared_ptr processor_; + std::shared_ptr processor_; int zmq_type_; zmq::socket_t sock_; }; diff --git a/contrib/zeromq/csharp/ThriftZMQ.csproj b/contrib/zeromq/csharp/ThriftZMQ.csproj index 9143a6921d4..80ad1dbd613 100755 --- a/contrib/zeromq/csharp/ThriftZMQ.csproj +++ b/contrib/zeromq/csharp/ThriftZMQ.csproj @@ -25,7 +25,7 @@ false true 0 - 0.12.0 + 1.0.0.0 false false true diff --git a/contrib/zeromq/test-client.cpp b/contrib/zeromq/test-client.cpp index 70a331ed57f..159c25030d5 100644 --- a/contrib/zeromq/test-client.cpp +++ b/contrib/zeromq/test-client.cpp @@ -6,7 +6,7 @@ #include "TZmqClient.h" #include "Storage.h" -using apache::thrift::stdcxx::shared_ptr; +using apache::thrift::std::shared_ptr; using apache::thrift::transport::TZmqClient; using apache::thrift::protocol::TBinaryProtocol; diff --git a/contrib/zeromq/test-receiver.cpp b/contrib/zeromq/test-receiver.cpp index 60791ac72fe..d465bff6343 100644 --- a/contrib/zeromq/test-receiver.cpp +++ b/contrib/zeromq/test-receiver.cpp @@ -2,7 +2,7 @@ #include "TZmqServer.h" #include "Storage.h" -using apache::thrift::stdcxx::shared_ptr; +using apache::thrift::std::shared_ptr; using apache::thrift::TProcessor; using apache::thrift::server::TZmqServer; using apache::thrift::server::TZmqMultiServer; diff --git a/contrib/zeromq/test-sender.cpp b/contrib/zeromq/test-sender.cpp index 8928db390a3..5c086a11ff8 100644 --- a/contrib/zeromq/test-sender.cpp +++ b/contrib/zeromq/test-sender.cpp @@ -6,7 +6,7 @@ #include "TZmqClient.h" #include "Storage.h" -using apache::thrift::stdcxx::shared_ptr; +using apache::thrift::std::shared_ptr; using apache::thrift::transport::TZmqClient; using apache::thrift::protocol::TBinaryProtocol; diff --git a/contrib/zeromq/test-server.cpp b/contrib/zeromq/test-server.cpp index baa1451dd27..e6f1b208370 100644 --- a/contrib/zeromq/test-server.cpp +++ b/contrib/zeromq/test-server.cpp @@ -2,7 +2,7 @@ #include "TZmqServer.h" #include "Storage.h" -using apache::thrift::stdcxx::shared_ptr; +using apache::thrift::std::shared_ptr; using apache::thrift::TProcessor; using apache::thrift::server::TZmqServer; using apache::thrift::server::TZmqMultiServer; diff --git a/debian/changelog b/debian/changelog index 5c8545fcb15..8fd900c7395 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,8 +1,20 @@ +thrift (0.14.0) stable; urgency=low + + * update to 0.14.0 + + -- Apache Thrift Developers Thu, 04 Feb 2021 17:00:00 -0500 + +thrift (0.13.0) stable; urgency=low + + * update to 0.13.0 + + -- Apache Thrift Developers Wed, 11 Nov 2019 12:00:00 -0500 + thrift (0.12.0) stable; urgency=low * update to 0.12.0 - -- Jake Farrell Wed, 15 Oct 2018 12:00:00 -0500 + -- Apache Thrift Developers Wed, 28 Dec 2018 12:00:00 -0500 thrift (0.11.0) stable; urgency=low diff --git a/debian/control b/debian/control index cb8a3764f17..a9e934f173a 100644 --- a/debian/control +++ b/debian/control @@ -1,18 +1,18 @@ Source: thrift Section: devel Priority: extra -Build-Depends: debhelper (>= 9), build-essential, mono-mcs, python-dev, ant, - mono-devel, libmono-system-web4.0-cil, erlang-base, ruby-dev | ruby1.9.1-dev, ruby-bundler ,autoconf, automake, - pkg-config, libtool, bison, flex, libboost-dev | libboost1.53-dev | libboost1.63-all-dev, +Build-Depends: dotnet-runtime-3.1, dotnet-sdk-3.1, debhelper (>= 9), build-essential, python-dev, ant, + erlang-base, ruby-dev | ruby1.9.1-dev, ruby-bundler ,autoconf, automake, + pkg-config, libtool, bison, flex, libboost-dev | libboost1.56-dev | libboost1.63-all-dev, python-all, python-setuptools, python-all-dev, python-all-dbg, python3-all, python3-setuptools, python3-all-dev, python3-all-dbg, - openjdk-7-jdk | openjdk-8-jdk | default-jdk, - libboost-test-dev | libboost-test1.53-dev | libboost-test1.63-dev, libevent-dev, libssl-dev, perl (>= 5.8.0-7), + openjdk-8-jdk | openjdk-8-jdk-headless | openjdk-11-jdk | openjdk-11-jdk-headless | default-jdk, + libboost-test-dev | libboost-test1.56-dev | libboost-test1.63-dev, libevent-dev, libssl-dev, perl (>= 5.8.0-7), php (>= 5), php-dev (>= 5), libglib2.0-dev, qtchooser, qtbase5-dev-tools Maintainer: Thrift Developer's Homepage: http://thrift.apache.org/ -Vcs-Git: https://git-wip-us.apache.org/repos/asf/thrift.git -Vcs-Browser: https://git-wip-us.apache.org/repos/asf?p=thrift.git +Vcs-Git: https://github.com/apache/thrift.git +Vcs-Browser: https://github.com/apache/thrift Standards-Version: 3.9.7 X-Python-Version: >= 2.6 X-Python3-Version: >= 3.3 @@ -126,17 +126,16 @@ Description: Java bindings for Thrift classes, and then the modules in this package will allow you to use those classes in your programs. -Package: libthrift-cil +Package: libthrift-netstd Architecture: all -Section: cli-mono -Depends: cli-common, libmono-corlib4.0-cil (>= 2.10) | libmono-corlib4.5-cil (>=3.2), libmono-system4.0-cil (>= 2.10), - libmono-system-web4.0-cil (>= 2.10), ${misc:Depends} -Description: CLI bindings for Thrift +Section: netstd +Depends: dotnet-runtime-3.1, ${misc:Depends} +Description: NET Standard bindings for Thrift Thrift is a software framework for scalable cross-language services development. It combines a software stack with a code generation engine to build services that work efficiently and seamlessly. . - This package contains the CLI bindings for Thrift. You will need the thrift + This package contains the NET Standard bindings for Thrift. You will need the thrift tool (in the thrift-compiler package) to compile your definition to C# classes, and then the modules in this package will allow you to use those classes in your programs. diff --git a/debian/copyright b/debian/copyright index 850643c9aa4..ada769b0808 100644 --- a/debian/copyright +++ b/debian/copyright @@ -28,9 +28,8 @@ the Thrift Software License (see doc/old-thrift-license.txt), and relicensed under the Apache 2.0 License: compiler/cpp/Makefile.am - compiler/cpp/src/generate/t_cocoa_generator.cc compiler/cpp/src/generate/t_cpp_generator.cc - compiler/cpp/src/generate/t_csharp_generator.cc + compiler/cpp/src/generate/t_netstd_generator.cc compiler/cpp/src/generate/t_erl_generator.cc compiler/cpp/src/generate/t_hs_generator.cc compiler/cpp/src/generate/t_java_generator.cc @@ -47,36 +46,36 @@ under the Apache 2.0 License: compiler/cpp/src/platform.h compiler/cpp/src/thriftl.ll compiler/cpp/src/thrifty.yy - lib/csharp/src/Protocol/TBinaryProtocol.cs - lib/csharp/src/Protocol/TField.cs - lib/csharp/src/Protocol/TList.cs - lib/csharp/src/Protocol/TMap.cs - lib/csharp/src/Protocol/TMessage.cs - lib/csharp/src/Protocol/TMessageType.cs - lib/csharp/src/Protocol/TProtocol.cs - lib/csharp/src/Protocol/TProtocolException.cs - lib/csharp/src/Protocol/TProtocolFactory.cs - lib/csharp/src/Protocol/TProtocolUtil.cs - lib/csharp/src/Protocol/TSet.cs - lib/csharp/src/Protocol/TStruct.cs - lib/csharp/src/Protocol/TType.cs - lib/csharp/src/Server/TServer.cs - lib/csharp/src/Server/TSimpleServer.cs - lib/csharp/src/Server/TThreadPoolServer.cs - lib/csharp/src/TApplicationException.cs - lib/csharp/src/Thrift.csproj - lib/csharp/src/Thrift.sln - lib/csharp/src/TProcessor.cs - lib/csharp/src/Transport/TServerSocket.cs - lib/csharp/src/Transport/TServerTransport.cs - lib/csharp/src/Transport/TSocket.cs - lib/csharp/src/Transport/TStreamTransport.cs - lib/csharp/src/Transport/TTransport.cs - lib/csharp/src/Transport/TTransportException.cs - lib/csharp/src/Transport/TTransportFactory.cs - lib/csharp/ThriftMSBuildTask/Properties/AssemblyInfo.cs - lib/csharp/ThriftMSBuildTask/ThriftBuild.cs - lib/csharp/ThriftMSBuildTask/ThriftMSBuildTask.csproj + lib/netstd/src/Protocol/TBinaryProtocol.cs + lib/netstd/src/Protocol/TField.cs + lib/netstd/src/Protocol/TList.cs + lib/netstd/src/Protocol/TMap.cs + lib/netstd/src/Protocol/TMessage.cs + lib/netstd/src/Protocol/TMessageType.cs + lib/netstd/src/Protocol/TProtocol.cs + lib/netstd/src/Protocol/TProtocolException.cs + lib/netstd/src/Protocol/TProtocolFactory.cs + lib/netstd/src/Protocol/TProtocolUtil.cs + lib/netstd/src/Protocol/TSet.cs + lib/netstd/src/Protocol/TStruct.cs + lib/netstd/src/Protocol/TType.cs + lib/netstd/src/Server/TServer.cs + lib/netstd/src/Server/TSimpleServer.cs + lib/netstd/src/Server/TThreadPoolServer.cs + lib/netstd/src/TApplicationException.cs + lib/netstd/src/Thrift.csproj + lib/netstd/src/Thrift.sln + lib/netstd/src/TProcessor.cs + lib/netstd/src/Transport/TServerSocket.cs + lib/netstd/src/Transport/TServerTransport.cs + lib/netstd/src/Transport/TSocket.cs + lib/netstd/src/Transport/TStreamTransport.cs + lib/netstd/src/Transport/TTransport.cs + lib/netstd/src/Transport/TTransportException.cs + lib/netstd/src/Transport/TTransportFactory.cs + lib/netstd/ThriftMSBuildTask/Properties/AssemblyInfo.cs + lib/netstd/ThriftMSBuildTask/ThriftBuild.cs + lib/netstd/ThriftMSBuildTask/ThriftMSBuildTask.csproj lib/rb/lib/thrift.rb lib/st/README lib/st/thrift.st diff --git a/debian/rules b/debian/rules index 9b436d97aaa..d0de508724c 100755 --- a/debian/rules +++ b/debian/rules @@ -76,7 +76,7 @@ build-indep-stamp: configure-stamp $(CURDIR)/compiler/cpp/thrift ./gradlew --no-daemon -Prelease=true jar # C# - $(MAKE) -C $(CURDIR)/lib/csharp + $(MAKE) -C $(CURDIR)/lib/netstd # Ruby $(MAKE) -C $(CURDIR)/lib/rb @@ -96,8 +96,6 @@ clean: # Add here commands to clean up after the build process. -$(MAKE) clean - $(CURDIR)/cleanup.sh - dh_clean install: install-indep install-arch @@ -124,9 +122,9 @@ install-indep: $(CURDIR)/debian/ruby-thrift/usr/lib/ruby/1.9.1 # C# - mkdir -p $(CURDIR)/debian/libthrift-cil/usr/lib/cli/thrift/ && \ - cp $(CURDIR)/lib/csharp/Thrift.dll \ - $(CURDIR)/debian/libthrift-cil/usr/lib/cli/thrift/Thrift.dll + mkdir -p $(CURDIR)/debian/libthrift-netstd/usr/lib/cli/thrift/ && \ + cp $(CURDIR)/lib/netstd/Thrift/bin/Release/netstandard2.0/Thrift.dll \ + $(CURDIR)/debian/libthrift-netstd/usr/lib/cli/thrift/Thrift.dll # Perl $(MAKE) -C $(CURDIR)/lib/perl install DESTDIR=$(CURDIR)/debian/libthrift-perl diff --git a/doap.rdf b/doap.rdf index cd4aeccee0f..6094de72821 100755 --- a/doap.rdf +++ b/doap.rdf @@ -27,7 +27,7 @@ Apache Thrift - Apache Thrift software provides a framework for scalable cross-language services development, combines a software stack with a code generation engine to build services that work efficiently and seamlessly between C++, Java, Python, PHP, Ruby, Erlang, Perl, Haskell, C#, Cocoa, JavaScript, Node.js, Smalltalk, OCaml and Delphi and other languages. + Apache Thrift software provides a framework for scalable cross-language services development, combines a software stack with a code generation engine to build services that work efficiently and seamlessly between C++, Java, Python, PHP, Ruby, Erlang, Perl, Haskell, C#, JavaScript, Node.js, Smalltalk, OCaml and Delphi and other languages. Apache Thrift allows you to define data types and service interfaces in a simple definition file. Taking that file as input, the compiler generates code to be used to easily build RPC clients and servers that communicate seamlessly across programming languages. Instead of writing a load of boilerplate code to serialize and transport your objects and invoke remote methods, you can get right down to business. @@ -36,7 +36,6 @@ C C# C++ - Cocoa D Dart Delphi @@ -60,7 +59,17 @@ Apache Thrift - 2018-10-15 + 2021-02-04 + 0.14.0 + + + Apache Thrift + 2019-10-11 + 0.13.0 + + + Apache Thrift + 2018-12-28 0.12.0 @@ -136,8 +145,8 @@ - - + + diff --git a/doc/ReleaseManagement.md b/doc/ReleaseManagement.md new file mode 100644 index 00000000000..d2b6f342eba --- /dev/null +++ b/doc/ReleaseManagement.md @@ -0,0 +1,419 @@ +# Apache Thrift Release Management + +Instructions for preparing and distributing a release of Apache Thrift are fairly complex. These procedures are documented here, and we're working to automate as much of this as possible. There are few projects like ours that integrate with 28 programming languages. Given the extreme number of package management systems that Apache Thrift integrates with (compared to perhaps any), part of the burden of releasing Apache Thrift is to manually package and upload some of these [language-specific packages](http://apache.thrift.org/libraries). + +It is important to note here that Apache Thrift is designed for version interoperability, so one can use a version 0.7.0 client with a 0.12.0 server. A particular version number does not make any guarantees as to the features available in any given language. See the [Language Feature Matrix](https://github.com/apache/thrift/blob/master/LANGUAGES.md) to learn more. + +## Concepts + +### Versioning + +Apache Thrift and the vast majority of package management systems out there conform to the [SemVer 2.0](https://semver.org/spec/v2.0.0.html) version numbering specification. Apache Thrift uses the following versioning rules: + +- *major* is currently always zero; +- *minor* is increased for each release cycle; +- *patch* is increased for patch builds between release cycles to address critical defect, security, or packaging issues + +Further, if there are only packaging changes for a single third-party distribution point to correct an issue, the major.minor.patch may remain the same while adding a suffix compatible with that distribution point, for example, "0.12.0.1" for nuget, or "0.12.0-1" for maven. + +#### External Package Patches + +It is common to have language-specific critical defects or packaging errors that need to be resolved between releases of Apache Thrift. The project handles these on a case-by-case basis for languages that have their own [package management systems](http://apache.thrift.org/libraries). When a language-specific patch is made, the patch level of the distribution pushed to the external package manager is bumped. + + As such, there may be cases between Apache Thrift releases where there are (for example) a `0.12.1` and `0.12.2` version of a Haskell Hackage package, and perhaps also a `0.12.3` version of a dlang dub package. You will not find a tag or an official project release in these cases, however, the code changes will be reflected in the release branch and in master. In these cases we would not release a version of Apache Thrift nor would we refresh all the external language packages. + +#### Version in the master branch + +The master branch will always contain the next anticipated release version. When a release cycle begins, a branch is cut from master. The release branch will already have all of the correct versions, and therefore release branches can be easily merged back into master. (This was not true of releases before 0.12.0). + +### Code Repository + +The authoritative repository for Apache Thrift is stored in [GitHub](https://github.com/apache/thrift). It is mirrored by [GitBox](https://gitbox.apache.org/repos/asf?p=thrift.git). + +### Branches + +All code (submitted via pull request or direct push) is committed to the `master` branch. Until version 1.0 of Apache Thrift each release branch was named ``, for example in version `0.12.0` there is a branch named the same. For version 1.0 releases any beyond, releases will have a branch named `release/`. + +### Tags + +Up to version `0.12.0` each release of Apache Thrift was tagged with a `` tag. Starting with the `0.12.0` release, each release of Apache Thrift will be tagged with a `v` tag to satisfy external package management tools (such as ones for dlang and golang). For example the tag of version `0.12.0` is `v0.12.0`. + +## Release Procedures + +### Release Schedule + +Apache Thrift has no official release schedule, however the project aims to release at least twice per year. + +A complete release cycle will take about 1 week to complete, if things go well, with half of that time waiting for a vote. + +### Release Manager + +Before a release cycle begins, someone must nominate themselves on the development mailing list as the release manager for that release. In order to be a release manager you must meet the following criteria: + +1. You are a [member](http://people.apache.org/phonebook.html?pmc=thrift) of the Apache PMC group. +1. Your profile at https://id.apache.org/ is valid and contains a PGP key. If it does not, see the [Apache OpenPGP Instructions](https://www.apache.org/dev/openpgp.html). If your PGP private key creation seems to hang indefinitely while creating entropy, try these fixes: + - Generate disk I/O with: `dd if=/dev/sda of=/dev/zero` + - Install the `rng-tools` package. +1. Your PGP key is visible in the [Apache Committer Keys](http://people.apache.org/keys/committer/) for code signing. This list is updated periodically from your Apache ID (see previous step). +1. You have read and agree with the contents of the [ASF Release Distribution Policy](https://www.apache.org/dev/release-distribution.html). +1. You have access and the ability to use subversion. All distribution artifacts are released through a subversion commit. +1. You can build in the Linux Docker Container, and you have Visual Studio 2017. +1. You have sufficient time to complete a release distribution. + +### Release Candidate + +All Apache Thrift releases go through a 72-hour final release candidate voting procedure. Votes from members of the Apache Thrift PMC are binding, and all others are non-binding. For these examples, the `master` branch is at version 1.0.0 and that is the next release. + +1. Scrub the Apache Jira backlog. There are a couple things to do: + + 1. [Open Issues without a Component](https://issues.apache.org/jira/issues/?filter=-1&jql=project%20%3D%20THRIFT%20and%20status%20!%3D%20Closed%20and%20component%20is%20empty) - make sure everything has an assigned component, as the release notes are grouped together by language. + + 1. [Open Issues with a Fix Version](https://issues.apache.org/jira/issues/?filter=-1&jql=project%20%3D%20THRIFT%20and%20status%20in%20(OPEN%2C%20%27IN%20PROGRESS%27%2C%20REOPENED)%20and%20fixVersion%20is%20not%20empty) - these will be issues that someone placed a fixVersion on in Jira, but have not been resolved or closed yet. They are likely stale somehow. Resolutions for these issues include resolving or closing the issue in Jira, or simply removing the fixVersion if the issue hasn't been fixed. + + 1. [Open Blocking Issues](https://issues.apache.org/jira/issues/?filter=-1&jql=project%20%3D%20THRIFT%20and%20priority%20in%20(blocker)%20and%20status%20not%20in%20(closed)%20order%20by%20component%20ASC) - blocking issues should block a release. Scrub the list to see if they are really blocking the release, and if not change their priority. + + 1. [Open Critical Issues](https://issues.apache.org/jira/issues/?filter=-1&jql=project%20%3D%20THRIFT%20and%20priority%20in%20(critical)%20and%20status%20not%20in%20(closed)%20and%20type%20not%20in%20(%22wish%22)%20order%20by%20component%20ASC) - this list will end up in the known critical issues list in the changes file. Scrub it to make sure everything is actually critical. + + It is healthy to scrub these periodically, whether or not you are making a new release. + +1. Check that the version number in the `master` branch matches the version number of the upcoming release. To check the `master` branch version, run: + + ```bash + thrift$ grep AC_INIT configure.ac | cut -d'[' -f3 | cut -d']' -f1 + 1.0.0 + ``` + + If it does not match (this should be extremely rare), you need to submit a pull request setting the `master` branch to the desired version of the upcoming release. In the following example, we prepare to commit a branch where the version number is changed from `1.0.0` to `1.1.0`: + + ```bash + thrift$ git checkout -b fix-version-for-release + thrift$ build/veralign.sh 1.0.0 1.1.0 + # check to see if any of the manually modified files needs changes + thrift$ git push ... # make a pull request + ``` + +1. Create a release branch for the release, in this example `1.0.0`: + + ```bash + thrift$ git checkout master + thrift$ git pull + thrift$ git checkout -b "release/1.0.0" + thrift$ git push + ``` + + Now there is a `release/1.0.0` branch in GitHub for Apache Thrift. + + By creating a release branch we allow work to continue on the `master` branch for the next release while we finalize this one. Note that `release/1.0.0` and `master` in this example are now identical, and therefore it is possible to merge the release branch back into `master` at the end of the release! + +1. Modify these files manually, inserting the release into them at the appropriate location. Follow existing patterns in each file: + - `doap.rdf` + - `debian/changelog` + +1. Generate the content for `CHANGES.md` - this is one of the most time-consuming parts of the release cycle. It is a lot of work, but the result is well worth it to the consumers of Apache Thrift: + + 1. Find all [Issues Fixed but not Closed in 1.0.0](https://issues.apache.org/jira/issues/?filter=-1&jql=project%20%3D%20thrift%20and%20fixVersion%20%3D%201.0.0%20and%20status%20!%3D%20closed) (adjust the version in the link to suit your needs). + + 1. Export the list of issues to a CSV (Current Fields) and open in Excel (or a similar spreadsheet). + + 1. Hide all columns except for the issue id (i.e. THRIFT-nnnn), the component (first one), and the summary. + + 1. Sort by component ascending and then by id ascending. + + 1. Create a fourth column that will contain the contents of each line that goes into the release notes. Once you have the formula working in one cell paste it into the other rows to populate them. Use a formula to get the column to look like this: + + ```vcol + Issue Component Summary RelNote + THRIFT-123 C++ - Library Drop C++03 [THRIFT-123](https://issues.apache.org/jira/browse/THRIFT-3978) - Drop C++03 + ``` + + For example, if the row above was row "B" in EXCEL it would look something like: + + ```text + =CONCAT("[", B1, "]", + "https://issues.apache.org/jira/browse/", + B1, " - ", B3) + ``` + + 1. Create a level 3 section in `CHANGES.md` under the release for each component and copy the items from the RelNote column into the changes file. + + 1. Find all [Open Critical Issues](https://issues.apache.org/jira/issues/?filter=-1&jql=project%20%3D%20THRIFT%20and%20priority%20in%20(critical)%20and%20status%20not%20in%20(closed)%20and%20type%20not%20in%20(%22wish%22)%20order%20by%20component%20ASC) and add them to `CHANGES.md` in the list of known critical issues for the release. + +1. Commit all changes to the release branch. + +1. Generate the source tarball. + + 1. On a linux system get a clean copy of the release branch, for example: + + ```bash + ~$ git clone -b "release/1.0.0" git@github.com:apache/thrift.git thrift-1.0.0-src + ``` + + 1. In the clean copy of the release branch, start a docker build container and run `make dist`: + + ```code + ~$ cd thrift-1.0.0-src + ~/thrift-1.0.0-src$ docker run -v $(pwd):/thrift/src:rw \ + -it thrift/thrift-build:ubuntu-bionic /bin/bash + root@8b4101188aa2:/thrift/src# ./bootstrap.sh && ./configure && make dist + ``` + + The result will be a file named `thrift-1.0.0.tar.gz`. Check the size and make sure it is roughly 4MB. It could get larger over time, but it shouldn't jump by orders of magnitude. Once satisfied you can exit the docker container with `exit`. + + 1. Generate signatures and checksums for the tarball: + + ```bash + gpg --armor --output thrift-1.0.0.tar.gz.asc --detach-sig thrift-1.0.0.tar.gz + md5sum thrift-1.0.0.tar.gz > thrift-1.0.0.tar.gz.md5 + sha1sum thrift-1.0.0.tar.gz > thrift-1.0.0.tar.gz.sha1 + sha256sum thrift-1.0.0.tar.gz > thrift-1.0.0.tar.gz.sha256 + +1. Generate the Windows Thrift Compiler. This is a statically linked compiler that is portable and folks find it useful to be able to download one, especially if they are using third-party distributed runtime libraries for interpreted languages on Windows. There are two ways to generate this: + + - Using a Development VM + + 1. On a Windows machine with Visual Studio, pull down the source code and checkout the release branch. + 1. Open an x64 Native Tools Command Prompt for VS 2017 and create an out-of-tree build directory. + 1. Install the latest version of cmake. + 1. Install chocolatey and install winflexbison with chocolatey. + 1. Run cmake to generate an out-of-tree build environment: + ```cmd + C:\build> cmake ..\thrift -DBISON_EXECUTABLE=c:\ProgramData\chocolatey\lib\winflexbison\tools\win_bison.exe -DFLEX_EXECUTABLE=c:\ProgramData\chocolatey\lib\winflexbison\tools\win_flex.exe -DWITH_MT=ON -DWITH_SHARED_LIB=OFF -DWITH_CPP=OFF -DWITH_JAVA=OFF -DWITH_HASKELL=OFF -DWITH_PYTHON=OFF -DWITH_C_GLIB=OFF -DBUILD_TESTING=OFF -DBUILD_TUTORIALS=OFF -DBUILD_COMPILER=ON + C:\build> cmake --build . --config Release + ``` + + - Using [Docker for Windows](../build/docker/msvc2017/README.md), follow the instructions for building the compiler. + - In both cases: + 1. Verify the executable only depends on kernel32.dll using [depends.exe](http://www.dependencywalker.com/). + 1. Copy the executable `thrift.exe` to your linux system where the signed tarball lives and rename it to `thrift-1.0.0.exe` (substitute the correct version, of course). + 1. Sign the executable the same way you signed the tarball. + +1. Upload the release artifacts to the Apache Dist/Dev site. This requires subversion: + + ```bash + ~$ mkdir -p dist/dev + ~$ cd dist/dev + ~/dist/dev$ svn co "https://dist.apache.org/repos/dist/dev/thrift" thrift + ~/dist/dev$ cd thrift + ``` + + Copy the tarball, windows compiler executable, and 8 additional signing files into a new directory for the release: + + ``` bash + ~/dist/dev/thrift$ mkdir 1.0.0-rc0 + # copy the files into the directory + ~/dist/dev/thrift$ svn add 1.0.0-rc0 + ``` + + The layout of the files should match the [current release](https://www.apache.org/dist/thrift/). Once done, add the release candidate and check it in: + + ```bash + ~/dist/dev/thrift$ svn status + # verify everything is correct + ~/dist/dev/thrift$ svn commit -m "Apache Thrift 1.0.0-rc0 in dist dev" \ + --username --password + ``` + +1. Verify the release candidate artifacts are available at: + + [https://dist.apache.org/repos/dist/dev/thrift/](https://dist.apache.org/repos/dist/dev/thrift/) + +1. Send a voting announcement message to `dev@thrift.apache.org` following this template as a guide: + + ```code + To: dev@thrift.apache.org + Subject: [VOTE] Apache Thrift 1.0.0-rc0 release candidate + --- + All, + + I propose that we accept the following release candidate as the official Apache Thrift 1.0.0 release: + + https://dist.apache.org/repos/dist/dev/thrift/1.0.0-rc0/thrift-1.0.0-rc0.tar.gz + + The release candidate was created from the release/1.0.0 branch and can be cloned using: + + git clone -b release/1.0.0 https://github.com/apache/thrift.git + + The release candidates GPG signature can be found at: + https://dist.apache.org/repos/dist/dev/thrift/1.0.0-rc0/thrift-1.0.0-rc0.tar.gz.asc + + The release candidates checksums are: + md5: + sha1: + sha256: + + + A prebuilt statically-linked Windows compiler is available at: + https://dist.apache.org/repos/dist/dev/thrift/1.0.0-rc0/thrift-1.0.0-rc0.exe + + Prebuilt statically-linked Windows compiler GPG signature: + https://dist.apache.org/repos/dist/dev/thrift/1.0.0-rc0/thrift-1.0.0-rc0.exe.asc + + Prebuilt statically-linked Windows compiler checksums are: + md5: + sha1: + sha256: + + + The source tree as ZIP file to be published via Github releases: + https://dist.apache.org/repos/dist/dev/thrift/1.0.0-rc0/thrift-1.0.0-rc0.zip + + ZIP source tree GPG signature: + https://dist.apache.org/repos/dist/dev/thrift/1.0.0-rc0/thrift-1.0.0-rc0.zip.asc + + ZIP source tree checksums are: + md5: + sha1: + sha256: + + The CHANGES list for this release is available at: + https://github.com/apache/thrift/blob/release/1.0.0/CHANGES.md + + + Please download, verify sig/sum, install and test the libraries and languages of your choice. + + This vote will close in 72 hours on 2019-07-06 21:00 UTC + + [ ] +1 Release this as Apache Thrift 1.0.0 + [ ] +0 + [ ] -1 Do not release this as Apache Thrift 1.0.0 because... + ``` + +1. If any issues are brought up with the release candidate, you will need to package another and reset the voting clock. + +Voting on the development mailing list provides additional benefits (wisdom from [Christopher Tubbs](https://issues.apache.org/jira/browse/THRIFT-4506?focusedCommentId=16791902&page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel#comment-16791902)): +- It creates a public record for the vote, +- It allows for participation/evaluation from our wider user audience (more diversity in evaluators improves quality), and +- It provides more entry points for potential future committers/PMC members to earn merit through participation. + +### Official Release + +1. Send a message to `dev@thrift.apache.org` with the voting results. Use this template as a guide: + + ```code + To: dev~thrift.apache.org + Subject: [VOTE][RESULT] Release Apache Thrift 1.0.0 + --- + All, + + Including my own vote of +1 we have N binding +1 and no -1. + The vote for the Apache Thrift 1.0.0 release is ***successful***. + Thank you to all who helped test and verify. + ``` + +1. Use svn to checkout the release part of thrift (similar to dev) and copy the files over from dev, matching the previous release structure: + + ```bash + ~$ mkdir -p dist/release + ~$ cd dist/release + ~/dist/release$ svn co "https://dist.apache.org/repos/dist/release/thrift" thrift + ~/dist/release$ cd thrift + ~/dist/release/thrift$ mkdir 1.0.0 + ~/dist/release/thrift$ cp -p ../../dev/thrift/1.0.0-rc0/* 1.0.0/ + ~/dist/release/thrift$ svn status + # verify everything is correct + ~/dist/release/thrift$ svn commit -m "Apache Thrift 1.0.0 official release" \ + --username --password + ``` + + **NOTE** Once you check-in, you need to wait about a day for all the mirrors to update. You cannot send the announcement email or update the web site until the mirrors are updated. + +1. Create and push a tag for the release, for example "v1.0.0". + + **NOTE:** All new releases must have the "v" prefix to satisfy third-party package managers (dlang dub, golang, etc..) + + **NOTE:** You **should** [sign the release tag](https://git-scm.com/book/en/v2/Git-Tools-Signing-Your-Work). Since you already have a GPG signing key for publishing the Apache Release, you want to [upload that key to your GitHub account](https://help.github.com/en/articles/adding-a-new-gpg-key-to-your-github-account). Once the key is known by GitHub you can sign the tag. + + ```bash + ~/thrift$ # make sure you are on the release branch + ~/thrift$ git checkout release/1.0.0 + ~/thrift$ git pull + ~/thrift$ git tag -s v1.0.0 -m "Version 1.0.0" + ~/thrift$ git push --tags + ``` + + **NOTE:** If you get the error "gpg failed to sign the data" when tagging, try this fix: ```export GPG_TTY=$(tty)```. Alternatively, it may be necessary to specify the ```-u ``` as an additional argument. + +1. Create a new release from the [GitHub Tags Page](https://github.com/apache/thrift/tags). Attach the statically built Windows thrift compiler as a binary here. + +1. Merge the release branch into master. This ensures all changes made to fix up the release are in master. + + ```bash + ~/thrift$ git checkout master + ~/thrift$ git pull + ~/thrift$ git merge release/1.0.0 + ``` + + The merge of 1.0.0 into master should proceed as a fast-forward since the 1.0.0 release branch. If there are discrepancies the best thing to do is resolve them and then submit a pull request. This pull request must be *MERGED* and not *REBASED* after the CI build is successful. You may want to do this yourself and mark the pull request as `[DO NOT MERGE]`. + +1. Update the ASF CMS content for thrift to include the new release. Note over time we will retire this in favor of including all documentation in the GitHub repository. The page with the variables that are important like the current release or distribution links is in trunk/lib/path.pm in the ASF CMS for thrift. + + 1. Go to the [ASF CMS for Thrift](https://cms.apache.org/thrift/). + 1. Get a working copy. + 1. On the top right, click on `trunk`. + 1. Navigate into `lib`. + 1. Open `path.pm`. + 1. Edit + 1. Change `current_release` and `current_release_date` to reflect the correct information. + 1. Submit + 1. Commit + 1. Submit + 1. Follow Staging Build until it completes. + 1. Open the Staged site. + 1. Ensure the download links work. + 1. Publish Site. + +1. Make an announcement on the dev@ and user@ mailing lists of the release. There's no template to follow, but you can point folks to the official web site at https://thrift.apache.org, and to the GitHub site at https://github.org/apache.thrift. + +### Post-Release + +1. Visit https://reporter.apache.org/addrelease.html?thrift and register it. You will get an automated reminder as the one who committed into dist. This informs the Apache Board of Directors of releases through project reports. + +1. Create a local branch to bump the release number to the next anticipated release: + + ```bash + ~/thrift$ git checkout -b bump-master + ~/thrift$ build/veralign.sh 1.0.0 1.1.0 + ``` + + The veralign script will set the version number in all of the language packaging files and headers. You do not need to worry about the manually modified files at this time. You should however ensure everything is correct by looking at the diff. + +1. Create a pull request to advance master to the next anticipated release. + +1. In Apache Jira, select all tickets where the fix version is the release and the status is not closed ([example](https://issues.apache.org/jira/issues/?jql=project%20%3D%20THRIFT%20AND%20fixVersion%20%3D%201.0%20%20and%20status%20!%3D%20Closed)) and use the bulk editing tool to close them. +1. **FIXME** Ask someone with admin access to Apache Jira to change the fixVersion in question from unreleased to released, for example: + https://issues.apache.org/jira/browse/THRIFT-4686 + +1. Ensure that the [Jira release page](https://issues.apache.org/jira/projects/THRIFT?selectedItem=com.atlassian.jira.jira-projects-plugin%3Arelease-page&status=unreleased) for the version has the same number of issues in the version as issues done, and that there are no issues in progress and no issues to do, and no warnings. Finally, mark it as released and set the date of the release. + +* [Report any CVEs](https://apache.org/security/committers.html) that were fixed. You can email `security@apache.org` if you are not sure if there are any CVEs to report. + +#### Third Party Package Managers + +See https://thrift.apache.org/lib/ for the current status of each external package manager's distribution. The information below is from the 0.12.0 release: + + > This section needs to be updated with detailed instructions for each language, or pointers to the README.md files in each language directory with detailed release instructions for the given package management system. + +* [dart] Releasing this requires a google account. + * You will need to install the same version of dart that is used in the docker image. + * Go into lib/dart and run "pub publish --dry-run" and resolve any warnings. + * Run "pub publish" and go through the google account authorization to allow it. +* [dlang] Within a day, the dlang dub site https://code.dlang.org/packages/apache-thrift?tab=info + should pick up the release based on the tag. No action is needed. +* [haskell] https://hackage.haskell.org/package/thrift + https://jira.apache.org/jira/browse/THRIFT-4698 +* [npmjs] @jfarrell is the only one who can do this right now. + https://issues.apache.org/jira/browse/THRIFT-4688 +* [perl] A submission to CPAN is necessary (normally jeking3 does this): + * Checkout the release branch or tag on a linux system. + * Fire up the docker build container. + * Run "make clean" and remove any gen-perl directories. + * Inside `lib/perl` run the script `build-cpan-dist.sh`. + * Upload the resulting package. If there's a mistake that needs to be corrected, + increase the suffix. (_1, _2, ...) and upload another. You cannot replace a release on CPAN. +* [php] @jfarrell, @bufferoverflow, @jeking3 are the only ones who can do this right now. + * Once the release is tagged, one just has to hit the "Update" button to pick it up. +* [pypi] @jfarrell is the only one who can do this right now. + https://issues.apache.org/jira/browse/THRIFT-4687 +* [rust] Any thrift project committer is allowed to upload a new crate. + +If you have any questions email `dev@thrift.apache.org`. diff --git a/doc/coding_standards.md b/doc/coding_standards.md index 308100ab060..a8c7b603959 100644 --- a/doc/coding_standards.md +++ b/doc/coding_standards.md @@ -6,18 +6,18 @@ The purpose of this document is to make everyone's life easier. -It's easier when you read good, well formatted, with clearly defined purpose, code. +It's easier when you read good, well-formatted, with a clearly defined purpose, code. But the only way to read clean code is to write such. This document can help achieve that, but keep in mind that those are not silver-bullet, fix-all-at-once rules. Just think about readability while writing code. -Write code like you would have to read it in ten years from now. +Write code like you would have to read it ten years from now. ## General Coding Standards Thrift has some history. Not all existing code follows those rules. But we want to improve over time. -When making small change / bugfix - like single line fix - do *not* refactor whole function. +When making a small change / bugfix - like a single line fix - do *not* refactor the whole function. That disturbs code repository history. Whenever adding something new and / or making bigger refactoring - follow those rules as strictly as you can. @@ -26,23 +26,23 @@ When in doubt - contact other developers (using dev@ mailing list or IRC). Code review is the best way to improve readability. ### Basics - * Use spaces not tabs + * Use spaces, not tabs * Use only ASCII characters in file and directory names - * Commit to repository using Unix-style line endings (LF) + * Commit to the repository using Unix-style line endings (LF) On Windows: git config core.autocrlf true * Maximum line width - 100 characters * If not specified otherwise in language specific standard - use 2 spaces as indent/tab ### Comments - * Each file has to start with comment containing [Apache License](http://www.apache.org/licenses/LICENSE-2.0) - * Public API of library should be documented, preferably using format native for language specific documentation generation tools (Javadoc, Doxygen etc.) + * Each file has to start with a comment containing [Apache License](http://www.apache.org/licenses/LICENSE-2.0) + * Public API of a library should be documented, preferably using format native for language-specific documentation generation tools (Javadoc, Doxygen etc.) * Other comments are discouraged - comments are lies. When one has to make comment it means one failed to write readable code. Instead of "I should write a comment here" think "I should clean it up" * Do not leave "TODO/FIXME" comments - file [Jira](http://issues.apache.org/jira/browse/THRIFT) issue instead ### Naming Finding proper names is the most important and most difficult task in software development. -## Language Specific Coding Standards +## Language-Specific Coding Standards For detailed information see `lib/LANG/coding_standards.md` diff --git a/doc/committers.md b/doc/committers.md index b02edbe00d2..0dad94fe21e 100644 --- a/doc/committers.md +++ b/doc/committers.md @@ -4,7 +4,7 @@ 1. Check out the latest version of the source code - * git clone https://git-wip-us.apache.org/repos/asf/thrift.git thrift + * git clone https://github.com/apache/thrift.git thrift 1. Apply the patch @@ -17,7 +17,7 @@ 1. Inspect the applied patch to ensure that all [Legal aspects on Submission of Contributions (Patches)](http://www.apache.org/licenses/LICENSE-2.0.html#contributions) are met -1. Run the necessary unit tests and cross language test cases to verify the patch +1. Run the necessary unit tests and cross-language test cases to verify the patch 1. Commit the patch @@ -28,17 +28,16 @@ 1. The commit message should be in the format: - THRIFT-###: + THRIFT-####: Client: Patch: Description of what was fixed or addressed. - <% - if this is a github pull request then add below comment - to automaticaly close GitHub request. - %> - This closes #XX + If this is a github pull request then add the below comment to automatically close the GitHub request, + where #NNNN is the PR number: + + This closes #NNNN 1. Double check the patch committed and that nothing was missed then push the patch @@ -48,7 +47,7 @@ git push origin master -1. Resolve the jira issue and set the following for the changelog +1. Resolve the Jira issue and set the following for the changelog * Component the patch is for * fixVersion to the current version on master diff --git a/doc/install/README.md b/doc/install/README.md index e37f4ff0fb0..3bc4d3559b8 100644 --- a/doc/install/README.md +++ b/doc/install/README.md @@ -3,7 +3,7 @@ * A relatively POSIX-compliant *NIX system * Cygwin or MinGW can be used on Windows (but there are better options, see below) * g++ 4.2 -* boost 1.53.0 +* boost 1.56.0 * Runtime libraries for lex and yacc might be needed for the compiler. ## Requirements for building from source @@ -23,12 +23,13 @@ These are only required if you choose to build the libraries for the given language * C++ - * Boost 1.53.0 + * Boost 1.56.0 * libevent (optional, to build the nonblocking server) * zlib (optional) + * Qt (optional) * Java - * Java 1.7 - * Apache Ant + * Java 1.8 + * Gradle * C#: Mono 1.2.4 (and pkg-config to detect it) or Visual Studio 2005+ * Python 2.6 (including header files for extension modules) * PHP 5.0 (optionally including header files for extension modules) diff --git a/doc/install/centos.md b/doc/install/centos.md index 609e118ead0..18282a3a189 100644 --- a/doc/install/centos.md +++ b/doc/install/centos.md @@ -54,17 +54,17 @@ If you will be developing Apache Thrift clients/servers in C++ you will also nee sudo yum -y install libevent-devel zlib-devel openssl-devel -### Upgrade Boost >= 1.53 +### Upgrade Boost >= 1.56 - wget http://sourceforge.net/projects/boost/files/boost/1.53.0/boost_1_53_0.tar.gz - tar xvf boost_1_53_0.tar.gz - cd boost_1_53_0 + wget http://sourceforge.net/projects/boost/files/boost/1.56.0/boost_1_56_0.tar.gz + tar xvf boost_1_56_0.tar.gz + cd boost_1_56_0 ./bootstrap.sh sudo ./b2 install ## Build and Install the Apache Thrift IDL Compiler - git clone https://git-wip-us.apache.org/repos/asf/thrift.git + git clone https://github.com/apache/thrift.git cd thrift ./bootstrap.sh ./configure --with-lua=no diff --git a/doc/install/debian.md b/doc/install/debian.md index 83090ab336f..d6b550088d8 100644 --- a/doc/install/debian.md +++ b/doc/install/debian.md @@ -18,8 +18,8 @@ Debian 7/Ubuntu 12 users need to manually install a more recent version of autom If you would like to build Apache Thrift libraries for other programming languages you may need to install additional packages. The following languages require the specified additional packages: * Java - * packages: ant - * You will also need Java JDK v1.7 or higher. Type **javac** to see a list of available packages, pick the one you prefer and **apt-get install** it (e.g. default-jdk). + * packages: gradle + * You will also need Java JDK v1.8 or higher. Type **javac** to see a list of available packages, pick the one you prefer and **apt-get install** it (e.g. default-jdk). * Ruby * ruby-full ruby-dev ruby-rspec rake rubygems bundler * Python @@ -32,8 +32,8 @@ If you would like to build Apache Thrift libraries for other programming languag * libglib2.0-dev * Erlang * erlang-base erlang-eunit erlang-dev rebar - * Csharp - * mono-gmcs mono-devel libmono-system-web2.0-cil nunit nunit-console + * NetStd + * apt-transport-https dotnet-sdk-3.1 aspnetcore-runtime-3.1 * Haskell * ghc cabal-install libghc-binary-dev libghc-network-dev libghc-http-dev * Thrift Compiler for Windows diff --git a/doc/install/windows.md b/doc/install/windows.md index 7b09840bef0..065b5742e39 100644 --- a/doc/install/windows.md +++ b/doc/install/windows.md @@ -1,6 +1,6 @@ ## Windows Setup -The Thrift environment consists of two main parts: The Thrift compiler EXE and the language-dependent libraries. Most of these libraries will require some kind of build and/or installation. But regarding the Thrift compiler utility there are a number of different alternatives. +The Thrift environment consists of two main parts: The Thrift compiler EXE and the language-dependent libraries. Most of these libraries will require some kind of build and/or installation. But regarding the Thrift compiler utility, there are a number of different alternatives. The first one of these alternatives is to download the **pre-built Thrift Compiler EXE** and only build the libraries needed from source, following one of the "Setup from source" methods outlined below. @@ -112,7 +112,7 @@ This can be implemented in a Makefile using the following lines: Then linking using $(LTHRIFT) instead of -lthrift. - TODO - diagnose issue further + TODO - diagnose the issue further #### C++ runtime segfault with cygwin 1.7.5-1, g++-4.3.4, fork() and throw @@ -131,7 +131,7 @@ To compile the Thrift generator & runtime libraries (untested) without the cygwi * MinGW * [Apache Thrift Requirements](/docs/install) -In addition you need to add the following entry to your windows PATH variable. +In addition, you need to add the following entry to your windows PATH variable. C:\MINGW\BIN @@ -145,7 +145,7 @@ Run bootstrap.sh: Make sure you have java in your $PATH variable, if not do(adjust path if necessary): - export PATH=$PATH:"/cygdrive/c/program files/java/jre1.6.0_05/bin" + export PATH=$PATH:"/cygdrive/c/program files/java/jre1.8.0_191/bin" Run configure - using CXXFLAGS to work around an issue with an old pthreads define (untested on MinGW - works on Cygwin): diff --git a/doc/specs/HeaderFormat.md b/doc/specs/HeaderFormat.md index 42ec7ae3880..9b74159a564 100644 --- a/doc/specs/HeaderFormat.md +++ b/doc/specs/HeaderFormat.md @@ -46,7 +46,7 @@ doesn't know about the transform ID, an error MUST be returned as we don't know how to transform the data. Conversely, data in the info headers is ignorable. This should only -be things like timestamps, debuging tracing, etc. Using the header +be things like timestamps, debugging tracing, etc. Using the header size you should be able to skip this data and read the payload safely if you don't know the info ID. @@ -61,7 +61,7 @@ Info ID's and transform ID's should share the same ID space. Header will be padded out to next 4-byte boundary with `0x00`. Max frame size is `0x3FFFFFFF`, which is slightly less than `HTTP_MAGIC`. -This allows us to distingush between different (older) transports. +This allows us to distinguish between different (older) transports. ### Transform IDs: @@ -74,7 +74,7 @@ This allows us to distingush between different (older) transports. data. -###Info IDs: +### Info IDs: INFO_KEYVALUE 0x01 - varint32 number of headers. - key/value pairs of varstrings (varint16 length plus diff --git a/doc/specs/SequenceNumbers.md b/doc/specs/SequenceNumbers.md new file mode 100644 index 00000000000..fef3fcff198 --- /dev/null +++ b/doc/specs/SequenceNumbers.md @@ -0,0 +1,23 @@ +# Sequence Number # + +Apache Thrift built sequence numbers into every protocol exchange to allow +for clients that may submit multiple outstanding requests on a single transport +connection. This is typically done by asynchronous clients. + +The following rules apply to sequence numbers: + +1. A sequence number is a signed 32-bit integer. Negative values are allowed. +1. Sequence numbers `MUST` be unique across all outstanding requests on a + given transport connection. There is no requirement for unique numbers + between different transport connections even if they are from the same client. +1. A server `MUST` reply to a client with the same sequence number that was + used in the request. This includes any exception-based reply. +1. A client `MAY` use sequence numbers if it needs them for proper operation. +1. A client `SHOULD` set the sequence number to zero if it does not rely + on them. +1. Wrapped protocols (such as THeaderProtocol) `SHOULD` use the same sequence + number on the wrapping as is used on the payload protocol. + +Servers will not inspect or make any logic choices based on the sequence number +sent by the client. The server's only job is to process the request and reply +with the same sequence number. diff --git a/doc/specs/idl.md b/doc/specs/idl.md index dab04c7692b..39b2dddb00a 100644 --- a/doc/specs/idl.md +++ b/doc/specs/idl.md @@ -1,10 +1,11 @@ ## Thrift interface description language + +For Thrift version 0.14.0. + The Thrift interface definition language (IDL) allows for the definition of [Thrift Types](/docs/types). A Thrift IDL file is processed by the Thrift code generator to produce code for the various target languages to support the defined structs and services in the IDL file. ## Description -*Under construction* - Here is a description of the Thrift IDL. ## Document @@ -35,32 +36,9 @@ A C++ include adds a custom C++ include to the output of the C++ code generator A namespace declares which namespaces/package/module/etc. the type definitions in this file will be declared in for the target languages. The namespace scope indicates which language the namespace applies to; a scope of '*' indicates that the namespace applies to all target languages. - [5] Namespace ::= ( 'namespace' ( NamespaceScope Identifier ) | - ( 'smalltalk.category' STIdentifier ) | - ( 'smalltalk.prefix' Identifier ) ) | - ( 'php_namespace' Literal ) | - ( 'xsd_namespace' Literal ) - - [6] NamespaceScope ::= '*' | 'cpp' | 'java' | 'py' | 'perl' | 'rb' | 'cocoa' | 'csharp' - -N.B.: Smalltalk has two distinct types of namespace commands: - -- smalltalk.prefix: Prepended to generated classnames. - - Smalltalk does not have namespaces for classes, so prefixes - are used to avoid class-name collisions. - Often, the prefix is the author's initials, like "KB" or "JWS", - or an abbreviation of the package name, like "MC" for "Monticello". -- smalltalk.category: Determines the category for generated classes. - Any dots in the identifier will be replaced with hyphens when generating - the category name. - If not provided, defaults to "Generated-" + the program name. - Methods will not be categorized beyond "as yet uncategorized". - - Smalltalk allows filing both classes and methods within classes into named - groups. These named groups of methods are called categories. - -N.B.: The `php_namespace` directive will be deprecated at some point in the future in favor of the scoped syntax, but the scoped syntax is not yet supported for PHP. + [5] Namespace ::= ( 'namespace' ( NamespaceScope Identifier ) ) -N.B.: The `xsd_namespace` directive has some purpose internal to Facebook but serves no purpose in Thrift itself. Use of this feature is strongly discouraged + [6] NamespaceScope ::= '*' | 'c_glib' | 'cpp' | 'delphi' | 'haxe' | 'go' | 'java' | 'js' | 'lua' | 'netstd' | 'perl' | 'php' | 'py' | 'py.twisted' | 'rb' | 'st' | 'xsd' ## Definition @@ -118,7 +96,7 @@ A service provides the interface for a set of functionality provided by a Thrift ## Field - [16] Field ::= FieldID? FieldReq? FieldType Identifier ('= ConstValue)? XsdFieldOptions ListSeparator? + [16] Field ::= FieldID? FieldReq? FieldType Identifier ('=' ConstValue)? XsdFieldOptions ListSeparator? ### Field ID @@ -126,7 +104,7 @@ A service provides the interface for a set of functionality provided by a Thrift ### Field Requiredness -There are two explicit requiredness values, and a third one that is applied implicity if neither *required* nor *optional* are given: *default* requiredness. +There are two explicit requiredness values, and a third one that is applied implicitly if neither *required* nor *optional* are given: *default* requiredness. [18] FieldReq ::= 'required' | 'optional' @@ -148,7 +126,7 @@ Because of this behaviour, required fields drastically limit the options with re - Read: Optional fields may, or may not be part of the input stream. - Default values: are written when the isset flag is set -Most language implementations use the recommended pratice of so-called "isset" flags to indicate whether a particular optional field is set or not. Only fields with this flag set are written, and conversely the flag is only set when a field value has been read from the input stream. +Most language implementations use the recommended practice of so-called "isset" flags to indicate whether a particular optional field is set or not. Only fields with this flag set are written, and conversely the flag is only set when a field value has been read from the input stream. #### default requiredness (implicit) @@ -166,7 +144,7 @@ The major point to keep in mind here is the fact, that any unwritten default val ### XSD Options -N.B.: These have some internal purpose at Facebook but serve no current purpose in Thrift. Use of these options is strongly discouraged. +N.B.: These have some internal purpose at Facebook but serve no current purpose in Thrift. The use of these options is strongly discouraged. [19] XsdFieldOptions ::= 'xsd_optional'? 'xsd_nillable'? XsdAttrs? @@ -242,12 +220,12 @@ Here are some examples of Thrift definitions, using the Thrift IDL: * [Apache Cassandra's][] Thrift IDL: [cassandra.thrift][] * [Evernote API][] - [ThriftTest.thrift]: https://git-wip-us.apache.org/repos/asf?p=thrift.git;a=blob_plain;f=test/ThriftTest.thrift;hb=HEAD + [ThriftTest.thrift]: https://raw.githubusercontent.com/apache/thrift/master/test/ThriftTest.thrift [tutorial]: /tutorial/ - [fb303.thrift]: https://git-wip-us.apache.org/repos/asf?p=thrift.git;a=blob_plain;f=contrib/fb303/if/fb303.thrift;hb=HEAD + [fb303.thrift]: https://raw.githubusercontent.com/apache/thrift/master/contrib/fb303/if/fb303.thrift [Apache Cassandra's]: http://cassandra.apache.org/ - [cassandra.thrift]: http://svn.apache.org/viewvc/cassandra/trunk/interface/cassandra.thrift?view=co - [Evernote API]: http://www.evernote.com/about/developer/api/ + [cassandra.thrift]: https://gitbox.apache.org/repos/asf?p=cassandra.git;a=blob_plain;f=interface/cassandra.thrift;hb=refs/heads/cassandra-3.0 + [Evernote API]: https://github.com/evernote/evernote-thrift ## To Do/Questions @@ -258,7 +236,7 @@ Initialization of Base Types for all Languages? Why does position of `CppType` vary between `SetType` and `ListType`? * std::set does sort the elements automatically, that's the design. see [Thrift Types](/docs/types) or the [C++ std:set reference][] for further details - * The question is, how other languages are doing that? What about custom objects, do they have a Compare function the set the order correctly? + * The question is, how other languages are doing that? What about custom objects, do they have a Compare function to set the order correctly? [C++ std:set reference]: http://www.cplusplus.com/reference/stl/set/ diff --git a/doc/specs/thrift-binary-protocol.md b/doc/specs/thrift-binary-protocol.md index a8526851720..fc3338f6b31 100644 --- a/doc/specs/thrift-binary-protocol.md +++ b/doc/specs/thrift-binary-protocol.md @@ -24,10 +24,10 @@ under the License. -------------------------------------------------------------------- --> -This documents describes the wire encoding for RPC using the older Thrift *binary protocol*. +This document describes the wire encoding for RPC using the older Thrift *binary protocol*. The information here is _mostly_ based on the Java implementation in the Apache thrift library (version 0.9.1 and -0.9.3). Other implementation however, should behave the same. +0.9.3). Other implementation, however, should behave the same. For background on Thrift see the [Thrift whitepaper (pdf)](https://thrift.apache.org/static/files/thrift-20070401.pdf). @@ -150,7 +150,7 @@ determine how to decode the field value. Note that the field name is not encoded so field renames in the IDL do not affect forward and backward compatibility. The default Java implementation (Apache Thrift 0.9.1) has undefined behavior when it tries to decode a field that has -another field-type then what is expected. Theoretically this could be detected at the cost of some additional checking. +another field-type than what is expected. Theoretically, this could be detected at the cost of some additional checking. Other implementation may perform this check and then either ignore the field, or return a protocol exception. A *Union* is encoded exactly the same as a struct with the additional restriction that at most 1 field may be encoded. @@ -213,7 +213,7 @@ Where: The element-type values are the same as field-types. The full list is included in the struct section above. -The maximum list/set size is configurable. By default there is no limit (meaning the limit is the maximum int32 value: +The maximum list/set size is configurable. By default, there is no limit (meaning the limit is the maximum int32 value: 2147483647). ## Map diff --git a/doc/specs/thrift-compact-protocol.md b/doc/specs/thrift-compact-protocol.md index 02467dd19fa..6be2a62f8cd 100644 --- a/doc/specs/thrift-compact-protocol.md +++ b/doc/specs/thrift-compact-protocol.md @@ -97,8 +97,9 @@ Where: ### Double encoding Values of type `double` are first converted to an int64 according to the IEEE 754 floating-point "double format" bit -layout. Most run-times provide a library to make this conversion. Both the binary protocol as the compact protocol then -encode the int64 in 8 bytes in big endian order. +layout. Most run-times provide a library to make this conversion. But while the binary protocol encodes the int64 +in 8 bytes in big endian order, the compact protocol encodes it in little endian order - this is due to an early +implementation bug that finally became the de-facto standard. ### Boolean encoding diff --git a/doc/specs/thrift-protocol-spec.md b/doc/specs/thrift-protocol-spec.md index 0c1a61cb26b..080487e1bd8 100644 --- a/doc/specs/thrift-protocol-spec.md +++ b/doc/specs/thrift-protocol-spec.md @@ -33,7 +33,7 @@ implementation, but this document specifies the minimum required structure. There are some "dumb" terminals like STRING and INT that take the place of an actual encoding specification. -They key point to notice is that ALL messages are just one wrapped +The key point to notice is that ALL messages are just one wrapped ``. Depending upon the message type, the `` can be interpreted as the argument list to a function, the return value of a function, or an exception. diff --git a/doc/specs/thrift-rpc.md b/doc/specs/thrift-rpc.md index d45c06f3041..fbff3b6a1db 100644 --- a/doc/specs/thrift-rpc.md +++ b/doc/specs/thrift-rpc.md @@ -24,15 +24,15 @@ under the License. -------------------------------------------------------------------- --> -This document describes the high level message exchange between the Thrift RPC client and server. +This document describes the high-level message exchange between the Thrift RPC client and server. See [thrift-binary-protocol.md] and [thrift-compact-protocol.md] for a description of how the exchanges are encoded on the wire. -In addition, this document compares the binary protocol with the compact protocol. Finally it describes the framed vs. +In addition, this document compares the binary protocol with the compact protocol. Finally, it describes the framed vs. unframed transport. The information here is _mostly_ based on the Java implementation in the Apache thrift library (version 0.9.1 and -0.9.3). Other implementation however, should behave the same. +0.9.3). Other implementation, however, should behave the same. For background on Thrift see the [Thrift whitepaper (pdf)](https://thrift.apache.org/static/files/thrift-20070401.pdf). @@ -93,14 +93,14 @@ in the Thrift IDL file, or some other part of the Thrift stack throws an excepti not encode or decode a message or struct. In the Java implementation (0.9.3) there is different behavior for the synchronous and asynchronous server. In the async -server all exceptions are send as a `TApplicationException` (see 'Response struct' below). In the synchronous Java +server all exceptions are sent as a `TApplicationException` (see 'Response struct' below). In the synchronous Java implementation only (undeclared) exceptions that extend `TException` are send as a `TApplicationException`. Unchecked exceptions lead to an immediate close of the connection. Type `Oneway` is only used starting from Apache Thrift 0.9.3. Earlier versions do _not_ send TMessages of type `Oneway`, even for service methods defined with the `oneway` modifier. -When client sends a request with type `Oneway`, the server must _not_ send a response (steps 3 and 4 are skipped). Note +When the client sends a request with type `Oneway`, the server must _not_ send a response (steps 3 and 4 are skipped). Note that the Thrift IDL enforces a return type of `void` and does not allow exceptions for oneway services. ## Request struct @@ -151,7 +151,7 @@ always slightly faster. ## Compatibility A server could automatically determine whether a client talks the binary protocol or the compact protocol by -investigating the first byte. If the value is `1000 0001` or `0000 0000` (assuming a name shorter then ±16 MB) it is the +investigating the first byte. If the value is `1000 0001` or `0000 0000` (assuming a name shorter than ±16 MB) it is the binary protocol. When the value is `1000 0010` it is talking the compact protocol. ## Framed vs. unframed transport diff --git a/doc/specs/thrift-tconfiguration.md b/doc/specs/thrift-tconfiguration.md new file mode 100644 index 00000000000..e7736cf9c29 --- /dev/null +++ b/doc/specs/thrift-tconfiguration.md @@ -0,0 +1,92 @@ +Thrift TConfiguration +==================================================================== + +Last Modified: 2019-Dec-03 + + + +Starting with THRIFT-5021 the need to centralize certain limit settings that are used throughout the whole protocol / transport stack became an obvious need. Previous patches already added some of these limits, but they were not consistently managed and just randomly distributed across the code base. + +# Design goals + +Following the tradition of similar experience across languages in Thrift, any implementation should meet these design goals: + + * There MUST be a standard CTOR (or equivalent thereof) that provides a default TConfiguration instance. + * The default values used SHOULD be implemented as outlined below. + * For backwards compatibility, the protocol / transport stack should accept null TConfiguration argument, in which case it should fallback to a default instance automatically. This is to prevent from code-breaking changes as much as possible. + +# Implementation + +The new TConfiguration class or struct currently holds three settings: + +## MaxMessageSize + +The MaxMessageSize member defines the maximum size of a (received) message, in bytes. The default value is represented by a constant named DEFAULT_MAX_MESSAGE_SIZE, whose value is 100 * 1024 * 1024 bytes. + +## MaxFrameSize + +MaxFrameSize limits the size of one frame of data for the TFramedTransport. Since all implementations currently send messages in one frame only if TFramedTransport is used, this value may interfere with MaxMessageSize. In the case of an conflict, the smaller value of the two is used (see remark below). The default value is called DEFAULT_MAX_FRAME_SIZE and has a value of 16384000 bytes. + +## RecursionLimit + +The RecursionLimit defines, how deep structures may be nested into each other. The default named DEFAULT_RECURSION_DEPTH allows for structures nested up to 64 levels deep. + +# Further considerations + +## MaxFrameSize vs. MaxMessageSize + +The difference between the two options is, that MaxFrameSize exists much longer and it is used only in conjunction with TFramedTransport. In contrast, MaxMessageSize is intended to be a general device to be used with any transport or protocol. + +In order to combine both approaches in the most optimal way when using TFramedTransport, it is recommended that the implementation SHOULD update the remaining number of bytes to read based on the received frame size value for the current message. + +For calculation purposes it is important to know, that MaxFrameSize excludes the 4 bytes that hold the frame size, while MaxMessageSize is always looking at the whole data. Hence, when updating the remaining read byte count, the known message size should be set to frameSize + sizeof(i32). + +## Error handling + +If any limit is exceeded, an error should be thrown. Additionally, it may be helpful to check larger memory allocations against the remaining max number of bytes before the allocation attempt takes place. + +# Q&A + +## Is this a breaking change or not? + +There is actually two answers to that question. + +1. If done right, it should not be a breaking change vis-á-vis compiling your source code that uses Thrift. + +1. It may, however, be a breaking change in the way it limits the accepted overall size of messages or the accepted frame size. This behaviour is by design. If your application hits any of these limits during normal operation, it may require you to instantiate an actual TConfiguration and tweak the settings according to your needs. + +## Is splitting the general transport base class into Endpoint and Layered transport base classes necessary? + +No, it's not. However, it turned out that this split is a great help when it comes to managing the TConfiguration instance that is passed through the stack. Having two distinct base classes for each of the different transport types not only allows to implement a shared solution for this. + +The added benefit is, that a clear distinction between the two transport types makes the Thrift architectural idea much more clear to "newbie" developers. + +## I want to contribute an implementation of TConfiguration and I am not sure whether to pick class or struct? + +Short answer: Pick whatever is more efficient in the language of your choice. + +Technically, remember that the instance is passed down the stack and should therefore be cheap on copying. To ensure this and to make sure all pieces of the protocol / transport stack are really pointing to the same TConfiguration instance, we want to pass the instance **by reference** rather than by value. + +For example, in the C# language a class is a suitable choice for this, because classes are naturally reference parameters, while structs are not. + diff --git a/dub.json b/dub.json index 1e268600e56..af76afc0c9e 100644 --- a/dub.json +++ b/dub.json @@ -11,10 +11,10 @@ "version": "~>2.0.2" }, "openssl": { - "version": "~>1.1.6" + "version": ">=1.1.6" } }, - "systemDependencies": "requires openssl 1.0 until deimos module is updated", + "systemDependencies": "On systems with native openssl 1.0.x use dub package openssl~>1.1, on systems with native openssl 1.1.x use dub package openssl~>2.0", "targetType": "library", "sourcePaths": [ "lib/d/src" diff --git a/jitpack.yml b/jitpack.yml new file mode 100644 index 00000000000..a640d5d8edf --- /dev/null +++ b/jitpack.yml @@ -0,0 +1,2 @@ +install: + - gradle -Prelease=true -p lib/java/ clean install diff --git a/lib/Makefile.am b/lib/Makefile.am index b31560915e7..5d16256a473 100644 --- a/lib/Makefile.am +++ b/lib/Makefile.am @@ -20,21 +20,18 @@ SUBDIRS = json xml PRECROSS_TARGET = +if WITH_AS3 +SUBDIRS += as3 +endif + if WITH_CPP -# cpp dir is picked directly by plugin build -if !WITH_PLUGIN SUBDIRS += cpp endif -endif if WITH_C_GLIB SUBDIRS += c_glib endif -if WITH_MONO -SUBDIRS += csharp -endif - if WITH_JAVA SUBDIRS += java PRECROSS_TARGET += precross-java @@ -71,8 +68,8 @@ if WITH_DART SUBDIRS += dart endif -if WITH_DOTNETCORE -SUBDIRS += netcore +if WITH_DOTNET +SUBDIRS += netstd endif if WITH_GO @@ -102,11 +99,14 @@ if WITH_CL SUBDIRS += cl endif +if WITH_SWIFT +SUBDIRS += swift +endif + # All of the libs that don't use Automake need to go in here # so they will end up in our release tarballs. EXTRA_DIST = \ as3 \ - cocoa \ d \ dart \ delphi \ diff --git a/lib/as3/CMakeLists.txt b/lib/as3/CMakeLists.txt new file mode 100644 index 00000000000..999905da0aa --- /dev/null +++ b/lib/as3/CMakeLists.txt @@ -0,0 +1,68 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +if (IS_ABSOLUTE "${LIB_INSTALL_DIR}") + set(AS3_INSTALL_DIR "${LIB_INSTALL_DIR}/as3") +else () + set(AS3_INSTALL_DIR "${CMAKE_INSTALL_PREFIX}/${LIB_INSTALL_DIR}/as3") +endif () + +set(PRELEASE "true") +if (CMAKE_BUILD_TYPE MATCHES DEBUG) + set(PRELEASE "false") +endif () + +add_custom_target(ThriftAs3 ALL + COMMENT "Building as3 library using Gradle Wrapper" + COMMAND ${GRADLEW_EXECUTABLE} ${GRADLE_OPTS} compileFlex + --console=plain --no-daemon + -Prelease=${PRELEASE} + "-Pbuild.dir=${CMAKE_CURRENT_BINARY_DIR}/build" + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} + ) + +# Enable publishing from CMake if the publishing information is provided +if (NOT CMAKE_BUILD_TYPE MATCHES DEBUG) + add_custom_target(MavenPublishAs3 + COMMENT "Publishing as3 library to Apache Maven staging" + COMMAND ${GRADLEW_EXECUTABLE} ${GRADLE_OPTS} clean publishMavenPublicationToMavenRepository + --console=plain --no-daemon + -Prelease=${PRELEASE} + -Psign=${PRELEASE} + "-Pbuild.dir=${CMAKE_CURRENT_BINARY_DIR}/build" + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} + ) +endif () + +# Hook the CMake install process to the results from make ALL. +# This works best when 'make all && sudo make install/fast' is used. +# Using slash to end the source location to avoid copying the directory path. +install(DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/build/libs/ + DESTINATION ${AS3_INSTALL_DIR} + FILES_MATCHING PATTERN "libthrift-as3.swc") + +if (BUILD_TESTING) + add_test(NAME As3Test + COMMAND ${GRADLEW_EXECUTABLE} ${GRADLE_OPTS} test + --console=plain --no-daemon + -Prelease=${PRELEASE} + "-Pbuild.dir=${CMAKE_CURRENT_BINARY_DIR}/build" + "-Pthrift.compiler=${THRIFT_COMPILER}" + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}) +endif () diff --git a/compiler/cpp/src/thrift/plugin/Makefile.am b/lib/as3/Makefile.am similarity index 51% rename from compiler/cpp/src/thrift/plugin/Makefile.am rename to lib/as3/Makefile.am index e84cdbd9c0c..0b3c3befc77 100644 --- a/compiler/cpp/src/thrift/plugin/Makefile.am +++ b/lib/as3/Makefile.am @@ -16,32 +16,45 @@ # 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. -AUTOMAKE_OPTIONS = subdir-objects +all-local: + ./gradlew $(GRADLE_OPTS) compile \ + -Prelease=true \ + --console=plain -if WITH_PLUGIN -plugin_gen = plugin_types.h \ - plugin_types.cpp \ - plugin_constants.h \ - plugin_constants.cpp +install-exec-hook: + ./gradlew $(GRADLE_OPTS) publishToMavenLocal \ + -Prelease=true \ + --console=plain -BUILT_SOURCES = $(plugin_gen) -gen.stamp: plugin.thrift $(top_builddir)/compiler/cpp/src/thrift/thrift-bootstrap - @$(RM) -f gen.tmp - @touch gen.tmp - $(top_builddir)/compiler/cpp/src/thrift/thrift-bootstrap -gen cpp -out . $< - @mv -f gen.tmp $@ +clean-local: + ./gradlew $(GRADLE_OPTS) clean \ + -Prelease=true \ + --console=plain + $(RM) -r .gradle -$(plugin_gen): gen.stamp - @if test -f $@; then :; else \ - $(RM) -f gen.stamp; \ - $(MAKE) $(AM_MAKEFLAGS) gen.stamp; \ - fi +check-local: $(THRIFT) + ./gradlew $(GRADLE_OPTS) test \ + -Prelease=true \ + --console=plain -clean-local: - $(RM) version.h windows/version.h $(plugin_gen) -endif +maven-publish: + ./gradlew $(GRADLE_OPTS) publishMavenPublicationToMavenRepository \ + -Prelease=true \ + -Psign=true \ + --console=plain + +dist-hook: + $(RM) -r $(distdir)/.gradle/ + +EXTRA_DIST = \ + CMakeLists.txt \ + README.md \ + build.gradle \ + coding_standards.md \ + gradle \ + gradle.properties \ + gradlew \ + gradlew.bat \ + settings.gradle \ + src diff --git a/lib/as3/README.md b/lib/as3/README.md new file mode 100644 index 00000000000..c14f8c78b68 --- /dev/null +++ b/lib/as3/README.md @@ -0,0 +1,37 @@ +# Apache Thrift ActionScript Library + +## Building + +We use gradle and gradlefx to build the as3 library. Unfortunately gradlefx requires +an older version of gradle (2.5) but it still works - for now. If you use the docker +container to do the build, the Adobe Flex SDK 4.6 is installed and the FLEX_HOME +environment variable is configured: + + dev@ubuntu:~/thrift$ docker run -v $(pwd):/thrift/src:rw -it thrift/thrift-build:ubuntu-bionic /bin/bash + root@7624b61bbf84:/thrift/src# cd lib/as3 + root@7624b61bbf84:/thrift/src/lib/as3# ./gradlew -Prelease=true compileFlex + + ... + + :compileFlex UP-TO-DATE + + BUILD SUCCESSFUL + + Total time: 10.784 secs + + root@7624b61bbf84:/thrift/src/lib/as3# ls -ls build/ + total 4 + 4 -rw-r--r-- 1 root root 1379 Jan 22 19:23 libthrift-as3.swc + +## Publishing + +We use a similar gradle-based signing and publishing mechanism as in the java +library. See the java library [README.md](../java/README.md) for more details. + +To publish into a local .m2 repository you can mount a directory into the docker container, +for example: + + dev@ubuntu:~/thrift$ docker run -v~/.m2:/root/.m2 -v $(pwd):/thrift/src:rw -it thrift/thrift-build:ubuntu-bionic /bin/bash + root@7624b61bbf84:/thrift/src/lib/as3# ./gradlew -Prelease=true publishToMavenLocal + +You will find your `~/.m2` directory is now populated with a release build `swc`. diff --git a/lib/as3/build.gradle b/lib/as3/build.gradle new file mode 100644 index 00000000000..78534996ada --- /dev/null +++ b/lib/as3/build.gradle @@ -0,0 +1,56 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +buildscript { + repositories { + mavenLocal() + mavenCentral() + } + dependencies { + classpath group: 'org.gradlefx', name: 'gradlefx', version: '1.5.0' + } +} + +plugins { + id 'maven-publish' + id 'signing' +} + +apply plugin: 'gradlefx' + +description = 'Apache Thrift ActionScript Library' +frameworkLinkage = 'none' +group = property('thrift.groupid') +srcDirs = ['src'] +type = 'swc' + +// We use the SNAPSHOT suffix for non-release versions +if (Boolean.parseBoolean(project.release)) { + additionalCompilerOptions = ['-compiler.debug=false', '-compiler.strict=true'] + version = property('thrift.version') +} else { + additionalCompilerOptions = ['-compiler.debug=true', '-compiler.strict=true'] + version = property('thrift.version') + '-SNAPSHOT' +} + +defaultTasks 'compile' + +// Keeping the rest of the build logic in functional named scripts for clarity +apply from: 'gradle/publishing.gradle' + diff --git a/lib/as3/build.properties b/lib/as3/build.properties deleted file mode 100644 index 84636683c94..00000000000 --- a/lib/as3/build.properties +++ /dev/null @@ -1,5 +0,0 @@ -# Maven Ant tasks Jar details -mvn.ant.task.version=2.1.3 -mvn.repo=http://repo1.maven.org/maven2 -mvn.ant.task.url=${mvn.repo}/org/apache/maven/maven-ant-tasks/${mvn.ant.task.version} -mvn.ant.task.jar=maven-ant-tasks-${mvn.ant.task.version}.jar diff --git a/lib/as3/build.xml b/lib/as3/build.xml deleted file mode 100755 index 2b374ddde43..00000000000 --- a/lib/as3/build.xml +++ /dev/null @@ -1,182 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/lib/as3/gradle.properties b/lib/as3/gradle.properties new file mode 100644 index 00000000000..9ace275381c --- /dev/null +++ b/lib/as3/gradle.properties @@ -0,0 +1,23 @@ +# This file is shared currently between this Gradle build and the +# Ant builds for fd303 and JavaScript. Keep the dotted notation for +# the properties to minimize the changes in the dependencies. +thrift.version=0.14.0 +thrift.groupid=org.apache.thrift +release=false +sign=false + +# Local Install paths +install.path=/usr/local/lib +install.javadoc.path=/usr/local/lib + +# Test execution properties +testPort=9090 + +# Maven dependency download locations +mvn.repo=https://repo1.maven.org/maven2 +apache.repo=https://repository.apache.org/content/repositories/releases + +# Apache Maven publish +license=https://www.apache.org/licenses/LICENSE-2.0.txt +maven-repository-url=https://repository.apache.org/service/local/staging/deploy/maven2 +maven-repository-id=apache.releases.https diff --git a/lib/as3/gradle/publishing.gradle b/lib/as3/gradle/publishing.gradle new file mode 100644 index 00000000000..3e0ecf3192d --- /dev/null +++ b/lib/as3/gradle/publishing.gradle @@ -0,0 +1,96 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +// Following Gradle best practices to keep build logic organized + +model { + tasks.signMavenPublication { + dependsOn compileFlex + } +} + +publishing { + publications { + maven(MavenPublication) { + + groupId = "$group" + artifactId = "${project.name}" + version = "$version" + + def swcFile = file("$buildDir/libthrift-as3.swc") + artifact(swcFile) + + pom { + description = 'Thrift is a software framework for scalable cross-language services development.' + packaging = 'swc' + + // older gradle doesn't recognize all the properties, so we inject them.. + withXml { + asNode().with { + appendNode('name', 'Apache Thrift') + appendNode('url', 'http://thrift.apache.org/') + appendNode('scm').with { + appendNode('url', 'https://github.com/apache/thrift/') + appendNode('connection', 'scm:git:https://github.com/apache/thrift.git') + appendNode('developerConnection', 'scm:git:git@github.com:apache/thrift.git') + } + appendNode('issueManagement').with { + appendNode('url', 'https://issues.apache.org/jira/projects/THRIFT/') + appendNode('system', 'Jira') + } + appendNode('licenses').with { + appendNode('license').with { + appendNode('name', 'The Apache Software License, Version 2.0') + appendNode('url', "${project.license}") + } + } + appendNode('organization').with { + appendNode('name', 'The Apache Software Foundation') + appendNode('url', 'http://www.apache.org/') + } + appendNode('developers').with { + appendNode('developer').with { + appendNode('id', 'dev') + appendNode('name', 'Apache Thrift Developers') + appendNode('email', 'dev@thrift.apache.org') + } + } + } + } + } + } + } + repositories { + maven { + url = property('maven-repository-url') + + if (project.hasProperty('mavenUser') && project.hasProperty('mavenPassword')) { + credentials { + username = property('mavenUser') + password = property('mavenPassword') + } + } + } + } +} + +signing { + required { property('sign') } + sign publishing.publications.maven +} diff --git a/lib/as3/gradle/wrapper/gradle-wrapper.jar b/lib/as3/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 00000000000..87b738cbd05 Binary files /dev/null and b/lib/as3/gradle/wrapper/gradle-wrapper.jar differ diff --git a/lib/as3/gradle/wrapper/gradle-wrapper.properties b/lib/as3/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 00000000000..5028f28f8e4 --- /dev/null +++ b/lib/as3/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,5 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.4-bin.zip +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/lib/as3/gradlew b/lib/as3/gradlew new file mode 100755 index 00000000000..af6708ff229 --- /dev/null +++ b/lib/as3/gradlew @@ -0,0 +1,172 @@ +#!/usr/bin/env sh + +############################################################################## +## +## Gradle start up script for UN*X +## +############################################################################## + +# Attempt to set APP_HOME +# Resolve links: $0 may be a link +PRG="$0" +# Need this for relative symlinks. +while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >/dev/null +APP_HOME="`pwd -P`" +cd "$SAVED" >/dev/null + +APP_NAME="Gradle" +APP_BASE_NAME=`basename "$0"` + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m"' + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD="maximum" + +warn () { + echo "$*" +} + +die () { + echo + echo "$*" + echo + exit 1 +} + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "`uname`" in + CYGWIN* ) + cygwin=true + ;; + Darwin* ) + darwin=true + ;; + MINGW* ) + msys=true + ;; + NONSTOP* ) + nonstop=true + ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD="java" + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ $? -eq 0 ] ; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ] ; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# For Cygwin, switch paths to Windows format before running java +if $cygwin ; then + APP_HOME=`cygpath --path --mixed "$APP_HOME"` + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + JAVACMD=`cygpath --unix "$JAVACMD"` + + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` + SEP="" + for dir in $ROOTDIRSRAW ; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ] ; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@" ; do + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` + else + eval `echo args$i`="\"$arg\"" + fi + i=$((i+1)) + done + case $i in + (0) set -- ;; + (1) set -- "$args0" ;; + (2) set -- "$args0" "$args1" ;; + (3) set -- "$args0" "$args1" "$args2" ;; + (4) set -- "$args0" "$args1" "$args2" "$args3" ;; + (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Escape application args +save () { + for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done + echo " " +} +APP_ARGS=$(save "$@") + +# Collect all arguments for the java command, following the shell quoting and substitution rules +eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" + +# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong +if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then + cd "$(dirname "$0")" +fi + +exec "$JAVACMD" "$@" diff --git a/lib/as3/gradlew.bat b/lib/as3/gradlew.bat new file mode 100644 index 00000000000..9618d8d9607 --- /dev/null +++ b/lib/as3/gradlew.bat @@ -0,0 +1,100 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto init + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto init + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:init +@rem Get command-line arguments, handling Windows variants + +if not "%OS%" == "Windows_NT" goto win9xME_args + +:win9xME_args +@rem Slurp the command line arguments. +set CMD_LINE_ARGS= +set _SKIP=2 + +:win9xME_args_slurp +if "x%~1" == "x" goto execute + +set CMD_LINE_ARGS=%* + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/lib/cocoa/src/Thrift.h b/lib/as3/settings.gradle similarity index 95% rename from lib/cocoa/src/Thrift.h rename to lib/as3/settings.gradle index 502ba17596c..49323464190 100644 --- a/lib/cocoa/src/Thrift.h +++ b/lib/as3/settings.gradle @@ -17,4 +17,4 @@ * under the License. */ -#define ThriftVersion @"0.12.0" +rootProject.name = 'libthrift-as3' diff --git a/lib/as3/src/org/apache/thrift/protocol/TProtocolUtil.as b/lib/as3/src/org/apache/thrift/protocol/TProtocolUtil.as index 513df954bee..22877b75b2b 100644 --- a/lib/as3/src/org/apache/thrift/protocol/TProtocolUtil.as +++ b/lib/as3/src/org/apache/thrift/protocol/TProtocolUtil.as @@ -141,7 +141,7 @@ package org.apache.thrift.protocol { break; } default: - break; + throw new TProtocolError(TProtocolError.INVALID_DATA, "invalid data"); } } } diff --git a/lib/c_glib/CMakeLists.txt b/lib/c_glib/CMakeLists.txt index 3743a68dd85..3a1f1880c32 100644 --- a/lib/c_glib/CMakeLists.txt +++ b/lib/c_glib/CMakeLists.txt @@ -31,6 +31,7 @@ set(thrift_c_glib_SOURCES src/thrift/c_glib/thrift.c src/thrift/c_glib/thrift_struct.c src/thrift/c_glib/thrift_application_exception.c + src/thrift/c_glib/thrift_configuration.c src/thrift/c_glib/processor/thrift_processor.c src/thrift/c_glib/processor/thrift_dispatch_processor.c src/thrift/c_glib/processor/thrift_multiplexed_processor.c @@ -39,6 +40,7 @@ set(thrift_c_glib_SOURCES src/thrift/c_glib/protocol/thrift_protocol_decorator.c src/thrift/c_glib/protocol/thrift_binary_protocol.c src/thrift/c_glib/protocol/thrift_stored_message_protocol.c + src/thrift/c_glib/protocol/thrift_multiplexed_protocol.c src/thrift/c_glib/protocol/thrift_binary_protocol_factory.c src/thrift/c_glib/protocol/thrift_compact_protocol.c src/thrift/c_glib/protocol/thrift_compact_protocol_factory.c @@ -58,9 +60,8 @@ set(thrift_c_glib_SOURCES ) # If OpenSSL is not found just ignore the OpenSSL stuff -find_package(OpenSSL) -if(OPENSSL_FOUND AND WITH_OPENSSL) - list( APPEND thriftcpp_SOURCES +if(WITH_OPENSSL) + list(APPEND thrift_c_glib_SOURCES src/thrift/c_glib/transport/thrift_ssl_socket.c ) include_directories(SYSTEM "${OPENSSL_INCLUDE_DIR}") diff --git a/lib/c_glib/Makefile.am b/lib/c_glib/Makefile.am index 49b5b238cd9..7619fb4cbbb 100755 --- a/lib/c_glib/Makefile.am +++ b/lib/c_glib/Makefile.am @@ -16,7 +16,7 @@ # specific language governing permissions and limitations # under the License. # -AUTOMAKE_OPTIONS = serial-tests +AUTOMAKE_OPTIONS = serial-tests nostdinc SUBDIRS = . test pkgconfigdir = $(libdir)/pkgconfig @@ -32,6 +32,7 @@ AM_CFLAGS = -Wall -Wextra -pedantic libthrift_c_glib_la_SOURCES = src/thrift/c_glib/thrift.c \ src/thrift/c_glib/thrift_struct.c \ src/thrift/c_glib/thrift_application_exception.c \ + src/thrift/c_glib/thrift_configuration.c \ src/thrift/c_glib/processor/thrift_processor.c \ src/thrift/c_glib/processor/thrift_dispatch_processor.c \ src/thrift/c_glib/processor/thrift_multiplexed_processor.c \ @@ -59,7 +60,7 @@ libthrift_c_glib_la_SOURCES = src/thrift/c_glib/thrift.c \ src/thrift/c_glib/server/thrift_server.c \ src/thrift/c_glib/server/thrift_simple_server.c -libthrift_c_glib_la_CFLAGS = $(AM_CFLAGS) $(GLIB_CFLAGS) $(GOBJECT_CFLAGS) $(OPENSSL_INCLUDES) +libthrift_c_glib_la_CFLAGS = $(AM_CFLAGS) $(GLIB_CFLAGS) $(GOBJECT_CFLAGS) $(OPENSSL_INCLUDES) -I$(top_builddir)/lib/c_glib/src/thrift libthrift_c_glib_la_LDFLAGS = $(AM_LDFLAGS) $(GLIB_LIBS) $(GOBJECT_LIBS) $(OPENSSL_LDFLAGS) $(OPENSSL_LIBS) include_thriftdir = $(includedir)/thrift/c_glib @@ -67,7 +68,8 @@ include_thrift_HEADERS = \ $(top_builddir)/config.h \ src/thrift/c_glib/thrift.h \ src/thrift/c_glib/thrift_application_exception.h \ - src/thrift/c_glib/thrift_struct.h + src/thrift/c_glib/thrift_struct.h \ + src/thrift/c_glib/thrift_configuration.h include_protocoldir = $(include_thriftdir)/protocol include_protocol_HEADERS = src/thrift/c_glib/protocol/thrift_protocol.h \ diff --git a/lib/c_glib/src/thrift/c_glib/processor/thrift_multiplexed_processor.c b/lib/c_glib/src/thrift/c_glib/processor/thrift_multiplexed_processor.c index 68a0f4d46cb..7a5600ce5a0 100644 --- a/lib/c_glib/src/thrift/c_glib/processor/thrift_multiplexed_processor.c +++ b/lib/c_glib/src/thrift/c_glib/processor/thrift_multiplexed_processor.c @@ -41,6 +41,8 @@ static GParamSpec *thrift_multiplexed_processor_obj_properties[PROP_THRIFT_MULTI static gboolean thrift_multiplexed_processor_register_processor_impl(ThriftProcessor *processor, const gchar * multiplexed_processor_name, ThriftProcessor * multiplexed_processor , GError **error) { + THRIFT_UNUSED_VAR (error); + ThriftMultiplexedProcessor *self = THRIFT_MULTIPLEXED_PROCESSOR(processor); g_hash_table_replace(self->multiplexed_services, g_strdup(multiplexed_processor_name), @@ -213,13 +215,9 @@ thrift_multiplexed_processor_process_impl (ThriftProcessor *processor, ThriftPro } - /* - FIXME This makes everything fail, I don't know why. - if(stored_message_protocol!=NULL){ - // After its use we must free it - g_object_unref(stored_message_protocol); + if (stored_message_protocol != NULL) { + g_object_unref (stored_message_protocol); } - */ return retval; } @@ -336,11 +334,9 @@ thrift_multiplexed_processor_init (ThriftMultiplexedProcessor *self) self->default_processor_name = NULL; } - gboolean thrift_multiplexed_processor_register_processor(ThriftProcessor *processor, const gchar * multiplexed_processor_name, ThriftProcessor * multiplexed_processor , GError **error) { return THRIFT_MULTIPLEXED_PROCESSOR_GET_CLASS(processor)->register_processor(processor, multiplexed_processor_name, multiplexed_processor, error); } - diff --git a/lib/c_glib/src/thrift/c_glib/protocol/thrift_binary_protocol.c b/lib/c_glib/src/thrift/c_glib/protocol/thrift_binary_protocol.c index 7c2d017e166..9e80e107c2a 100644 --- a/lib/c_glib/src/thrift/c_glib/protocol/thrift_binary_protocol.c +++ b/lib/c_glib/src/thrift/c_glib/protocol/thrift_binary_protocol.c @@ -17,6 +17,7 @@ * under the License. */ +#include #include #include @@ -529,6 +530,9 @@ thrift_binary_protocol_read_map_begin (ThriftProtocol *protocol, g_return_val_if_fail (THRIFT_IS_BINARY_PROTOCOL (protocol), -1); + ThriftProtocol *tp = THRIFT_PROTOCOL (protocol); + ThriftTransportClass *ttc = THRIFT_TRANSPORT_GET_CLASS (tp->transport); + if ((ret = thrift_protocol_read_byte (protocol, &k, error)) < 0) { return -1; @@ -557,6 +561,14 @@ thrift_binary_protocol_read_map_begin (ThriftProtocol *protocol, return -1; } + if(!ttc->checkReadBytesAvailable (THRIFT_TRANSPORT(tp->transport), + sizei * thrift_binary_protocol_get_min_serialized_size(protocol, k, error) + + sizei * thrift_binary_protocol_get_min_serialized_size(protocol, v, error), + error)) + { + return -1; + } + *size = (guint32) sizei; return xfer; } @@ -582,6 +594,9 @@ thrift_binary_protocol_read_list_begin (ThriftProtocol *protocol, g_return_val_if_fail (THRIFT_IS_BINARY_PROTOCOL (protocol), -1); + ThriftProtocol *tp = THRIFT_PROTOCOL (protocol); + ThriftTransportClass *ttc = THRIFT_TRANSPORT_GET_CLASS (tp->transport); + if ((ret = thrift_protocol_read_byte (protocol, &e, error)) < 0) { return -1; @@ -603,6 +618,13 @@ thrift_binary_protocol_read_list_begin (ThriftProtocol *protocol, return -1; } + if(!ttc->checkReadBytesAvailable (THRIFT_TRANSPORT(tp->transport), + (sizei * thrift_binary_protocol_get_min_serialized_size(protocol, e, error)), + error)) + { + return -1; + } + *size = (guint32) sizei; return xfer; } @@ -855,6 +877,48 @@ thrift_binary_protocol_read_binary (ThriftProtocol *protocol, return xfer; } +gint +thrift_binary_protocol_get_min_serialized_size(ThriftProtocol *protocol, ThriftType type, GError **error) +{ + THRIFT_UNUSED_VAR (protocol); + + switch (type) + { + case T_STOP: + return 0; + case T_VOID: + return 0; + case T_BOOL: + return sizeof(gint8); + case T_BYTE: + return sizeof(gint8); + case T_DOUBLE: + return sizeof(double); + case T_I16: + return sizeof(short); + case T_I32: + return sizeof(int); + case T_I64: + return sizeof(long); + case T_STRING: + return sizeof(int); + case T_STRUCT: + return 0; + case T_MAP: + return sizeof(int); + case T_SET: + return sizeof(int); + case T_LIST: + return sizeof(int); + default: + g_set_error(error, + THRIFT_PROTOCOL_ERROR, + THRIFT_PROTOCOL_ERROR_INVALID_DATA, + "unrecognized type"); + return -1; + } +} + static void thrift_binary_protocol_init (ThriftBinaryProtocol *protocol) { @@ -908,4 +972,5 @@ thrift_binary_protocol_class_init (ThriftBinaryProtocolClass *klass) cls->read_double = thrift_binary_protocol_read_double; cls->read_string = thrift_binary_protocol_read_string; cls->read_binary = thrift_binary_protocol_read_binary; + cls->get_min_serialized_size = thrift_binary_protocol_get_min_serialized_size; } diff --git a/lib/c_glib/src/thrift/c_glib/protocol/thrift_binary_protocol.h b/lib/c_glib/src/thrift/c_glib/protocol/thrift_binary_protocol.h index 5230bcced10..bd8b84effbc 100644 --- a/lib/c_glib/src/thrift/c_glib/protocol/thrift_binary_protocol.h +++ b/lib/c_glib/src/thrift/c_glib/protocol/thrift_binary_protocol.h @@ -67,6 +67,9 @@ struct _ThriftBinaryProtocolClass /* used by THRIFT_TYPE_BINARY_PROTOCOL */ GType thrift_binary_protocol_get_type (void); +gint +thrift_binary_protocol_get_min_serialized_size(ThriftProtocol *protocol, ThriftType type, GError **error); + G_END_DECLS #endif /* _THRIFT_BINARY_PROTOCOL_H */ diff --git a/lib/c_glib/src/thrift/c_glib/protocol/thrift_compact_protocol.c b/lib/c_glib/src/thrift/c_glib/protocol/thrift_compact_protocol.c index cae4749f002..0aa9a6fe640 100644 --- a/lib/c_glib/src/thrift/c_glib/protocol/thrift_compact_protocol.c +++ b/lib/c_glib/src/thrift/c_glib/protocol/thrift_compact_protocol.c @@ -1051,6 +1051,8 @@ thrift_compact_protocol_read_map_begin (ThriftProtocol *protocol, gint32 msize; ThriftCompactProtocol *cp; + ThriftProtocol *tp = THRIFT_PROTOCOL (protocol); + ThriftTransportClass *ttc = THRIFT_TRANSPORT_GET_CLASS (tp->transport); g_return_val_if_fail (THRIFT_IS_COMPACT_PROTOCOL (protocol), -1); @@ -1106,6 +1108,14 @@ thrift_compact_protocol_read_map_begin (ThriftProtocol *protocol, return -1; } + if(!ttc->checkReadBytesAvailable (THRIFT_TRANSPORT (tp->transport), + msize * thrift_protocol_get_min_serialized_size (protocol, *key_type, error) + + msize * thrift_protocol_get_min_serialized_size (protocol, *value_type, error), + error)) + { + return -1; + } + return xfer; } @@ -1124,7 +1134,8 @@ thrift_compact_protocol_read_list_begin (ThriftProtocol *protocol, guint32 *size, GError **error) { ThriftCompactProtocol *cp; - + ThriftProtocol *tp = THRIFT_PROTOCOL (protocol); + ThriftTransportClass *ttc = THRIFT_TRANSPORT_GET_CLASS (tp->transport); gint32 ret; gint32 xfer; @@ -1172,6 +1183,13 @@ thrift_compact_protocol_read_list_begin (ThriftProtocol *protocol, *element_type = ret; *size = (guint32) lsize; + if(!ttc->checkReadBytesAvailable (THRIFT_TRANSPORT (tp->transport), + (lsize * thrift_protocol_get_min_serialized_size (protocol, *element_type, error)), + error)) + { + return -1; + } + return xfer; } @@ -1479,6 +1497,48 @@ thrift_compact_protocol_read_binary (ThriftProtocol *protocol, return xfer; } +gint +thrift_compact_protocol_get_min_serialized_size (ThriftProtocol *protocol, ThriftType type, GError **error) +{ + THRIFT_UNUSED_VAR (protocol); + + switch (type) + { + case T_STOP: + return 0; + case T_VOID: + return 0; + case T_BOOL: + return sizeof(gint8); + case T_DOUBLE: + return 8; + case T_BYTE: + return sizeof(gint8); + case T_I16: + return sizeof(gint8); + case T_I32: + return sizeof(gint8); + case T_I64: + return sizeof(gint8); + case T_STRING: + return sizeof(gint8); + case T_STRUCT: + return 0; + case T_MAP: + return sizeof(gint8); + case T_SET: + return sizeof(gint8); + case T_LIST: + return sizeof(gint8); + default: + g_set_error(error, + THRIFT_PROTOCOL_ERROR, + THRIFT_PROTOCOL_ERROR_INVALID_DATA, + "unrecognized type"); + return -1; + } +} + /* property accessor */ void thrift_compact_protocol_get_property (GObject *object, guint property_id, @@ -1600,6 +1660,7 @@ thrift_compact_protocol_class_init (ThriftCompactProtocolClass *klass) cls->read_double = thrift_compact_protocol_read_double; cls->read_string = thrift_compact_protocol_read_string; cls->read_binary = thrift_compact_protocol_read_binary; + cls->get_min_serialized_size = thrift_compact_protocol_get_min_serialized_size; } static void diff --git a/lib/c_glib/src/thrift/c_glib/protocol/thrift_multiplexed_protocol.c b/lib/c_glib/src/thrift/c_glib/protocol/thrift_multiplexed_protocol.c index 727f4a87c44..38e3fd4b40b 100644 --- a/lib/c_glib/src/thrift/c_glib/protocol/thrift_multiplexed_protocol.c +++ b/lib/c_glib/src/thrift/c_glib/protocol/thrift_multiplexed_protocol.c @@ -40,7 +40,7 @@ G_DEFINE_TYPE(ThriftMultiplexedProtocol, thrift_multiplexed_protocol, THRIFT_TYP static GParamSpec *thrift_multiplexed_protocol_obj_properties[PROP_THRIFT_MULTIPLEXED_PROTOCOL_END] = { NULL, }; gint32 -thrift_multiplexed_protocol_write_message_begin (ThriftMultiplexedProtocol *protocol, +thrift_multiplexed_protocol_write_message_begin (ThriftProtocol *protocol, const gchar *name, const ThriftMessageType message_type, const gint32 seqid, GError **error) { @@ -49,7 +49,6 @@ thrift_multiplexed_protocol_write_message_begin (ThriftMultiplexedProtocol *prot g_return_val_if_fail (THRIFT_IS_MULTIPLEXED_PROTOCOL (protocol), -1); ThriftMultiplexedProtocol *self = THRIFT_MULTIPLEXED_PROTOCOL (protocol); - ThriftMultiplexedProtocolClass *multiplexClass = THRIFT_MULTIPLEXED_PROTOCOL_GET_CLASS(self); if( (message_type == T_CALL || message_type == T_ONEWAY) && self->service_name != NULL) { service_name = g_strdup_printf("%s%s%s", self->service_name, THRIFT_MULTIPLEXED_PROTOCOL_DEFAULT_SEPARATOR, name); @@ -77,6 +76,7 @@ thrift_multiplexed_protocol_set_property (GObject *object, switch (property_id) { case PROP_THRIFT_MULTIPLEXED_PROTOCOL_SERVICE_NAME: + g_free(self->service_name); self->service_name = g_value_dup_string (value); break; diff --git a/lib/c_glib/src/thrift/c_glib/protocol/thrift_protocol.c b/lib/c_glib/src/thrift/c_glib/protocol/thrift_protocol.c index 8296a8cad13..252f4bec96f 100644 --- a/lib/c_glib/src/thrift/c_glib/protocol/thrift_protocol.c +++ b/lib/c_glib/src/thrift/c_glib/protocol/thrift_protocol.c @@ -419,6 +419,20 @@ thrift_protocol_read_binary (ThriftProtocol *protocol, gpointer *buf, len, error); } +gint +thrift_protocol_get_min_serialized_size (ThriftProtocol *protocol, ThriftType type, GError ** error) +{ + return THRIFT_PROTOCOL_GET_CLASS (protocol)->get_min_serialized_size (protocol, + type, error); +} + +#define THRIFT_SKIP_RESULT_OR_RETURN(_RES, _CALL) \ + { \ + gint32 _x = (_CALL); \ + if (_x < 0) { return _x; } \ + (_RES) += _x; \ + } + gint32 thrift_protocol_skip (ThriftProtocol *protocol, ThriftType type, GError **error) { @@ -469,24 +483,24 @@ thrift_protocol_skip (ThriftProtocol *protocol, ThriftType type, GError **error) gchar *name; gint16 fid; ThriftType ftype; - result += thrift_protocol_read_struct_begin (protocol, &name, error); - + THRIFT_SKIP_RESULT_OR_RETURN(result, + thrift_protocol_read_struct_begin (protocol, &name, error)) while (1) { - result += thrift_protocol_read_field_begin (protocol, &name, &ftype, - &fid, error); - if (result < 0) - { - return result; - } + THRIFT_SKIP_RESULT_OR_RETURN(result, + thrift_protocol_read_field_begin (protocol, &name, &ftype, + &fid, error)) if (ftype == T_STOP) { break; } - result += thrift_protocol_skip (protocol, ftype, error); - result += thrift_protocol_read_field_end (protocol, error); + THRIFT_SKIP_RESULT_OR_RETURN(result, + thrift_protocol_skip (protocol, ftype, error)) + THRIFT_SKIP_RESULT_OR_RETURN(result, + thrift_protocol_read_field_end (protocol, error)) } - result += thrift_protocol_read_struct_end (protocol, error); + THRIFT_SKIP_RESULT_OR_RETURN(result, + thrift_protocol_read_struct_end (protocol, error)) return result; } case T_SET: @@ -494,13 +508,16 @@ thrift_protocol_skip (ThriftProtocol *protocol, ThriftType type, GError **error) gint32 result = 0; ThriftType elem_type; guint32 i, size; - result += thrift_protocol_read_set_begin (protocol, &elem_type, &size, - error); + THRIFT_SKIP_RESULT_OR_RETURN(result, + thrift_protocol_read_set_begin (protocol, &elem_type, &size, + error)) for (i = 0; i < size; i++) { - result += thrift_protocol_skip (protocol, elem_type, error); + THRIFT_SKIP_RESULT_OR_RETURN(result, + thrift_protocol_skip (protocol, elem_type, error)) } - result += thrift_protocol_read_set_end (protocol, error); + THRIFT_SKIP_RESULT_OR_RETURN(result, + thrift_protocol_read_set_end (protocol, error)) return result; } case T_MAP: @@ -509,14 +526,18 @@ thrift_protocol_skip (ThriftProtocol *protocol, ThriftType type, GError **error) ThriftType elem_type; ThriftType key_type; guint32 i, size; - result += thrift_protocol_read_map_begin (protocol, &key_type, &elem_type, &size, - error); + THRIFT_SKIP_RESULT_OR_RETURN(result, + thrift_protocol_read_map_begin (protocol, &key_type, &elem_type, &size, + error)) for (i = 0; i < size; i++) { - result += thrift_protocol_skip (protocol, key_type, error); - result += thrift_protocol_skip (protocol, elem_type, error); + THRIFT_SKIP_RESULT_OR_RETURN(result, + thrift_protocol_skip (protocol, key_type, error)) + THRIFT_SKIP_RESULT_OR_RETURN(result, + thrift_protocol_skip (protocol, elem_type, error)) } - result += thrift_protocol_read_map_end (protocol, error); + THRIFT_SKIP_RESULT_OR_RETURN(result, + thrift_protocol_read_map_end (protocol, error)) return result; } case T_LIST: @@ -524,18 +545,26 @@ thrift_protocol_skip (ThriftProtocol *protocol, ThriftType type, GError **error) gint32 result = 0; ThriftType elem_type; guint32 i, size; - result += thrift_protocol_read_list_begin (protocol, &elem_type, &size, - error); + THRIFT_SKIP_RESULT_OR_RETURN(result, + thrift_protocol_read_list_begin (protocol, &elem_type, &size, + error)) for (i = 0; i < size; i++) { - result += thrift_protocol_skip (protocol, elem_type, error); + THRIFT_SKIP_RESULT_OR_RETURN(result, + thrift_protocol_skip (protocol, elem_type, error)) } - result += thrift_protocol_read_list_end (protocol, error); + THRIFT_SKIP_RESULT_OR_RETURN(result, + thrift_protocol_read_list_end (protocol, error)) return result; } default: - return 0; + break; } + + g_set_error (error, THRIFT_PROTOCOL_ERROR, + THRIFT_PROTOCOL_ERROR_INVALID_DATA, + "unrecognized type"); + return -1; } /* define the GError domain for Thrift protocols */ @@ -576,10 +605,10 @@ thrift_protocol_class_init (ThriftProtocolClass *cls) gobject_class->dispose = thrift_protocol_dispose; g_object_class_install_property (gobject_class, - PROP_THRIFT_PROTOCOL_TRANSPORT, - g_param_spec_object ("transport", "Transport", "Thrift Transport", - THRIFT_TYPE_TRANSPORT, - G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); + PROP_THRIFT_PROTOCOL_TRANSPORT, + g_param_spec_object ("transport", "Transport", "Thrift Transport", + THRIFT_TYPE_TRANSPORT, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); cls->write_message_begin = thrift_protocol_write_message_begin; cls->write_message_end = thrift_protocol_write_message_end; @@ -621,4 +650,5 @@ thrift_protocol_class_init (ThriftProtocolClass *cls) cls->read_double = thrift_protocol_read_double; cls->read_string = thrift_protocol_read_string; cls->read_binary = thrift_protocol_read_binary; + cls->get_min_serialized_size = thrift_protocol_get_min_serialized_size; } diff --git a/lib/c_glib/src/thrift/c_glib/protocol/thrift_protocol.h b/lib/c_glib/src/thrift/c_glib/protocol/thrift_protocol.h index 58fe5e0afa9..d9369a66d08 100644 --- a/lib/c_glib/src/thrift/c_glib/protocol/thrift_protocol.h +++ b/lib/c_glib/src/thrift/c_glib/protocol/thrift_protocol.h @@ -86,6 +86,7 @@ struct _ThriftProtocol /* protected */ ThriftTransport *transport; + }; typedef struct _ThriftProtocolClass ThriftProtocolClass; @@ -171,6 +172,7 @@ struct _ThriftProtocolClass gint32 (*read_string) (ThriftProtocol *protocol, gchar **str, GError **error); gint32 (*read_binary) (ThriftProtocol *protocol, gpointer *buf, guint32 *len, GError **error); + gint (*get_min_serialized_size) (ThriftProtocol *protocol, ThriftType type, GError **error); }; /* used by THRIFT_TYPE_PROTOCOL */ @@ -317,6 +319,9 @@ gint32 thrift_protocol_read_binary (ThriftProtocol *protocol, gpointer *buf, guint32 *len, GError **error); +gint thrift_protocol_get_min_serialized_size (ThriftProtocol *protocol, + ThriftType type, GError **error); + gint32 thrift_protocol_skip (ThriftProtocol *protocol, ThriftType type, GError **error); diff --git a/lib/c_glib/src/thrift/c_glib/protocol/thrift_protocol_decorator.h b/lib/c_glib/src/thrift/c_glib/protocol/thrift_protocol_decorator.h index 13b6af23854..cf53de6e449 100644 --- a/lib/c_glib/src/thrift/c_glib/protocol/thrift_protocol_decorator.h +++ b/lib/c_glib/src/thrift/c_glib/protocol/thrift_protocol_decorator.h @@ -67,6 +67,11 @@ struct _ThriftProtocolDecoratorClass /* used by THRIFT_TYPE_PROTOCOL_DECORATOR */ GType thrift_protocol_decorator_get_type (void); +gint32 thrift_protocol_decorator_write_message_begin (ThriftProtocol *protocol, + const gchar *name, + const ThriftMessageType message_type, + const gint32 seqid, GError **error); + G_END_DECLS #endif /* _THRIFT_PROTOCOL_DECORATOR_H */ diff --git a/lib/c_glib/src/thrift/c_glib/protocol/thrift_stored_message_protocol.c b/lib/c_glib/src/thrift/c_glib/protocol/thrift_stored_message_protocol.c index a0d560bfcfd..6f1586f2390 100644 --- a/lib/c_glib/src/thrift/c_glib/protocol/thrift_stored_message_protocol.c +++ b/lib/c_glib/src/thrift/c_glib/protocol/thrift_stored_message_protocol.c @@ -55,7 +55,7 @@ thrift_stored_message_protocol_read_message_begin (ThriftProtocol *protocol, ThriftStoredMessageProtocol *self = THRIFT_STORED_MESSAGE_PROTOCOL (protocol); /* We return the stored values on construction */ - *name = self->name; + *name = g_strdup (self->name); *message_type = self->mtype; *seqid = self->seqid; @@ -172,7 +172,7 @@ thrift_stored_message_protocol_class_init (ThriftStoredMessageProtocolClass *kla g_param_spec_int ("seqid", "Sequence id type in the wire", "Set the Sequence id in the wire", - 0, G_MAXINT, + G_MININT, G_MAXINT, 0, (G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE)); diff --git a/lib/c_glib/src/thrift/c_glib/server/thrift_server.c b/lib/c_glib/src/thrift/c_glib/server/thrift_server.c index ccf9153db68..2109addf75b 100644 --- a/lib/c_glib/src/thrift/c_glib/server/thrift_server.c +++ b/lib/c_glib/src/thrift/c_glib/server/thrift_server.c @@ -76,21 +76,27 @@ thrift_server_set_property (GObject *object, guint property_id, switch (property_id) { case PROP_THRIFT_SERVER_PROCESSOR: + g_clear_object (&server->processor); server->processor = g_value_dup_object (value); break; case PROP_THRIFT_SERVER_SERVER_TRANSPORT: + g_clear_object (&server->server_transport); server->server_transport = g_value_dup_object (value); break; case PROP_THRIFT_SERVER_INPUT_TRANSPORT_FACTORY: + g_clear_object (&server->input_transport_factory); server->input_transport_factory = g_value_dup_object (value); break; case PROP_THRIFT_SERVER_OUTPUT_TRANSPORT_FACTORY: + g_clear_object (&server->output_transport_factory); server->output_transport_factory = g_value_dup_object (value); break; case PROP_THRIFT_SERVER_INPUT_PROTOCOL_FACTORY: + g_clear_object (&server->input_protocol_factory); server->input_protocol_factory = g_value_dup_object (value); break; case PROP_THRIFT_SERVER_OUTPUT_PROTOCOL_FACTORY: + g_clear_object (&server->output_protocol_factory); server->output_protocol_factory = g_value_dup_object (value); break; } diff --git a/lib/c_glib/src/thrift/c_glib/server/thrift_simple_server.c b/lib/c_glib/src/thrift/c_glib/server/thrift_simple_server.c index 22a96c7a481..0c894590386 100644 --- a/lib/c_glib/src/thrift/c_glib/server/thrift_simple_server.c +++ b/lib/c_glib/src/thrift/c_glib/server/thrift_simple_server.c @@ -78,6 +78,18 @@ thrift_simple_server_serve (ThriftServer *server, GError **error) NULL); THRIFT_TRANSPORT_GET_CLASS (output_transport)->close (output_transport, NULL); + g_object_unref (input_transport); + g_object_unref (output_transport); + g_object_unref (input_protocol); + g_object_unref (output_protocol); + } + if ((*error) != NULL) { + g_message ("thrift_simple_server_serve : %s", (*error)->message); + g_clear_error (error); + } + if (t != NULL) + { + g_object_unref (t); } } diff --git a/lib/c_glib/src/thrift/c_glib/thrift_configuration.c b/lib/c_glib/src/thrift/c_glib/thrift_configuration.c new file mode 100644 index 00000000000..5b9ed1493bc --- /dev/null +++ b/lib/c_glib/src/thrift/c_glib/thrift_configuration.c @@ -0,0 +1,132 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0(the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distuributed on an + * "AS IS" BASIS, WITHOUT 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 +#include "thrift_configuration.h" + +/* object properties */ +enum _ThriftConfigurationProperties +{ + PROP_0, + PROP_THRIFT_CONFIGURATION_MAX_MESSAGE_SIZE, + PROP_THRIFT_CONFIGURATION_MAX_FRAME_SIZE, + PROP_THRIFT_CONFIGURATION_RECURSION_LIMIT +}; + +G_DEFINE_TYPE(ThriftConfiguration, thrift_configuration, G_TYPE_OBJECT) + +/* property accessor */ +void +thrift_configuration_get_property(GObject *object, guint property_id, + GValue *value, GParamSpec *pspec) +{ + ThriftConfiguration *configuration = THRIFT_CONFIGURATION(object); + + THRIFT_UNUSED_VAR (pspec); + + switch (property_id) + { + case PROP_THRIFT_CONFIGURATION_MAX_MESSAGE_SIZE: + g_value_set_int(value, configuration->maxMessageSize_); + break; + case PROP_THRIFT_CONFIGURATION_MAX_FRAME_SIZE: + g_value_set_int(value, configuration->maxFrameSize_); + break; + case PROP_THRIFT_CONFIGURATION_RECURSION_LIMIT: + g_value_set_int(value, configuration->recursionLimit_); + break; + } +} + +/* property mutator */ +void +thrift_configuration_set_property(GObject *object, guint property_id, + const GValue *value, GParamSpec *pspec) +{ + ThriftConfiguration *configuration = THRIFT_CONFIGURATION (object); + + THRIFT_UNUSED_VAR (pspec); + + switch (property_id) + { + case PROP_THRIFT_CONFIGURATION_MAX_MESSAGE_SIZE: + configuration->maxMessageSize_ = g_value_get_int (value); + break; + case PROP_THRIFT_CONFIGURATION_MAX_FRAME_SIZE: + configuration->maxFrameSize_ = g_value_get_int (value); + break; + case PROP_THRIFT_CONFIGURATION_RECURSION_LIMIT: + configuration->recursionLimit_ = g_value_get_int (value); + break; + } +} + +static void +thrift_configuration_class_init (ThriftConfigurationClass *cls) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS (cls); + GParamSpec *param_spec = NULL; + + /* setup accessors and mutators */ + gobject_class->get_property = thrift_configuration_get_property; + gobject_class->set_property = thrift_configuration_set_property; + + param_spec = g_param_spec_int ("max_message_size", + "max_message_size (construct)", + "Set the max size of the message", + 0, /* min */ + G_MAXINT32, /* max */ + DEFAULT_MAX_MESSAGE_SIZE, /* default by convention */ + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_READWRITE); + + g_object_class_install_property (gobject_class, PROP_THRIFT_CONFIGURATION_MAX_MESSAGE_SIZE, + param_spec); + + param_spec = g_param_spec_int ("max_frame_size", + "max_frame_size (construct)", + "Set the max size of the frame", + 0, /* min */ + G_MAXINT32, /* max */ + DEFAULT_MAX_FRAME_SIZE, /* default by convention */ + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_READWRITE); + + g_object_class_install_property (gobject_class, PROP_THRIFT_CONFIGURATION_MAX_FRAME_SIZE, + param_spec); + + param_spec = g_param_spec_int ("recursion_limit", + "recursion_limit (construct)", + "Set the limit of the resursion", + 0, /* min */ + G_MAXINT32, /* max */ + DEFAULT_RECURSION_DEPTH, /* default by convention */ + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_READWRITE); + + g_object_class_install_property (gobject_class, PROP_THRIFT_CONFIGURATION_RECURSION_LIMIT, + param_spec); + +} + +static void +thrift_configuration_init (ThriftConfiguration *configuration) +{ + THRIFT_UNUSED_VAR (configuration); +} diff --git a/lib/c_glib/src/thrift/c_glib/thrift_configuration.h b/lib/c_glib/src/thrift/c_glib/thrift_configuration.h new file mode 100644 index 00000000000..87f2e3ed6f1 --- /dev/null +++ b/lib/c_glib/src/thrift/c_glib/thrift_configuration.h @@ -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. + */ + +#ifndef _THRIFT_CONFIGURATION_H +#define _THRIFT_CONFIGURATION_H + +#include + +G_BEGIN_DECLS + +/* type macros */ +#define THRIFT_TYPE_CONFIGURATION (thrift_configuration_get_type ()) +#define THRIFT_CONFIGURATION(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), THRIFT_TYPE_CONFIGURATION, ThriftConfiguration)) +#define THRIFT_IS_CONFIGURATION(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), THRIFT_TYPE_CONFIGURATION)) +#define THRIFT_CONFIGURATTION_CLASS(c) (G_TYPE_CHECK_CLASS_CAST ((c), THRIFT_TYPE_CONFIGURATION, ThriftConfigurationClass)) +#define THRIFT_IS_CONFIGURATION_CLASS(c) (G_TYPE_CHECK_CLASS_TYPE ((c), THRIFT_TYPE_CONFIGURATION)) +#define THRIFT_CONFIGURATION_GET_CLASS(obj) (G_TYPE_INSTAANCE_GET_CLASS ((obj), THRIFT_TYPE_CONFIGURATION, ThriftconfigurationClass)) + +typedef struct _ThriftConfiguration ThriftConfiguration; + +/*! + * Thrift Configuration object + */ +struct _ThriftConfiguration +{ + GObject parent; + + /* private */ + int maxMessageSize_; + int maxFrameSize_; + int recursionLimit_; +}; + +typedef struct _ThriftConfigurationClass ThriftConfigurationClass; + +/*! + * Thrift Configuration class + */ +struct _ThriftConfigurationClass +{ + GObjectClass parent; +}; + +/* used by THRIFT_TYPE_CONFIGURATION */ +GType thrift_configuration_get_type(void); + +#define DEFAULT_MAX_MESSAGE_SIZE (100 * 1024 * 1024) +#define DEFAULT_MAX_FRAME_SIZE (16384000) +#define DEFAULT_RECURSION_DEPTH (64) + +G_END_DECLS + +#endif /* #ifndef _THRIFT_CONFIGURATION_H */ diff --git a/lib/c_glib/src/thrift/c_glib/transport/thrift_buffered_transport.c b/lib/c_glib/src/thrift/c_glib/transport/thrift_buffered_transport.c index 0ab3e9329f6..30aa95cafa2 100644 --- a/lib/c_glib/src/thrift/c_glib/transport/thrift_buffered_transport.c +++ b/lib/c_glib/src/thrift/c_glib/transport/thrift_buffered_transport.c @@ -17,6 +17,7 @@ * under the License. */ +#include #include #include #include @@ -33,7 +34,10 @@ enum _ThriftBufferedTransportProperties PROP_0, PROP_THRIFT_BUFFERED_TRANSPORT_TRANSPORT, PROP_THRIFT_BUFFERED_TRANSPORT_READ_BUFFER_SIZE, - PROP_THRIFT_BUFFERED_TRANSPORT_WRITE_BUFFER_SIZE + PROP_THRIFT_BUFFERED_TRANSPORT_WRITE_BUFFER_SIZE, + PROP_THRIFT_BUFFERED_TRANSPORT_CONFIGURATION, + PROP_THRIFT_BUFFERED_TRANSPORT_REMAINING_MESSAGE_SIZE, + PROP_THRIFT_BUFFERED_TRANSPORT_KNOW_MESSAGE_SIZE }; G_DEFINE_TYPE(ThriftBufferedTransport, thrift_buffered_transport, THRIFT_TYPE_TRANSPORT) @@ -79,9 +83,10 @@ thrift_buffered_transport_read_slow (ThriftTransport *transport, gpointer buf, gint ret = 0; guint32 want = len; guint32 got = 0; - guchar *tmpdata = g_alloca (len); + guchar *tmpdata = g_new0 (guchar, len); guint32 have = t->r_buf->len; + /* we shouldn't hit this unless the buffer doesn't have enough to read */ g_assert (t->r_buf->len < want); @@ -102,12 +107,14 @@ thrift_buffered_transport_read_slow (ThriftTransport *transport, gpointer buf, tmpdata, want, error)) < 0) { + g_free (tmpdata); return ret; } got += ret; /* copy the data starting from where we left off */ memcpy ((guint8 *)buf + have, tmpdata, got); + g_free (tmpdata); return got + have; } else { guint32 give; @@ -116,11 +123,12 @@ thrift_buffered_transport_read_slow (ThriftTransport *transport, gpointer buf, tmpdata, want, error)) < 0) { + g_free (tmpdata); return ret; } got += ret; t->r_buf = g_byte_array_append (t->r_buf, tmpdata, got); - + g_free (tmpdata); /* hand over what we have up to what the caller wants */ give = want < t->r_buf->len ? want : t->r_buf->len; @@ -139,6 +147,11 @@ thrift_buffered_transport_read (ThriftTransport *transport, gpointer buf, guint32 len, GError **error) { ThriftBufferedTransport *t = THRIFT_BUFFERED_TRANSPORT (transport); + ThriftTransportClass *ttc = THRIFT_TRANSPORT_GET_CLASS (transport); + if(!ttc->checkReadBytesAvailable (transport, len, error)) + { + return -1; + } /* if we have enough buffer data to fulfill the read, just use * a memcpy */ @@ -240,6 +253,12 @@ gboolean thrift_buffered_transport_flush (ThriftTransport *transport, GError **error) { ThriftBufferedTransport *t = THRIFT_BUFFERED_TRANSPORT (transport); + ThriftTransportClass *ttc = THRIFT_TRANSPORT_GET_CLASS (transport); + + if(!ttc->resetConsumedMessageSize (transport, -1, error)) + { + return FALSE; + } if (t->w_buf != NULL && t->w_buf->len > 0) { @@ -293,6 +312,8 @@ thrift_buffered_transport_get_property (GObject *object, guint property_id, { ThriftBufferedTransport *transport = THRIFT_BUFFERED_TRANSPORT (object); + ThriftTransport *tt = THRIFT_TRANSPORT (object); + THRIFT_UNUSED_VAR (pspec); switch (property_id) @@ -306,6 +327,15 @@ thrift_buffered_transport_get_property (GObject *object, guint property_id, case PROP_THRIFT_BUFFERED_TRANSPORT_WRITE_BUFFER_SIZE: g_value_set_uint (value, transport->w_buf_size); break; + case PROP_THRIFT_BUFFERED_TRANSPORT_CONFIGURATION: + g_value_set_object (value, tt->configuration); + break; + case PROP_THRIFT_BUFFERED_TRANSPORT_REMAINING_MESSAGE_SIZE: + g_value_set_long (value, tt->remainingMessageSize_); + break; + case PROP_THRIFT_BUFFERED_TRANSPORT_KNOW_MESSAGE_SIZE: + g_value_set_long (value, tt->knowMessageSize_); + break; } } @@ -316,6 +346,8 @@ thrift_buffered_transport_set_property (GObject *object, guint property_id, { ThriftBufferedTransport *transport = THRIFT_BUFFERED_TRANSPORT (object); + ThriftTransport *tt = THRIFT_TRANSPORT (object); + THRIFT_UNUSED_VAR (pspec); switch (property_id) @@ -329,6 +361,15 @@ thrift_buffered_transport_set_property (GObject *object, guint property_id, case PROP_THRIFT_BUFFERED_TRANSPORT_WRITE_BUFFER_SIZE: transport->w_buf_size = g_value_get_uint (value); break; + case PROP_THRIFT_BUFFERED_TRANSPORT_CONFIGURATION: + tt->configuration = g_value_dup_object (value); + break; + case PROP_THRIFT_BUFFERED_TRANSPORT_REMAINING_MESSAGE_SIZE: + tt->remainingMessageSize_ = g_value_get_long (value); + break; + case PROP_THRIFT_BUFFERED_TRANSPORT_KNOW_MESSAGE_SIZE: + tt->knowMessageSize_ = g_value_get_long (value); + break; } } @@ -376,6 +417,36 @@ thrift_buffered_transport_class_init (ThriftBufferedTransportClass *cls) PROP_THRIFT_BUFFERED_TRANSPORT_WRITE_BUFFER_SIZE, param_spec); + param_spec = g_param_spec_object ("configuration", + "configuration (construct)", + "Thrift Configuration", + THRIFT_TYPE_CONFIGURATION, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY); + g_object_class_install_property (gobject_class, + PROP_THRIFT_BUFFERED_TRANSPORT_CONFIGURATION, + param_spec); + + param_spec = g_param_spec_long ("remainingmessagesize", + "remainingmessagesize (construct)", + "Set the remaining message size", + 0, /* min */ + G_MAXINT32, /* max */ + DEFAULT_MAX_MESSAGE_SIZE, /* default by construct */ + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY); + g_object_class_install_property (gobject_class, + PROP_THRIFT_BUFFERED_TRANSPORT_REMAINING_MESSAGE_SIZE, + param_spec); + + param_spec = g_param_spec_long ("knowmessagesize", + "knowmessagesize (construct)", + "Set the known size of the message", + 0, /* min */ + G_MAXINT32, /* max */ + DEFAULT_MAX_MESSAGE_SIZE, /* default by construct */ + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY); + g_object_class_install_property (gobject_class, + PROP_THRIFT_BUFFERED_TRANSPORT_KNOW_MESSAGE_SIZE, + param_spec); gobject_class->finalize = thrift_buffered_transport_finalize; ttc->is_open = thrift_buffered_transport_is_open; diff --git a/lib/c_glib/src/thrift/c_glib/transport/thrift_buffered_transport.h b/lib/c_glib/src/thrift/c_glib/transport/thrift_buffered_transport.h index 837f46770d8..afb9466ff32 100644 --- a/lib/c_glib/src/thrift/c_glib/transport/thrift_buffered_transport.h +++ b/lib/c_glib/src/thrift/c_glib/transport/thrift_buffered_transport.h @@ -37,7 +37,7 @@ G_BEGIN_DECLS #define THRIFT_BUFFERED_TRANSPORT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), THRIFT_TYPE_BUFFERED_TRANSPORT, ThriftBufferedTransport)) #define THRIFT_IS_BUFFERED_TRANSPORT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), THRIFT_TYPE_BUFFERED_TRANSPORT)) #define THRIFT_BUFFERED_TRANSPORT_CLASS(c) (G_TYPE_CHECK_CLASS_CAST ((c), THRIFT_TYPE_BUFFERED_TRANSPORT, ThriftBufferedTransportClass)) -#define THRIFT_IS_BUFFERED_TRANSPORT_CLASS(c) (G_TYPE_CHECK_CLASS_TYPE ((c), THRIFT_TYPE_BUFFERED_TRANSPORT) +#define THRIFT_IS_BUFFERED_TRANSPORT_CLASS(c) (G_TYPE_CHECK_CLASS_TYPE ((c), THRIFT_TYPE_BUFFERED_TRANSPORT)) #define THRIFT_BUFFERED_TRANSPORT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), THRIFT_TYPE_BUFFERED_TRANSPORT, ThriftBufferedTransportClass)) typedef struct _ThriftBufferedTransport ThriftBufferedTransport; diff --git a/lib/c_glib/src/thrift/c_glib/transport/thrift_fd_transport.c b/lib/c_glib/src/thrift/c_glib/transport/thrift_fd_transport.c index 14abff7a4b9..1e2712c3232 100644 --- a/lib/c_glib/src/thrift/c_glib/transport/thrift_fd_transport.c +++ b/lib/c_glib/src/thrift/c_glib/transport/thrift_fd_transport.c @@ -26,6 +26,7 @@ #include #include +#include #include #include @@ -33,7 +34,10 @@ enum _ThriftFDTransportProperties { PROP_0, - PROP_THRIFT_FD_TRANSPORT_FD + PROP_THRIFT_FD_TRANSPORT_FD, + PROP_THRIFT_FD_TRANSPORT_CONFIGURATION, + PROP_THRIFT_FD_TRANSPORT_REMAINING_MESSAGE_SIZE, + PROP_THRIFT_FD_TRANSPORT_KNOW_MESSAGE_SIZE, }; G_DEFINE_TYPE (ThriftFDTransport, thrift_fd_transport, THRIFT_TYPE_TRANSPORT) @@ -87,6 +91,12 @@ thrift_fd_transport_read (ThriftTransport *transport, gpointer buf, ssize_t n; t = THRIFT_FD_TRANSPORT (transport); + ThriftTransportClass *ttc = THRIFT_TRANSPORT_GET_CLASS (transport); + if(!ttc->checkReadBytesAvailable(transport, len, error)) + { + return -1; + } + n = read (t->fd, (guint8 *) buf, len); if (n == -1) { g_set_error (error, @@ -157,6 +167,12 @@ thrift_fd_transport_flush (ThriftTransport *transport, GError **error) { ThriftFDTransport *t; t = THRIFT_FD_TRANSPORT (transport); + ThriftTransportClass *ttc = THRIFT_TRANSPORT_GET_CLASS (transport); + if(!ttc->resetConsumedMessageSize (transport, -1, error)) + { + return FALSE; + } + if (fsync (t->fd) == -1) { g_set_error (error, THRIFT_TRANSPORT_ERROR, @@ -194,10 +210,21 @@ thrift_fd_transport_get_property (GObject *object, guint property_id, t = THRIFT_FD_TRANSPORT (object); + ThriftTransport *tt = THRIFT_TRANSPORT (object); + switch (property_id) { case PROP_THRIFT_FD_TRANSPORT_FD: g_value_set_int (value, t->fd); break; + case PROP_THRIFT_FD_TRANSPORT_CONFIGURATION: + g_value_set_object (value, tt->configuration); + break; + case PROP_THRIFT_FD_TRANSPORT_REMAINING_MESSAGE_SIZE: + g_value_set_long (value, tt->remainingMessageSize_); + break; + case PROP_THRIFT_FD_TRANSPORT_KNOW_MESSAGE_SIZE: + g_value_set_long (value, tt->knowMessageSize_); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; @@ -215,10 +242,21 @@ thrift_fd_transport_set_property (GObject *object, guint property_id, t = THRIFT_FD_TRANSPORT (object); + ThriftTransport *tt = THRIFT_TRANSPORT (object); + switch (property_id) { case PROP_THRIFT_FD_TRANSPORT_FD: t->fd = g_value_get_int (value); break; + case PROP_THRIFT_FD_TRANSPORT_CONFIGURATION: + tt->configuration = g_value_dup_object (value); + break; + case PROP_THRIFT_FD_TRANSPORT_REMAINING_MESSAGE_SIZE: + tt->remainingMessageSize_ = g_value_get_long (value); + break; + case PROP_THRIFT_FD_TRANSPORT_KNOW_MESSAGE_SIZE: + tt->knowMessageSize_ = g_value_get_long (value); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; @@ -253,6 +291,38 @@ thrift_fd_transport_class_init (ThriftFDTransportClass *cls) PROP_THRIFT_FD_TRANSPORT_FD, param_spec); + param_spec = g_param_spec_object ("configuration", + "configuration (construct)", + "Thrift Configuration", + THRIFT_TYPE_CONFIGURATION, + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_READWRITE); + g_object_class_install_property (gobject_class, + PROP_THRIFT_FD_TRANSPORT_CONFIGURATION, + param_spec); + + param_spec = g_param_spec_long ("remainingmessagesize", + "remainingmessagesize (construct)", + "Set the remaining message size", + 0, /* min */ + G_MAXINT32, /* max */ + DEFAULT_MAX_MESSAGE_SIZE, /* default by construct */ + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY); + g_object_class_install_property (gobject_class, + PROP_THRIFT_FD_TRANSPORT_REMAINING_MESSAGE_SIZE, + param_spec); + + param_spec = g_param_spec_long ("knowmessagesize", + "knowmessagesize (construct)", + "Set the known size of the message", + 0, /* min */ + G_MAXINT32, /* max */ + DEFAULT_MAX_MESSAGE_SIZE, /* default by construct */ + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY); + g_object_class_install_property (gobject_class, + PROP_THRIFT_FD_TRANSPORT_KNOW_MESSAGE_SIZE, + param_spec); + gobject_class->finalize = thrift_fd_transport_finalize; ttc->is_open = thrift_fd_transport_is_open; ttc->open = thrift_fd_transport_open; diff --git a/lib/c_glib/src/thrift/c_glib/transport/thrift_framed_transport.c b/lib/c_glib/src/thrift/c_glib/transport/thrift_framed_transport.c index c5482463532..3cbb245e07c 100644 --- a/lib/c_glib/src/thrift/c_glib/transport/thrift_framed_transport.c +++ b/lib/c_glib/src/thrift/c_glib/transport/thrift_framed_transport.c @@ -17,6 +17,7 @@ * under the License. */ +#include #include #include #include @@ -26,6 +27,7 @@ #include #include +#include #include #include @@ -35,7 +37,10 @@ enum _ThriftFramedTransportProperties PROP_0, PROP_THRIFT_FRAMED_TRANSPORT_TRANSPORT, PROP_THRIFT_FRAMED_TRANSPORT_READ_BUFFER_SIZE, - PROP_THRIFT_FRAMED_TRANSPORT_WRITE_BUFFER_SIZE + PROP_THRIFT_FRAMED_TRANSPORT_WRITE_BUFFER_SIZE, + PROP_THRIFT_FRAMED_TRANSPORT_REMAINING_MESSAGE_SIZE, + PROP_THRIFT_FRAMED_TRANSPORT_KNOW_MESSAGE_SIZE, + PROP_THRIFT_FRAMED_TRANSPORT_CONFIGURATION }; G_DEFINE_TYPE(ThriftFramedTransport, thrift_framed_transport, THRIFT_TYPE_TRANSPORT) @@ -91,9 +96,17 @@ thrift_framed_transport_read_frame (ThriftTransport *transport, guchar *tmpdata; sz = ntohl (sz); + if (sz > t->max_frame_size) + { + g_set_error (error, + THRIFT_TRANSPORT_ERROR, + THRIFT_TRANSPORT_ERROR_MAX_MESSAGE_SIZE_REACHED, + "Recived an oversized frame,"); + return result; + } /* create a buffer to hold the data and read that much data */ - tmpdata = g_alloca (sz); + tmpdata = g_new0 (guchar, sz); bytes = thrift_transport_read (t->transport, tmpdata, sz, error); if (bytes > 0 && (error == NULL || *error == NULL)) @@ -103,6 +116,7 @@ thrift_framed_transport_read_frame (ThriftTransport *transport, result = TRUE; } + g_free (tmpdata); } return result; @@ -152,6 +166,12 @@ thrift_framed_transport_read (ThriftTransport *transport, gpointer buf, guint32 len, GError **error) { ThriftFramedTransport *t = THRIFT_FRAMED_TRANSPORT (transport); + ThriftTransportClass *ttc = THRIFT_TRANSPORT_GET_CLASS (transport); + + if(!ttc->checkReadBytesAvailable (transport, len, error)) + { + return -1; + } /* if we have enough buffer data to fulfill the read, just use * a memcpy from the buffer */ @@ -224,15 +244,21 @@ gboolean thrift_framed_transport_flush (ThriftTransport *transport, GError **error) { ThriftFramedTransport *t = THRIFT_FRAMED_TRANSPORT (transport); + ThriftTransportClass *ttc = THRIFT_TRANSPORT_GET_CLASS (transport); gint32 sz_hbo, sz_nbo; guchar *tmpdata; + if(!ttc->resetConsumedMessageSize (transport, -1, error)) + { + return FALSE; + } + /* get the size of the frame in host and network byte order */ sz_hbo = t->w_buf->len + sizeof(sz_nbo); sz_nbo = (gint32) htonl ((guint32) t->w_buf->len); /* copy the size of the frame and then the frame itself */ - tmpdata = g_alloca (sz_hbo); + tmpdata = g_new0 (guchar, sz_hbo); memcpy (tmpdata, (guint8 *) &sz_nbo, sizeof (sz_nbo)); if (t->w_buf->len > 0) @@ -248,7 +274,7 @@ thrift_framed_transport_flush (ThriftTransport *transport, GError **error) THRIFT_TRANSPORT_GET_CLASS (t->transport)->flush (t->transport, error); - + g_free (tmpdata); return TRUE; } @@ -259,6 +285,7 @@ thrift_framed_transport_init (ThriftFramedTransport *transport) transport->transport = NULL; transport->r_buf = g_byte_array_new (); transport->w_buf = g_byte_array_new (); + transport->max_frame_size = DEFAULT_MAX_FRAME_SIZE; } /* destructor */ @@ -286,6 +313,7 @@ thrift_framed_transport_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { ThriftFramedTransport *transport = THRIFT_FRAMED_TRANSPORT (object); + ThriftTransport *tt = THRIFT_TRANSPORT (object); THRIFT_UNUSED_VAR (pspec); @@ -300,6 +328,15 @@ thrift_framed_transport_get_property (GObject *object, guint property_id, case PROP_THRIFT_FRAMED_TRANSPORT_WRITE_BUFFER_SIZE: g_value_set_uint (value, transport->w_buf_size); break; + case PROP_THRIFT_FRAMED_TRANSPORT_CONFIGURATION: + g_value_set_object (value, tt->configuration); + break; + case PROP_THRIFT_FRAMED_TRANSPORT_REMAINING_MESSAGE_SIZE: + g_value_set_long (value, tt->remainingMessageSize_); + break; + case PROP_THRIFT_FRAMED_TRANSPORT_KNOW_MESSAGE_SIZE: + g_value_set_long (value, tt->knowMessageSize_); + break; } } @@ -309,6 +346,7 @@ thrift_framed_transport_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) { ThriftFramedTransport *transport = THRIFT_FRAMED_TRANSPORT (object); + ThriftTransport *tt = THRIFT_TRANSPORT (object); THRIFT_UNUSED_VAR (pspec); @@ -323,6 +361,19 @@ thrift_framed_transport_set_property (GObject *object, guint property_id, case PROP_THRIFT_FRAMED_TRANSPORT_WRITE_BUFFER_SIZE: transport->w_buf_size = g_value_get_uint (value); break; + case PROP_THRIFT_FRAMED_TRANSPORT_CONFIGURATION: + tt->configuration = g_value_dup_object (value); + if (tt->configuration != NULL) + { + transport->max_frame_size = tt->configuration->maxFrameSize_; + } + break; + case PROP_THRIFT_FRAMED_TRANSPORT_REMAINING_MESSAGE_SIZE: + tt->remainingMessageSize_ = g_value_get_long (value); + break; + case PROP_THRIFT_FRAMED_TRANSPORT_KNOW_MESSAGE_SIZE: + tt->knowMessageSize_ = g_value_get_long (value); + break; } } @@ -370,6 +421,36 @@ thrift_framed_transport_class_init (ThriftFramedTransportClass *cls) PROP_THRIFT_FRAMED_TRANSPORT_WRITE_BUFFER_SIZE, param_spec); + param_spec = g_param_spec_object ("configuration", "configuration (construct)", + "Thrift Configuration", + THRIFT_TYPE_CONFIGURATION, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY); + g_object_class_install_property (gobject_class, + PROP_THRIFT_FRAMED_TRANSPORT_CONFIGURATION, + param_spec); + + param_spec = g_param_spec_long ("remainingmessagesize", + "remainingmessagesize (construct)", + "Set the remaining message size", + 0, /* min */ + G_MAXINT32, /* max */ + DEFAULT_MAX_MESSAGE_SIZE, /* default by construct */ + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY); + g_object_class_install_property (gobject_class, + PROP_THRIFT_FRAMED_TRANSPORT_REMAINING_MESSAGE_SIZE, + param_spec); + + param_spec = g_param_spec_long ("knowmessagesize", + "knowmessagesize (construct)", + "Set the known size of the message", + 0, /* min */ + G_MAXINT32, /* max */ + DEFAULT_MAX_MESSAGE_SIZE, /* default by construct */ + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY); + g_object_class_install_property (gobject_class, + PROP_THRIFT_FRAMED_TRANSPORT_KNOW_MESSAGE_SIZE, + param_spec); + gobject_class->finalize = thrift_framed_transport_finalize; ttc->is_open = thrift_framed_transport_is_open; ttc->peek = thrift_framed_transport_peek; diff --git a/lib/c_glib/src/thrift/c_glib/transport/thrift_framed_transport.h b/lib/c_glib/src/thrift/c_glib/transport/thrift_framed_transport.h index 95c01239381..245cd21c4fe 100644 --- a/lib/c_glib/src/thrift/c_glib/transport/thrift_framed_transport.h +++ b/lib/c_glib/src/thrift/c_glib/transport/thrift_framed_transport.h @@ -37,7 +37,7 @@ G_BEGIN_DECLS #define THRIFT_FRAMED_TRANSPORT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), THRIFT_TYPE_FRAMED_TRANSPORT, ThriftFramedTransport)) #define THRIFT_IS_FRAMED_TRANSPORT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), THRIFT_TYPE_FRAMED_TRANSPORT)) #define THRIFT_FRAMED_TRANSPORT_CLASS(c) (G_TYPE_CHECK_CLASS_CAST ((c), THRIFT_TYPE_FRAMED_TRANSPORT, ThriftFramedTransportClass)) -#define THRIFT_IS_FRAMED_TRANSPORT_CLASS(c) (G_TYPE_CHECK_CLASS_TYPE ((c), THRIFT_TYPE_FRAMED_TRANSPORT) +#define THRIFT_IS_FRAMED_TRANSPORT_CLASS(c) (G_TYPE_CHECK_CLASS_TYPE ((c), THRIFT_TYPE_FRAMED_TRANSPORT)) #define THRIFT_FRAMED_TRANSPORT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), THRIFT_TYPE_FRAMED_TRANSPORT, ThriftFramedTransportClass)) typedef struct _ThriftFramedTransport ThriftFramedTransport; @@ -53,6 +53,7 @@ struct _ThriftFramedTransport ThriftTransport *transport; /* private */ + guint32 max_frame_size; GByteArray *r_buf; GByteArray *w_buf; guint32 r_buf_size; diff --git a/lib/c_glib/src/thrift/c_glib/transport/thrift_memory_buffer.c b/lib/c_glib/src/thrift/c_glib/transport/thrift_memory_buffer.c index 91818e91aa6..d38d494b6a2 100644 --- a/lib/c_glib/src/thrift/c_glib/transport/thrift_memory_buffer.c +++ b/lib/c_glib/src/thrift/c_glib/transport/thrift_memory_buffer.c @@ -17,6 +17,7 @@ * under the License. */ +#include #include #include #include @@ -24,6 +25,7 @@ #include #include +#include #include #include @@ -33,7 +35,10 @@ enum _ThriftMemoryBufferProperties PROP_0, PROP_THRIFT_MEMORY_BUFFER_BUFFER_SIZE, PROP_THRIFT_MEMORY_BUFFER_BUFFER, - PROP_THRIFT_MEMORY_BUFFER_OWNER + PROP_THRIFT_MEMORY_BUFFER_OWNER, + PROP_THRIFT_MEMORY_BUFFER_CONFIGURATION, + PROP_THRIFT_MEMORY_BUFFER_REMAINING_MESSAGE_SIZE, + PROP_THRIFT_MEMORY_BUFFER_KNOW_MESSAGE_SIZE }; G_DEFINE_TYPE(ThriftMemoryBuffer, thrift_memory_buffer, THRIFT_TYPE_TRANSPORT) @@ -70,9 +75,14 @@ thrift_memory_buffer_read (ThriftTransport *transport, gpointer buf, guint32 len, GError **error) { ThriftMemoryBuffer *t = THRIFT_MEMORY_BUFFER (transport); + ThriftTransportClass *ttc = THRIFT_TRANSPORT_GET_CLASS (transport); + guint32 give = len; - THRIFT_UNUSED_VAR (error); + if(!ttc->checkReadBytesAvailable (transport, len, error)) + { + return -1; + } /* if the requested bytes are more than what we have available, * just give all that we have the buffer */ @@ -168,6 +178,8 @@ thrift_memory_buffer_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { ThriftMemoryBuffer *t = THRIFT_MEMORY_BUFFER (object); + + ThriftTransport *tt = THRIFT_TRANSPORT (object); THRIFT_UNUSED_VAR (pspec); @@ -182,6 +194,15 @@ thrift_memory_buffer_get_property (GObject *object, guint property_id, case PROP_THRIFT_MEMORY_BUFFER_OWNER: g_value_set_boolean (value, t->owner); break; + case PROP_THRIFT_MEMORY_BUFFER_CONFIGURATION: + g_value_set_object (value, tt->configuration); + break; + case PROP_THRIFT_MEMORY_BUFFER_REMAINING_MESSAGE_SIZE: + g_value_set_long (value, tt->remainingMessageSize_); + break; + case PROP_THRIFT_MEMORY_BUFFER_KNOW_MESSAGE_SIZE: + g_value_set_long (value, tt->knowMessageSize_); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; @@ -195,6 +216,8 @@ thrift_memory_buffer_set_property (GObject *object, guint property_id, { ThriftMemoryBuffer *t = THRIFT_MEMORY_BUFFER (object); + ThriftTransport *tt = THRIFT_TRANSPORT (object); + THRIFT_UNUSED_VAR (pspec); switch (property_id) @@ -208,6 +231,15 @@ thrift_memory_buffer_set_property (GObject *object, guint property_id, case PROP_THRIFT_MEMORY_BUFFER_OWNER: t->owner = g_value_get_boolean (value); break; + case PROP_THRIFT_MEMORY_BUFFER_CONFIGURATION: + tt->configuration = g_value_dup_object (value); + break; + case PROP_THRIFT_MEMORY_BUFFER_REMAINING_MESSAGE_SIZE: + tt->remainingMessageSize_ = g_value_get_long (value); + break; + case PROP_THRIFT_MEMORY_BUFFER_KNOW_MESSAGE_SIZE: + tt->knowMessageSize_ = g_value_get_long (value); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; @@ -272,6 +304,38 @@ thrift_memory_buffer_class_init (ThriftMemoryBufferClass *cls) PROP_THRIFT_MEMORY_BUFFER_OWNER, param_spec); + param_spec = g_param_spec_object ("configuration", + "configuration (construct)", + "Thrift Configuration", + THRIFT_TYPE_CONFIGURATION, + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_READWRITE); + g_object_class_install_property (gobject_class, + PROP_THRIFT_MEMORY_BUFFER_CONFIGURATION, + param_spec); + + param_spec = g_param_spec_long ("remainingmessagesize", + "remainingmessagesize (construct)", + "Set the remaining message size", + 0, /* min */ + G_MAXINT32, /* max */ + DEFAULT_MAX_MESSAGE_SIZE, /* default by construct */ + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY); + g_object_class_install_property (gobject_class, + PROP_THRIFT_MEMORY_BUFFER_REMAINING_MESSAGE_SIZE, + param_spec); + + param_spec = g_param_spec_long ("knowmessagesize", + "knowmessagesize (construct)", + "Set the known size of the message", + 0, /* min */ + G_MAXINT32, /* max */ + DEFAULT_MAX_MESSAGE_SIZE, /* default by construct */ + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY); + g_object_class_install_property (gobject_class, + PROP_THRIFT_MEMORY_BUFFER_KNOW_MESSAGE_SIZE, + param_spec); + gobject_class->constructed = thrift_memory_buffer_constructed; gobject_class->finalize = thrift_memory_buffer_finalize; ttc->is_open = thrift_memory_buffer_is_open; diff --git a/lib/c_glib/src/thrift/c_glib/transport/thrift_memory_buffer.h b/lib/c_glib/src/thrift/c_glib/transport/thrift_memory_buffer.h index d5d47b39000..9ddc16c0e8d 100644 --- a/lib/c_glib/src/thrift/c_glib/transport/thrift_memory_buffer.h +++ b/lib/c_glib/src/thrift/c_glib/transport/thrift_memory_buffer.h @@ -36,7 +36,7 @@ G_BEGIN_DECLS #define THRIFT_MEMORY_BUFFER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), THRIFT_TYPE_MEMORY_BUFFER, ThriftMemoryBuffer)) #define THRIFT_IS_MEMORY_BUFFER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), THRIFT_TYPE_MEMORY_BUFFER)) #define THRIFT_MEMORY_BUFFER_CLASS(c) (G_TYPE_CHECK_CLASS_CAST ((c), THRIFT_TYPE_MEMORY_BUFFER, ThriftMemoryBufferClass)) -#define THRIFT_IS_MEMORY_BUFFER_CLASS(c) (G_TYPE_CHECK_CLASS_TYPE ((c), THRIFT_TYPE_MEMORY_BUFFER) +#define THRIFT_IS_MEMORY_BUFFER_CLASS(c) (G_TYPE_CHECK_CLASS_TYPE ((c), THRIFT_TYPE_MEMORY_BUFFER)) #define THRIFT_MEMORY_BUFFER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), THRIFT_TYPE_MEMORY_BUFFER, ThriftMemoryBufferClass)) typedef struct _ThriftMemoryBuffer ThriftMemoryBuffer; diff --git a/lib/c_glib/src/thrift/c_glib/transport/thrift_server_socket.c b/lib/c_glib/src/thrift/c_glib/transport/thrift_server_socket.c index 6ea897c9110..1abd615f078 100644 --- a/lib/c_glib/src/thrift/c_glib/transport/thrift_server_socket.c +++ b/lib/c_glib/src/thrift/c_glib/transport/thrift_server_socket.c @@ -23,6 +23,7 @@ #include #include #include +#include #include #include @@ -36,7 +37,11 @@ enum _ThriftServerSocketProperties { PROP_0, PROP_THRIFT_SERVER_SOCKET_PORT, - PROP_THRIFT_SERVER_SOCKET_BACKLOG + PROP_THRIFT_SERVER_SOCKET_PATH, + PROP_THRIFT_SERVER_SOCKET_BACKLOG, + PROP_THRIFT_SERVER_SOCKET_CONFIGURATION, + PROP_THRIFT_SERVER_SOCKET_REMAINING_MESSAGE_SIZE, + PROP_THRIFT_SERVER_SOCKET_KNOW_MESSAGE_SIZE }; /* define the GError domain string */ @@ -48,17 +53,12 @@ gboolean thrift_server_socket_listen (ThriftServerTransport *transport, GError **error) { int enabled = 1; /* for setsockopt() */ - struct sockaddr_in pin; ThriftServerSocket *tsocket = THRIFT_SERVER_SOCKET (transport); - /* create a address structure */ - memset (&pin, 0, sizeof(pin)); - pin.sin_family = AF_INET; - pin.sin_addr.s_addr = INADDR_ANY; - pin.sin_port = htons(tsocket->port); + const int socket_domain = tsocket->path ? PF_UNIX : AF_INET; /* create a socket */ - if ((tsocket->sd = socket (AF_INET, SOCK_STREAM, 0)) == -1) + if ((tsocket->sd = socket (socket_domain, SOCK_STREAM, 0)) == -1) { g_set_error (error, THRIFT_SERVER_SOCKET_ERROR, THRIFT_SERVER_SOCKET_ERROR_SOCKET, @@ -76,22 +76,60 @@ thrift_server_socket_listen (ThriftServerTransport *transport, GError **error) } /* bind to the socket */ - if (bind(tsocket->sd, (struct sockaddr *) &pin, sizeof(pin)) == -1) + if (tsocket->path) { - g_set_error (error, THRIFT_SERVER_SOCKET_ERROR, - THRIFT_SERVER_SOCKET_ERROR_BIND, - "failed to bind to port %d - %s", - tsocket->port, strerror(errno)); - return FALSE; + /* create a socket structure */ + struct sockaddr_un pin; + memset (&pin, 0, sizeof(pin)); + pin.sun_family = AF_UNIX; + memcpy(pin.sun_path, tsocket->path, strlen(tsocket->path) + 1); + + if (bind(tsocket->sd, (struct sockaddr *) &pin, sizeof(pin)) == -1) + { + g_set_error (error, THRIFT_SERVER_SOCKET_ERROR, + THRIFT_SERVER_SOCKET_ERROR_BIND, + "failed to bind to path %s: - %s", + tsocket->path, strerror(errno)); + return FALSE; + } + } + else + { + /* create a address structure */ + struct sockaddr_in pin; + memset (&pin, 0, sizeof(pin)); + pin.sin_family = AF_INET; + pin.sin_addr.s_addr = INADDR_ANY; + pin.sin_port = htons(tsocket->port); + + if (bind(tsocket->sd, (struct sockaddr *) &pin, sizeof(pin)) == -1) + { + g_set_error (error, THRIFT_SERVER_SOCKET_ERROR, + THRIFT_SERVER_SOCKET_ERROR_BIND, + "failed to bind to port %d - %s", + tsocket->port, strerror(errno)); + return FALSE; + } } if (listen(tsocket->sd, tsocket->backlog) == -1) { - g_set_error (error, THRIFT_SERVER_SOCKET_ERROR, - THRIFT_SERVER_SOCKET_ERROR_LISTEN, - "failed to listen to port %d - %s", - tsocket->port, strerror(errno)); - return FALSE; + if (tsocket->path) + { + g_set_error (error, THRIFT_SERVER_SOCKET_ERROR, + THRIFT_SERVER_SOCKET_ERROR_BIND, + "failed to listen to path %s: - %s", + tsocket->path, strerror(errno)); + return FALSE; + } + else + { + g_set_error (error, THRIFT_SERVER_SOCKET_ERROR, + THRIFT_SERVER_SOCKET_ERROR_LISTEN, + "failed to listen to port %d - %s", + tsocket->port, strerror(errno)); + return FALSE; + } } return TRUE; @@ -106,6 +144,7 @@ thrift_server_socket_accept (ThriftServerTransport *transport, GError **error) ThriftSocket *socket = NULL; ThriftServerSocket *tsocket = THRIFT_SERVER_SOCKET (transport); + ThriftServerTransport *tst = THRIFT_SERVER_TRANSPORT (transport); if ((sd = accept(tsocket->sd, (struct sockaddr *) &address, &addrlen)) == -1) { @@ -113,10 +152,19 @@ thrift_server_socket_accept (ThriftServerTransport *transport, GError **error) THRIFT_SERVER_SOCKET_ERROR_ACCEPT, "failed to accept connection - %s", strerror(errno)); - return FALSE; + return NULL; } - socket = g_object_new (THRIFT_TYPE_SOCKET, NULL); + if(tst->configuration != NULL) + { + socket = g_object_new (THRIFT_TYPE_SOCKET, "configuration", tst->configuration, + "remainingmessagesize", tst->configuration->maxMessageSize_, + "knowmessagesize", tst->configuration->maxMessageSize_, NULL); + } + else + { + socket = g_object_new (THRIFT_TYPE_SOCKET, NULL); + } socket->sd = sd; return THRIFT_TRANSPORT(socket); @@ -172,15 +220,28 @@ thrift_server_socket_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { ThriftServerSocket *socket = THRIFT_SERVER_SOCKET (object); + ThriftServerTransport *transport = THRIFT_SERVER_TRANSPORT (object); switch (property_id) { case PROP_THRIFT_SERVER_SOCKET_PORT: g_value_set_uint (value, socket->port); break; + case PROP_THRIFT_SERVER_SOCKET_PATH: + g_value_set_string (value, socket->path); + break; case PROP_THRIFT_SERVER_SOCKET_BACKLOG: g_value_set_uint (value, socket->backlog); break; + case PROP_THRIFT_SERVER_SOCKET_CONFIGURATION: + g_value_set_object (value, transport->configuration); + break; + case PROP_THRIFT_SERVER_SOCKET_REMAINING_MESSAGE_SIZE: + g_value_set_long (value, transport->remainingMessageSize_); + break; + case PROP_THRIFT_SERVER_SOCKET_KNOW_MESSAGE_SIZE: + g_value_set_long (value, transport->knowMessageSize_); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; @@ -193,15 +254,31 @@ thrift_server_socket_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) { ThriftServerSocket *socket = THRIFT_SERVER_SOCKET (object); + ThriftServerTransport *transport = THRIFT_SERVER_TRANSPORT (object); switch (property_id) { case PROP_THRIFT_SERVER_SOCKET_PORT: socket->port = g_value_get_uint (value); break; + case PROP_THRIFT_SERVER_SOCKET_PATH: + if (socket->path) { + g_free(socket->path); + } + socket->path = g_strdup (g_value_get_string (value)); + break; case PROP_THRIFT_SERVER_SOCKET_BACKLOG: socket->backlog = g_value_get_uint (value); break; + case PROP_THRIFT_SERVER_SOCKET_CONFIGURATION: + transport->configuration = g_value_dup_object (value); + break; + case PROP_THRIFT_SERVER_SOCKET_REMAINING_MESSAGE_SIZE: + transport->remainingMessageSize_ = g_value_get_long (value); + break; + case PROP_THRIFT_SERVER_SOCKET_KNOW_MESSAGE_SIZE: + transport->knowMessageSize_ = g_value_get_long (value); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; @@ -232,6 +309,16 @@ thrift_server_socket_class_init (ThriftServerSocketClass *cls) PROP_THRIFT_SERVER_SOCKET_PORT, param_spec); + param_spec = g_param_spec_string ("path", + "path (construct)", + "Set the path to listen to", + NULL, /* default value */ + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_READWRITE); + g_object_class_install_property (gobject_class, + PROP_THRIFT_SERVER_SOCKET_PATH, + param_spec); + param_spec = g_param_spec_uint ("backlog", "backlog (construct)", "Set the accept backlog", @@ -244,6 +331,36 @@ thrift_server_socket_class_init (ThriftServerSocketClass *cls) PROP_THRIFT_SERVER_SOCKET_BACKLOG, param_spec); + param_spec = g_param_spec_object ("configuration", + "configuration (construct)", + "Thtift Configuration", + THRIFT_TYPE_CONFIGURATION, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY); + g_object_class_install_property (gobject_class, + PROP_THRIFT_SERVER_SOCKET_CONFIGURATION, + param_spec); + + param_spec = g_param_spec_long ("remainingmessagesize", + "remainingmessagesize (construct)", + "Set the remaining message size", + 0, /* min */ + G_MAXINT32, /* max */ + DEFAULT_MAX_MESSAGE_SIZE, /* default by construct */ + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY); + g_object_class_install_property (gobject_class, + PROP_THRIFT_SERVER_SOCKET_REMAINING_MESSAGE_SIZE, + param_spec); + + param_spec = g_param_spec_long ("knowmessagesize", + "knowmessagesize (construct)", + "Set the known size of the message", + 0, /* min */ + G_MAXINT32, /* max */ + DEFAULT_MAX_MESSAGE_SIZE, /* default by construct */ + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY); + g_object_class_install_property (gobject_class, + PROP_THRIFT_SERVER_SOCKET_KNOW_MESSAGE_SIZE, + param_spec); gobject_class->finalize = thrift_server_socket_finalize; tstc->listen = thrift_server_socket_listen; diff --git a/lib/c_glib/src/thrift/c_glib/transport/thrift_server_socket.h b/lib/c_glib/src/thrift/c_glib/transport/thrift_server_socket.h index fd04954015c..7710d5161f9 100644 --- a/lib/c_glib/src/thrift/c_glib/transport/thrift_server_socket.h +++ b/lib/c_glib/src/thrift/c_glib/transport/thrift_server_socket.h @@ -50,6 +50,7 @@ struct _ThriftServerSocket /* private */ guint port; + gchar *path; gshort backlog; int sd; guint8 *buf; diff --git a/lib/c_glib/src/thrift/c_glib/transport/thrift_server_transport.c b/lib/c_glib/src/thrift/c_glib/transport/thrift_server_transport.c index c25d1384f11..8354f80ac95 100644 --- a/lib/c_glib/src/thrift/c_glib/transport/thrift_server_transport.c +++ b/lib/c_glib/src/thrift/c_glib/transport/thrift_server_transport.c @@ -21,15 +21,206 @@ #include #include +/* object properties */ +enum _ThriftServerTransportProperties +{ + PROP_0, + PROP_THRIFT_SERVER_TRANSPORT_CONFIGURATION, + PROP_THRIFT_SERVER_TRANSPORT_REMAINING_MESSAGE_SIZE, + PROP_THRIFT_SERVER_TRANSPORT_KNOW_MESSAGE_SIZE +}; + G_DEFINE_ABSTRACT_TYPE(ThriftServerTransport, thrift_server_transport, G_TYPE_OBJECT) +gboolean +thrift_server_transport_updateKnownMessageSize(ThriftServerTransport *transport, glong size, GError **error) +{ + gboolean boolean = TRUE; + ThriftServerTransport *tst = THRIFT_SERVER_TRANSPORT (transport); + ThriftServerTransportClass *tstc = THRIFT_SERVER_TRANSPORT_GET_CLASS (transport); + glong consumed = tst->knowMessageSize_ - tst->remainingMessageSize_; + if(!tstc->resetConsumedMessageSize (transport, size, error)) + { + boolean = FALSE; + } + if(!tstc->countConsumedMessageBytes (transport, consumed, error)) + { + boolean = FALSE; + } + return boolean; +} + +gboolean +thrift_server_transport_checkReadBytesAvailable(ThriftServerTransport *transport, glong numBytes, GError **error) +{ + gboolean boolean = TRUE; + ThriftServerTransport *tst = THRIFT_SERVER_TRANSPORT (transport); + if(tst->remainingMessageSize_ < numBytes) + { + g_set_error(error, + THRIFT_TRANSPORT_ERROR, + THRIFT_TRANSPORT_ERROR_MAX_MESSAGE_SIZE_REACHED, + "MaxMessageSize reached"); + boolean = FALSE; + } + + return boolean; +} + +gboolean +thrift_server_transport_resetConsumedMessageSize(ThriftServerTransport *transport, glong newSize, GError **error) +{ + ThriftServerTransport *tst = THRIFT_SERVER_TRANSPORT (transport); + if(newSize < 0) + { + if(tst->configuration != NULL) + { + tst->knowMessageSize_ = tst->configuration->maxMessageSize_; + tst->remainingMessageSize_ = tst->configuration->maxMessageSize_; + } + else + { + tst->knowMessageSize_ = DEFAULT_MAX_MESSAGE_SIZE; + tst->remainingMessageSize_ = DEFAULT_MAX_MESSAGE_SIZE; + } + return TRUE; + } + /* update only: message size can shrink, but not grow */ + if(newSize > tst->knowMessageSize_) + { + g_set_error(error, + THRIFT_TRANSPORT_ERROR, + THRIFT_TRANSPORT_ERROR_MAX_MESSAGE_SIZE_REACHED, + "MaxMessageSize reached"); + return FALSE; + } + + tst->knowMessageSize_ = newSize; + tst->remainingMessageSize_ = newSize; + + return TRUE; +} + +gboolean +thrift_server_transport_countConsumedMessageBytes(ThriftServerTransport *transport, glong numBytes, GError **error) +{ + ThriftServerTransport *tst = THRIFT_SERVER_TRANSPORT (transport); + if(tst->remainingMessageSize_ > numBytes) + { + tst->remainingMessageSize_ -= numBytes; + } + else + { + tst->remainingMessageSize_ = 0; + if(*error == NULL) + { + g_set_error(error, + THRIFT_TRANSPORT_ERROR, + THRIFT_TRANSPORT_ERROR_MAX_MESSAGE_SIZE_REACHED, + "MaxMessageSize reached"); + } + return FALSE; + } + + return TRUE; +} + +/* property accesor */ +void +thrift_server_transport_get_property(GObject *object, guint property_id, + GValue *value, GParamSpec *pspec) +{ + ThriftServerTransport *transport = THRIFT_SERVER_TRANSPORT (object); + + THRIFT_UNUSED_VAR (pspec); + + switch (property_id) + { + case PROP_THRIFT_SERVER_TRANSPORT_CONFIGURATION: + g_value_set_object (value, transport->configuration); + break; + case PROP_THRIFT_SERVER_TRANSPORT_REMAINING_MESSAGE_SIZE: + g_value_set_long (value, transport->remainingMessageSize_); + break; + case PROP_THRIFT_SERVER_TRANSPORT_KNOW_MESSAGE_SIZE: + g_value_set_long (value, transport->knowMessageSize_); + break; + } +} + +/* property mutator */ +void +thrift_server_transport_set_property (GObject *object, guint property_id, + const GValue *value, GParamSpec *pspec) +{ + ThriftServerTransport *transport = THRIFT_SERVER_TRANSPORT (object); + + THRIFT_UNUSED_VAR (pspec); + + switch (property_id) + { + case PROP_THRIFT_SERVER_TRANSPORT_CONFIGURATION: + transport->configuration = g_value_dup_object (value); + break; + case PROP_THRIFT_SERVER_TRANSPORT_REMAINING_MESSAGE_SIZE: + transport->remainingMessageSize_ = g_value_get_long (value); + break; + case PROP_THRIFT_SERVER_TRANSPORT_KNOW_MESSAGE_SIZE: + transport->knowMessageSize_ = g_value_get_long (value); + break; + } +} + /* base initializer for the server transport interface */ static void thrift_server_transport_class_init (ThriftServerTransportClass *c) { + GObjectClass *gobject_class = G_OBJECT_CLASS (c); + ThriftServerTransportClass *tstc = THRIFT_SERVER_TRANSPORT_CLASS (c); + GParamSpec *param_spec = NULL; + + /* setup accessors and mutators */ + gobject_class->get_property = thrift_server_transport_get_property; + gobject_class->set_property = thrift_server_transport_set_property; + + param_spec = g_param_spec_object ("configuration", + "configuration (construct)", + "Thrift Configuration", + THRIFT_TYPE_CONFIGURATION, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY); + g_object_class_install_property (gobject_class, + PROP_THRIFT_SERVER_TRANSPORT_CONFIGURATION, + param_spec); + + param_spec = g_param_spec_long ("remainingmessagesize", + "remainingmessagesize (construct)", + "Set the remaining message size", + 0, /* min */ + G_MAXINT32, /* max */ + DEFAULT_MAX_MESSAGE_SIZE, /* default by construct */ + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY); + g_object_class_install_property (gobject_class, + PROP_THRIFT_SERVER_TRANSPORT_REMAINING_MESSAGE_SIZE, + param_spec); + + param_spec = g_param_spec_long ("knowmessagesize", + "knowmessagesize (construct)", + "Set the known size of the message", + 0, /* min */ + G_MAXINT32, /* max */ + DEFAULT_MAX_MESSAGE_SIZE, /* default by construct */ + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY); + g_object_class_install_property (gobject_class, + PROP_THRIFT_SERVER_TRANSPORT_KNOW_MESSAGE_SIZE, + param_spec); + c->listen = thrift_server_transport_listen; c->accept = thrift_server_transport_accept; c->close = thrift_server_transport_close; + tstc->updateKnownMessageSize = thrift_server_transport_updateKnownMessageSize; + tstc->checkReadBytesAvailable = thrift_server_transport_checkReadBytesAvailable; + tstc->resetConsumedMessageSize = thrift_server_transport_resetConsumedMessageSize; + tstc->countConsumedMessageBytes = thrift_server_transport_countConsumedMessageBytes; } static void diff --git a/lib/c_glib/src/thrift/c_glib/transport/thrift_server_transport.h b/lib/c_glib/src/thrift/c_glib/transport/thrift_server_transport.h index 98a9191bdac..9bf79012573 100644 --- a/lib/c_glib/src/thrift/c_glib/transport/thrift_server_transport.h +++ b/lib/c_glib/src/thrift/c_glib/transport/thrift_server_transport.h @@ -43,6 +43,11 @@ typedef struct _ThriftServerTransport ThriftServerTransport; struct _ThriftServerTransport { GObject parent; + + /* protected */ + ThriftConfiguration *configuration; + glong remainingMessageSize_; + glong knowMessageSize_; }; typedef struct _ThriftServerTransportClass ThriftServerTransportClass; @@ -58,6 +63,10 @@ struct _ThriftServerTransportClass gboolean (*listen) (ThriftServerTransport *transport, GError **error); ThriftTransport *(*accept) (ThriftServerTransport *transport, GError **error); gboolean (*close) (ThriftServerTransport *transport, GError **error); + gboolean (*updateKnownMessageSize) (ThriftServerTransport *transport, glong size, GError **error); + gboolean (*checkReadBytesAvailable) (ThriftServerTransport *transport, glong numBytes, GError **error); + gboolean (*resetConsumedMessageSize) (ThriftServerTransport *transport, glong newSize, GError **error); + gboolean (*countConsumedMessageBytes) (ThriftServerTransport *transport, glong numBytes, GError **error); }; /* used by THRIFT_TYPE_SERVER_TRANSPORT */ diff --git a/lib/c_glib/src/thrift/c_glib/transport/thrift_socket.c b/lib/c_glib/src/thrift/c_glib/transport/thrift_socket.c index 6dd0f0d3a11..cc746aacdfc 100644 --- a/lib/c_glib/src/thrift/c_glib/transport/thrift_socket.c +++ b/lib/c_glib/src/thrift/c_glib/transport/thrift_socket.c @@ -23,9 +23,11 @@ #include #include #include +#include #include #include +#include #include #include @@ -34,7 +36,11 @@ enum _ThriftSocketProperties { PROP_0, PROP_THRIFT_SOCKET_HOSTNAME, - PROP_THRIFT_SOCKET_PORT + PROP_THRIFT_SOCKET_PORT, + PROP_THRIFT_SOCKET_PATH, + PROP_THRIFT_SOCKET_CONFIGURATION, + PROP_THRIFT_SOCKET_REMAINING_MESSAGE_SIZE, + PROP_THRIFT_SOCKET_KNOW_MESSAGE_SIZE }; G_DEFINE_TYPE(ThriftSocket, thrift_socket, THRIFT_TYPE_TRANSPORT) @@ -128,6 +134,35 @@ thrift_socket_open (ThriftTransport *transport, GError **error) ThriftSocket *tsocket = THRIFT_SOCKET (transport); g_return_val_if_fail (tsocket->sd == THRIFT_INVALID_SOCKET, FALSE); + + if (tsocket->path) { + /* create a socket structure */ + struct sockaddr_un pin; + memset (&pin, 0, sizeof(pin)); + pin.sun_family = AF_UNIX; + memcpy(pin.sun_path, tsocket->path, strlen(tsocket->path) + 1); + + /* create the socket */ + if ((tsocket->sd = socket (PF_UNIX, SOCK_STREAM, 0)) == -1) + { + g_set_error (error, THRIFT_TRANSPORT_ERROR, THRIFT_TRANSPORT_ERROR_SOCKET, + "failed to create socket for path %s: - %s", + tsocket->path, + strerror(errno)); + return FALSE; + } + + /* open a connection */ + if (connect (tsocket->sd, (struct sockaddr *) &pin, sizeof(pin)) == -1) + { + thrift_socket_close(transport, NULL); + g_set_error (error, THRIFT_TRANSPORT_ERROR, THRIFT_TRANSPORT_ERROR_CONNECT, + "failed to connect to path %s: - %s", + tsocket->path, strerror(errno)); + return FALSE; + } + return TRUE; + } /* lookup the destination host */ #if defined(HAVE_GETHOSTBYNAME_R) @@ -163,7 +198,7 @@ thrift_socket_open (ThriftTransport *transport, GError **error) /* open a connection */ if (connect (tsocket->sd, (struct sockaddr *) &pin, sizeof(pin)) == -1) { - thrift_socket_close(tsocket, NULL); + thrift_socket_close(transport, NULL); g_set_error (error, THRIFT_TRANSPORT_ERROR, THRIFT_TRANSPORT_ERROR_CONNECT, "failed to connect to host %s:%d - %s", tsocket->hostname, tsocket->port, strerror(errno)); @@ -183,6 +218,11 @@ thrift_socket_read (ThriftTransport *transport, gpointer buf, guint got = 0; ThriftSocket *socket = THRIFT_SOCKET (transport); + ThriftTransportClass *ttc = THRIFT_TRANSPORT_GET_CLASS (transport); + if(!ttc->checkReadBytesAvailable (transport, len, error)) + { + return -1; + } while (got < len) { @@ -278,6 +318,10 @@ thrift_socket_finalize (GObject *object) g_free (socket->hostname); } socket->hostname = NULL; + if (socket->path != NULL) + { + g_free (socket->path); + } if (socket->sd != THRIFT_INVALID_SOCKET) { @@ -292,6 +336,7 @@ thrift_socket_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { ThriftSocket *socket = THRIFT_SOCKET (object); + ThriftTransport *tt = THRIFT_TRANSPORT (object); THRIFT_UNUSED_VAR (pspec); @@ -303,6 +348,18 @@ thrift_socket_get_property (GObject *object, guint property_id, case PROP_THRIFT_SOCKET_PORT: g_value_set_uint (value, socket->port); break; + case PROP_THRIFT_SOCKET_PATH: + g_value_set_string (value, socket->path); + break; + case PROP_THRIFT_SOCKET_CONFIGURATION: + g_value_set_object (value, tt->configuration); + break; + case PROP_THRIFT_SOCKET_REMAINING_MESSAGE_SIZE: + g_value_set_long (value, tt->remainingMessageSize_); + break; + case PROP_THRIFT_SOCKET_KNOW_MESSAGE_SIZE: + g_value_set_long (value, tt->knowMessageSize_); + break; } } @@ -312,6 +369,7 @@ thrift_socket_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) { ThriftSocket *socket = THRIFT_SOCKET (object); + ThriftTransport *tt = THRIFT_TRANSPORT (object); THRIFT_UNUSED_VAR (pspec); @@ -326,6 +384,20 @@ thrift_socket_set_property (GObject *object, guint property_id, case PROP_THRIFT_SOCKET_PORT: socket->port = g_value_get_uint (value); break; + case PROP_THRIFT_SOCKET_PATH: + if (socket->path) { + g_free(socket->path); + } + socket->path = g_strdup (g_value_get_string (value)); + break; + case PROP_THRIFT_SOCKET_CONFIGURATION: + tt->configuration = g_value_dup_object (value); + break; + case PROP_THRIFT_SOCKET_REMAINING_MESSAGE_SIZE: + tt->remainingMessageSize_ = g_value_get_long (value); + break; + case PROP_THRIFT_SOCKET_KNOW_MESSAGE_SIZE: + tt->knowMessageSize_ = g_value_get_long (value); } } @@ -361,6 +433,46 @@ thrift_socket_class_init (ThriftSocketClass *cls) g_object_class_install_property (gobject_class, PROP_THRIFT_SOCKET_PORT, param_spec); + param_spec = g_param_spec_string ("path", + "path (construct)", + "Set the path of the remote host", + NULL, /* default value */ + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_READWRITE); + g_object_class_install_property (gobject_class, PROP_THRIFT_SOCKET_PATH, + param_spec); + + param_spec = g_param_spec_object ("configuration", + "configuration", + "Thrift Configuration", + THRIFT_TYPE_CONFIGURATION, + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_READWRITE); + g_object_class_install_property (gobject_class, PROP_THRIFT_SOCKET_CONFIGURATION, + param_spec); + + param_spec = g_param_spec_long ("remainingmessagesize", + "remainingmessagesize (construct)", + "Set the remaining message size", + 0, /* min */ + G_MAXINT32, /* max */ + DEFAULT_MAX_MESSAGE_SIZE, /* default by construct */ + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY); + g_object_class_install_property (gobject_class, + PROP_THRIFT_SOCKET_REMAINING_MESSAGE_SIZE, + param_spec); + + param_spec = g_param_spec_long ("knowmessagesize", + "knowmessagesize (construct)", + "Set the known size of the message", + 0, /* min */ + G_MAXINT32, /* max */ + DEFAULT_MAX_MESSAGE_SIZE, /* default by construct */ + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY); + g_object_class_install_property (gobject_class, + PROP_THRIFT_SOCKET_KNOW_MESSAGE_SIZE, + param_spec); + gobject_class->finalize = thrift_socket_finalize; ttc->is_open = thrift_socket_is_open; ttc->peek = thrift_socket_peek; diff --git a/lib/c_glib/src/thrift/c_glib/transport/thrift_socket.h b/lib/c_glib/src/thrift/c_glib/transport/thrift_socket.h index 2f6f67db2f6..912929e81f3 100644 --- a/lib/c_glib/src/thrift/c_glib/transport/thrift_socket.h +++ b/lib/c_glib/src/thrift/c_glib/transport/thrift_socket.h @@ -51,6 +51,7 @@ struct _ThriftSocket /* private */ gchar *hostname; guint port; + gchar *path; int sd; }; @@ -67,6 +68,32 @@ struct _ThriftSocketClass /* used by THRIFT_TYPE_SOCKET */ GType thrift_socket_get_type (void); +/** + * Check if the socket is open and ready to send and receive + * @param transport + * @return true if open + */ +gboolean +thrift_socket_is_open (ThriftTransport *transport); + +/** + * Open connection if required and set the socket to be ready to send and receive + * @param transport + * @param error + * @return true if operation was correct + */ +gboolean +thrift_socket_open (ThriftTransport *transport, GError **error); + +/** + * Close connection if required + * @param transport + * @param error + * @return true if operation was correct + */ +gboolean +thrift_socket_close (ThriftTransport *transport, GError **error); + G_END_DECLS #endif diff --git a/lib/c_glib/src/thrift/c_glib/transport/thrift_ssl_socket.c b/lib/c_glib/src/thrift/c_glib/transport/thrift_ssl_socket.c index ee55406857e..0afcb1b8fb4 100644 --- a/lib/c_glib/src/thrift/c_glib/transport/thrift_ssl_socket.c +++ b/lib/c_glib/src/thrift/c_glib/transport/thrift_ssl_socket.c @@ -31,6 +31,7 @@ #include #include +#include #include #include #include @@ -57,7 +58,10 @@ enum _ThriftSSLSocketProperties { PROP_THRIFT_SSL_SOCKET_CONTEXT = 3, - PROP_THRIFT_SSL_SELF_SIGNED + PROP_THRIFT_SSL_SELF_SIGNED, + PROP_THRIFT_SSL_SOCKET_CONFIGURATION, + PROP_THRIFT_SSL_SOCKET_REMAINING_MESSAGE_SIZE, + PROP_THRIFT_SSL_SOCKET_KNOW_MESSAGE_SIZE, }; /* To hold a global state management of openssl for all instances */ @@ -65,6 +69,8 @@ static gboolean thrift_ssl_socket_openssl_initialized=FALSE; /* This array will store all of the mutexes available to OpenSSL. */ static MUTEX_TYPE *thrift_ssl_socket_global_mutex_buf=NULL; +gboolean +thrift_ssl_socket_authorize(ThriftTransport * transport, GError **error); /** * OpenSSL uniq id function. @@ -80,7 +86,11 @@ static unsigned long thrift_ssl_socket_static_id_function(void) #endif } -static void thrift_ssl_socket_static_locking_callback(int mode, int n, const char* unk, int id) { +static void thrift_ssl_socket_static_locking_callback(int mode, int n, const char* unk, int id) +{ + THRIFT_UNUSED_VAR (unk); + THRIFT_UNUSED_VAR (id); + if (mode & CRYPTO_LOCK) MUTEX_LOCK(thrift_ssl_socket_global_mutex_buf[n]); else @@ -151,7 +161,7 @@ G_DEFINE_TYPE(ThriftSSLSocket, thrift_ssl_socket, THRIFT_TYPE_SOCKET) * @param error */ static -void thrift_ssl_socket_get_ssl_error(ThriftSSLSocket *socket, const guchar *error_msg, guint thrift_error_no, int ssl_error, GError **error) +void thrift_ssl_socket_get_ssl_error(ThriftSSLSocket *socket, const gchar *error_msg, guint thrift_error_no, int ssl_error, GError **error) { unsigned long error_code; char buffer[1024]; @@ -175,15 +185,15 @@ void thrift_ssl_socket_get_ssl_error(ThriftSSLSocket *socket, const guchar *erro break; case SSL_ERROR_SYSCALL: buffer_size-=snprintf(buffer, buffer_size, "%s: ", error_msg); - buffer_size-=snprintf(buffer+(1024-buffer_size), buffer_size, "%lX -> %s", errno, strerror(errno)); + buffer_size-=snprintf(buffer+(1024-buffer_size), buffer_size, "%X -> %s", errno, strerror(errno)); break; case SSL_ERROR_WANT_READ: buffer_size-=snprintf(buffer, buffer_size, "%s: ", error_msg); - buffer_size-=snprintf(buffer+(1024-buffer_size), buffer_size, "%lX -> %s", ssl_error_type, "Error while reading from underlaying layer"); + buffer_size-=snprintf(buffer+(1024-buffer_size), buffer_size, "%X -> %s", ssl_error_type, "Error while reading from underlaying layer"); break; case SSL_ERROR_WANT_WRITE: buffer_size-=snprintf(buffer, buffer_size, "%s: ", error_msg); - buffer_size-=snprintf(buffer+(1024-buffer_size), buffer_size, "%lX -> %s", ssl_error_type, "Error while writting to underlaying layer"); + buffer_size-=snprintf(buffer+(1024-buffer_size), buffer_size, "%X -> %s", ssl_error_type, "Error while writting to underlaying layer"); break; } @@ -199,7 +209,7 @@ void thrift_ssl_socket_get_ssl_error(ThriftSSLSocket *socket, const guchar *erro * @param error */ static -void thrift_ssl_socket_get_error(const guchar *error_msg, guint thrift_error_no, GError **error) +void thrift_ssl_socket_get_error(const gchar *error_msg, guint thrift_error_no, GError **error) { unsigned long error_code; while ((error_code = ERR_get_error()) != 0) { @@ -237,7 +247,7 @@ thrift_ssl_socket_peek (ThriftTransport *transport, GError **error) gchar byte; rc = SSL_peek(ssl_socket->ssl, &byte, 1); if (rc < 0) { - thrift_ssl_socket_get_ssl_error(ssl_socket, "Check socket data", + thrift_ssl_socket_get_ssl_error(ssl_socket, (const guchar*)"Check socket data", THRIFT_SSL_SOCKET_ERROR_SSL, rc, error); } if (rc == 0) { @@ -270,13 +280,9 @@ thrift_ssl_socket_open (ThriftTransport *transport, GError **error) gboolean thrift_ssl_socket_close (ThriftTransport *transport, GError **error) { - gboolean retval = FALSE; ThriftSSLSocket *ssl_socket = THRIFT_SSL_SOCKET(transport); if(ssl_socket!=NULL && ssl_socket->ssl) { - int rc = SSL_shutdown(ssl_socket->ssl); -/* if (rc < 0) { - int errno_copy = THRIFT_SSL_SOCKET_ERROR_SSL; - }*/ + SSL_shutdown(ssl_socket->ssl); SSL_free(ssl_socket->ssl); ssl_socket->ssl = NULL; ERR_remove_state(0); @@ -291,9 +297,14 @@ thrift_ssl_socket_read (ThriftTransport *transport, gpointer buf, { guint maxRecvRetries_ = 10; ThriftSSLSocket *ssl_socket = THRIFT_SSL_SOCKET (transport); - guint bytes = 0; + gint32 bytes = 0; guint retries = 0; ThriftSocket *socket = THRIFT_SOCKET (transport); + ThriftTransportClass *ttc = THRIFT_TRANSPORT_GET_CLASS (transport); + if(!ttc->checkReadBytesAvailable (transport, len, error)) + { + return -1; + } g_return_val_if_fail (socket->sd != THRIFT_INVALID_SOCKET && ssl_socket->ssl!=NULL, FALSE); for (retries=0; retries < maxRecvRetries_; retries++) { @@ -306,7 +317,7 @@ thrift_ssl_socket_read (ThriftTransport *transport, gpointer buf, continue; } }else{ - thrift_ssl_socket_get_ssl_error(ssl_socket, "Receive error", + thrift_ssl_socket_get_ssl_error(ssl_socket, (const guchar*)"Receive error", THRIFT_SSL_SOCKET_ERROR_SSL, bytes, error); } @@ -342,7 +353,7 @@ thrift_ssl_socket_write (ThriftTransport *transport, const gpointer buf, ret = SSL_write (ssl_socket->ssl, (guint8 *)buf + sent, len - sent); if (ret < 0) { - thrift_ssl_socket_get_ssl_error(ssl_socket, "Send error", + thrift_ssl_socket_get_ssl_error(ssl_socket, (const guchar*)"Send error", THRIFT_SSL_SOCKET_ERROR_SSL, ret, error); return FALSE; } @@ -369,10 +380,15 @@ gboolean thrift_ssl_socket_flush (ThriftTransport *transport, GError **error) { ThriftSSLSocket *ssl_socket = THRIFT_SSL_SOCKET (transport); - gint ret = 0; - guint sent = 0; - + ThriftSocket *socket = THRIFT_SOCKET (transport); + + ThriftTransportClass *ttc = THRIFT_TRANSPORT_GET_CLASS (transport); + if(!ttc->resetConsumedMessageSize(transport, -1, error)) + { + return FALSE; + } + g_return_val_if_fail (socket->sd != THRIFT_INVALID_SOCKET && ssl_socket->ssl!=NULL, FALSE); BIO* bio = SSL_get_wbio(ssl_socket->ssl); @@ -409,7 +425,7 @@ thrift_ssl_socket_handle_handshake(ThriftTransport * transport, GError **error) rc = SSL_connect(ssl_socket->ssl); } if (rc <= 0) { - thrift_ssl_socket_get_ssl_error(ssl_socket, "Error while connect/bind", THRIFT_SSL_SOCKET_ERROR_CONNECT_BIND, rc, error); + thrift_ssl_socket_get_ssl_error(ssl_socket, (const guchar*)"Error while connect/bind", THRIFT_SSL_SOCKET_ERROR_CONNECT_BIND, rc, error); return FALSE; } }else @@ -590,6 +606,8 @@ thrift_ssl_socket_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { ThriftSSLSocket *socket = THRIFT_SSL_SOCKET (object); + ThriftTransport *tt = THRIFT_TRANSPORT (object); + THRIFT_UNUSED_VAR (pspec); switch (property_id) @@ -597,6 +615,15 @@ thrift_ssl_socket_get_property (GObject *object, guint property_id, case PROP_THRIFT_SSL_SOCKET_CONTEXT: g_value_set_pointer (value, socket->ctx); break; + case PROP_THRIFT_SSL_SOCKET_CONFIGURATION: + g_value_set_object (value, tt->configuration); + break; + case PROP_THRIFT_SSL_SOCKET_REMAINING_MESSAGE_SIZE: + g_value_set_long (value, tt->remainingMessageSize_); + break; + case PROP_THRIFT_SSL_SOCKET_KNOW_MESSAGE_SIZE: + g_value_set_long (value, tt->knowMessageSize_); + break; } } @@ -606,6 +633,7 @@ thrift_ssl_socket_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) { ThriftSSLSocket *socket = THRIFT_SSL_SOCKET (object); + ThriftTransport *tt = THRIFT_TRANSPORT (object); THRIFT_UNUSED_VAR (pspec); switch (property_id) @@ -621,6 +649,15 @@ thrift_ssl_socket_set_property (GObject *object, guint property_id, case PROP_THRIFT_SSL_SELF_SIGNED: socket->allow_selfsigned = g_value_get_boolean(value); break; + case PROP_THRIFT_SSL_SOCKET_CONFIGURATION: + tt->configuration = g_value_dup_object (value); + break; + case PROP_THRIFT_SSL_SOCKET_REMAINING_MESSAGE_SIZE: + tt->remainingMessageSize_ = g_value_get_long (value); + break; + case PROP_THRIFT_SSL_SOCKET_KNOW_MESSAGE_SIZE: + tt->knowMessageSize_ = g_value_get_long (value); + break; default: g_warning("Trying to set property %i that doesn't exists!", property_id); /* thrift_socket_set_property(object, property_id, value, pspec); */ @@ -685,18 +722,45 @@ thrift_ssl_socket_class_init (ThriftSSLSocketClass *cls) gobject_class->get_property = thrift_ssl_socket_get_property; gobject_class->set_property = thrift_ssl_socket_set_property; param_spec = g_param_spec_pointer ("ssl_context", - "SSLContext", - "Set the SSL context for handshake with the remote host", - G_PARAM_READWRITE); + "SSLContext", + "Set the SSL context for handshake with the remote host", + G_PARAM_READWRITE); g_object_class_install_property (gobject_class, PROP_THRIFT_SSL_SOCKET_CONTEXT, - param_spec); + param_spec); param_spec = g_param_spec_boolean ("ssl_accept_selfsigned", - "Accept Self Signed", - "Whether or not accept self signed certificate", - FALSE, - G_PARAM_READWRITE); + "Accept Self Signed", + "Whether or not accept self signed certificate", + FALSE, + G_PARAM_READWRITE); g_object_class_install_property (gobject_class, PROP_THRIFT_SSL_SELF_SIGNED, - param_spec); + param_spec); + param_spec = g_param_spec_object ("configuration", + "configuration (construct)", + "Set the conguration of the transport", + THRIFT_TYPE_CONFIGURATION, /* default value */ + G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE); + g_object_class_install_property (gobject_class, PROP_THRIFT_SSL_SOCKET_CONFIGURATION, + param_spec); + param_spec = g_param_spec_long ("remainingmessagesize", + "remainingmessagesize (construct)", + "Set the remaining message size", + 0, /* min */ + G_MAXINT32, /* max */ + DEFAULT_MAX_MESSAGE_SIZE, /* default by construct */ + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY); + g_object_class_install_property (gobject_class, + PROP_THRIFT_SSL_SOCKET_REMAINING_MESSAGE_SIZE, + param_spec); + param_spec = g_param_spec_long ("knowmessagesize", + "knowmessagesize (construct)", + "Set the known size of the message", + 0, /* min */ + G_MAXINT32, /* max */ + DEFAULT_MAX_MESSAGE_SIZE, /* default by construct */ + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY); + g_object_class_install_property (gobject_class, + PROP_THRIFT_SSL_SOCKET_KNOW_MESSAGE_SIZE, + param_spec); /* Class methods */ cls->handle_handshake = thrift_ssl_socket_handle_handshake; cls->create_ssl_context = thrift_ssl_socket_create_ssl_context; @@ -789,7 +853,7 @@ thrift_ssl_socket_context_initialize(ThriftSSLSocketProtocol ssl_protocol, GErro } if (context == NULL) { - thrift_ssl_socket_get_error("No cipher overlay", THRIFT_SSL_SOCKET_ERROR_CIPHER_NOT_AVAILABLE, error); + thrift_ssl_socket_get_error((const guchar*)"No cipher overlay", THRIFT_SSL_SOCKET_ERROR_CIPHER_NOT_AVAILABLE, error); return NULL; } SSL_CTX_set_mode(context, SSL_MODE_AUTO_RETRY); diff --git a/lib/c_glib/src/thrift/c_glib/transport/thrift_ssl_socket.h b/lib/c_glib/src/thrift/c_glib/transport/thrift_ssl_socket.h index 0ca465a0f31..8bbb4a35f63 100644 --- a/lib/c_glib/src/thrift/c_glib/transport/thrift_ssl_socket.h +++ b/lib/c_glib/src/thrift/c_glib/transport/thrift_ssl_socket.h @@ -188,6 +188,15 @@ thrift_ssl_socket_is_open (ThriftTransport *transport); gboolean thrift_ssl_socket_open (ThriftTransport *transport, GError **error); +/** + * Close connection if required + * @param transport + * @param error + * @return true if operation was correct + */ +gboolean +thrift_ssl_socket_close (ThriftTransport *transport, GError **error); + /** * @brief Initialization function @@ -214,5 +223,8 @@ thrift_ssl_socket_initialize_openssl(void); void thrift_ssl_socket_finalize_openssl(void); +gboolean +thrift_ssl_socket_authorize(ThriftTransport * transport, GError **error); + G_END_DECLS #endif diff --git a/lib/c_glib/src/thrift/c_glib/transport/thrift_transport.c b/lib/c_glib/src/thrift/c_glib/transport/thrift_transport.c index 9dd267143ea..9d3f25e75e7 100644 --- a/lib/c_glib/src/thrift/c_glib/transport/thrift_transport.c +++ b/lib/c_glib/src/thrift/c_glib/transport/thrift_transport.c @@ -17,8 +17,20 @@ * under the License. */ +#include +#include #include #include +#include + +/* object properties */ +enum _ThriftTransportProperties +{ + PROP_0, + PROP_THRIFT_TRANSPORT_CONFIGURATION, + PROP_THRIFT_TRANSPORT_REMAINING_MESSAGE_SIZE, + PROP_THRIFT_TRANSPORT_KNOW_MESSAGE_SIZE +}; /* define the GError domain string */ #define THRIFT_TRANSPORT_ERROR_DOMAIN "thrift-transport-error-quark" @@ -129,6 +141,142 @@ thrift_transport_real_read_all (ThriftTransport *transport, gpointer buf, return have; } +gboolean +thrift_transport_updateKnownMessageSize(ThriftTransport *transport, glong size, GError **error) +{ + gboolean boolean = TRUE; + ThriftTransport *tt = THRIFT_TRANSPORT (transport); + ThriftTransportClass *ttc = THRIFT_TRANSPORT_GET_CLASS (transport); + glong consumed = tt->knowMessageSize_ - tt->remainingMessageSize_; + if(!ttc->resetConsumedMessageSize (transport, size, error)) + { + boolean = FALSE; + } + if(!ttc->countConsumedMessageBytes (transport, consumed, error)) + { + boolean = FALSE; + } + return boolean; +} + +gboolean +thrift_transport_checkReadBytesAvailable(ThriftTransport *transport, glong numBytes, GError **error) +{ + gboolean boolean = TRUE; + ThriftTransport *tt = THRIFT_TRANSPORT (transport); + if(tt->remainingMessageSize_ < numBytes) + { + g_set_error(error, + THRIFT_TRANSPORT_ERROR, + THRIFT_TRANSPORT_ERROR_MAX_MESSAGE_SIZE_REACHED, + "MaxMessageSize reached"); + boolean = FALSE; + } + + return boolean; +} + +gboolean +thrift_transport_resetConsumedMessageSize(ThriftTransport *transport, glong newSize, GError **error) +{ + ThriftTransport *tt = THRIFT_TRANSPORT (transport); + if(newSize < 0) + { + if(tt->configuration != NULL) + { + tt->knowMessageSize_ = tt->configuration->maxMessageSize_; + tt->remainingMessageSize_ = tt->configuration->maxMessageSize_; + } + else + { + tt->knowMessageSize_ = DEFAULT_MAX_MESSAGE_SIZE; + tt->remainingMessageSize_ = DEFAULT_MAX_MESSAGE_SIZE; + } + return TRUE; + } + /* update only: message size can shrink, but not grow */ + if(newSize > tt->knowMessageSize_) + { + g_set_error(error, + THRIFT_TRANSPORT_ERROR, + THRIFT_TRANSPORT_ERROR_MAX_MESSAGE_SIZE_REACHED, + "MaxMessageSize reached"); + return FALSE; + } + + tt->knowMessageSize_ = newSize; + tt->remainingMessageSize_ = newSize; + + return TRUE; +} + +gboolean +thrift_transport_countConsumedMessageBytes(ThriftTransport *transport, glong numBytes, GError **error) +{ + ThriftTransport *tt = THRIFT_TRANSPORT (transport); + if(tt->remainingMessageSize_ > numBytes) + { + tt->remainingMessageSize_ -= numBytes; + } + else + { + tt->remainingMessageSize_ = 0; + g_set_error(error, + THRIFT_TRANSPORT_ERROR, + THRIFT_TRANSPORT_ERROR_MAX_MESSAGE_SIZE_REACHED, + "MaxMessageSize reached"); + return FALSE; + } + + return TRUE; +} + +/* property accesor */ +void +thrift_transport_get_property(GObject *object, guint property_id, + GValue *value, GParamSpec *pspec) +{ + ThriftTransport *transport = THRIFT_TRANSPORT (object); + + THRIFT_UNUSED_VAR (pspec); + + switch (property_id) + { + case PROP_THRIFT_TRANSPORT_CONFIGURATION: + g_value_set_object (value, transport->configuration); + break; + case PROP_THRIFT_TRANSPORT_REMAINING_MESSAGE_SIZE: + g_value_set_long (value, transport->remainingMessageSize_); + break; + case PROP_THRIFT_TRANSPORT_KNOW_MESSAGE_SIZE: + g_value_set_long (value, transport->knowMessageSize_); + break; + } +} + +/* property mutator */ +void +thrift_transport_set_property (GObject *object, guint property_id, + const GValue *value, GParamSpec *pspec) +{ + ThriftTransport *transport = THRIFT_TRANSPORT (object); + + THRIFT_UNUSED_VAR (pspec); + + switch (property_id) + { + case PROP_THRIFT_TRANSPORT_CONFIGURATION: + transport->configuration = g_value_get_object (value); + break; + case PROP_THRIFT_TRANSPORT_REMAINING_MESSAGE_SIZE: + transport->remainingMessageSize_ = g_value_get_long (value); + break; + case PROP_THRIFT_TRANSPORT_KNOW_MESSAGE_SIZE: + transport->knowMessageSize_ = g_value_get_long (value); + break; + } +} + /* define the GError domain for Thrift transports */ GQuark thrift_transport_error_quark (void) @@ -136,10 +284,65 @@ thrift_transport_error_quark (void) return g_quark_from_static_string (THRIFT_TRANSPORT_ERROR_DOMAIN); } +static void +thrift_transport_dispose (GObject *gobject) +{ + ThriftTransport *self = THRIFT_TRANSPORT (gobject); + + if(self->configuration != NULL) + g_clear_object (&self->configuration); + + /* Always chain up to the parent class; there is no need to check if + * the parent class implements the dispose() virtual function: it is + * always guaranteed to do so + */ + G_OBJECT_CLASS (thrift_transport_parent_class)->dispose (gobject); +} + /* class initializer for ThriftTransport */ static void thrift_transport_class_init (ThriftTransportClass *cls) { + GObjectClass *gobject_class = G_OBJECT_CLASS (cls); + GParamSpec *param_spec = NULL; + + /* setup accessors and mutators */ + gobject_class->get_property = thrift_transport_get_property; + gobject_class->set_property = thrift_transport_set_property; + gobject_class->dispose = thrift_transport_dispose; + + param_spec = g_param_spec_object ("configuration", + "configuration (construct)", + "Thrift Configuration", + THRIFT_TYPE_CONFIGURATION, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY); + g_object_class_install_property (gobject_class, + PROP_THRIFT_TRANSPORT_CONFIGURATION, + param_spec); + + param_spec = g_param_spec_long ("remainingmessagesize", + "remainingmessagesize (construct)", + "Set the remaining message size", + 0, /* min */ + G_MAXINT32, /* max */ + DEFAULT_MAX_MESSAGE_SIZE, /* default by construct */ + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY); + g_object_class_install_property (gobject_class, + PROP_THRIFT_TRANSPORT_REMAINING_MESSAGE_SIZE, + param_spec); + + param_spec = g_param_spec_long ("knowmessagesize", + "knowmessagesize (construct)", + "Set the known size of the message", + 0, /* min */ + G_MAXINT32, /* max */ + DEFAULT_MAX_MESSAGE_SIZE, /* default by construct */ + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY); + g_object_class_install_property (gobject_class, + PROP_THRIFT_TRANSPORT_KNOW_MESSAGE_SIZE, + param_spec); + + /* set these as virtual methods to be implemented by a subclass */ cls->is_open = thrift_transport_is_open; cls->open = thrift_transport_open; @@ -153,6 +356,11 @@ thrift_transport_class_init (ThriftTransportClass *cls) /* provide a default implementation for the peek and read_all methods */ cls->peek = thrift_transport_real_peek; cls->read_all = thrift_transport_real_read_all; + + cls->updateKnownMessageSize = thrift_transport_updateKnownMessageSize; + cls->checkReadBytesAvailable = thrift_transport_checkReadBytesAvailable; + cls->resetConsumedMessageSize = thrift_transport_resetConsumedMessageSize; + cls->countConsumedMessageBytes = thrift_transport_countConsumedMessageBytes; } static void diff --git a/lib/c_glib/src/thrift/c_glib/transport/thrift_transport.h b/lib/c_glib/src/thrift/c_glib/transport/thrift_transport.h index 94bb6f50704..83fb5da19da 100644 --- a/lib/c_glib/src/thrift/c_glib/transport/thrift_transport.h +++ b/lib/c_glib/src/thrift/c_glib/transport/thrift_transport.h @@ -22,6 +22,8 @@ #include +#include + G_BEGIN_DECLS /*! \file thrift_transport.h @@ -50,6 +52,11 @@ typedef struct _ThriftTransport ThriftTransport; struct _ThriftTransport { GObject parent; + + /* protected */ + ThriftConfiguration *configuration; + glong remainingMessageSize_; + glong knowMessageSize_; }; typedef struct _ThriftTransportClass ThriftTransportClass; @@ -75,6 +82,10 @@ struct _ThriftTransportClass gboolean (*flush) (ThriftTransport *transport, GError **error); gint32 (*read_all) (ThriftTransport *transport, gpointer buf, guint32 len, GError **error); + gboolean (*updateKnownMessageSize) (ThriftTransport *transport, glong size, GError **error); + gboolean (*checkReadBytesAvailable) (ThriftTransport *transport, glong numBytes, GError **error); + gboolean (*resetConsumedMessageSize) (ThriftTransport *transport, glong newSize, GError **error); + gboolean (*countConsumedMessageBytes) (ThriftTransport *transport, glong numBytes, GError **error); }; /* used by THRIFT_TYPE_TRANSPORT */ @@ -161,7 +172,8 @@ typedef enum THRIFT_TRANSPORT_ERROR_CONNECT, THRIFT_TRANSPORT_ERROR_SEND, THRIFT_TRANSPORT_ERROR_RECEIVE, - THRIFT_TRANSPORT_ERROR_CLOSE + THRIFT_TRANSPORT_ERROR_CLOSE, + THRIFT_TRANSPORT_ERROR_MAX_MESSAGE_SIZE_REACHED } ThriftTransportError; /* define an error domain for GError to use */ diff --git a/lib/c_glib/test/CMakeLists.txt b/lib/c_glib/test/CMakeLists.txt index fb3e41cecf4..cae8e512e58 100644 --- a/lib/c_glib/test/CMakeLists.txt +++ b/lib/c_glib/test/CMakeLists.txt @@ -20,7 +20,7 @@ set(TEST_PREFIX "c_glib") -include_directories(SYSTEM ${Boost_INCLUDE_DIRS}) +# include_directories(SYSTEM ${Boost_INCLUDE_DIRS}) #Make sure gen-cpp and gen-c_glib files can be included include_directories("${CMAKE_CURRENT_BINARY_DIR}") @@ -106,6 +106,30 @@ add_executable(testoptionalrequired testoptionalrequired.c) target_link_libraries(testoptionalrequired testgenc) add_test(NAME testoptionalrequired COMMAND testoptionalrequired) +add_executable(testthriftbinaryreadcheck testthriftbinaryreadcheck.c) +target_link_libraries(testthriftbinaryreadcheck testgenc) +add_test(NAME testthriftbinaryreadcheck COMMAND testthriftbinaryreadcheck) + +add_executable(testthriftbufferedreadcheck testthriftbufferedreadcheck.c) +target_link_libraries(testthriftbufferedreadcheck testgenc) +add_test(NAME testthriftbufferedreadcheck COMMAND testthriftbufferedreadcheck) + +add_executable(testthriftcompactreadcheck testthriftcompactreadcheck.c) +target_link_libraries(testthriftcompactreadcheck testgenc) +add_test(NAME testthriftcompactreadcheck COMMAND testthriftcompactreadcheck) + +add_executable(testthriftframedreadcheck testthriftframedreadcheck.c) +target_link_libraries(testthriftframedreadcheck testgenc) +add_test(NAME testthriftframedreadcheck COMMAND testthriftframedreadcheck) + +add_executable(testthriftfdreadcheck testthriftfdreadcheck.c) +target_link_libraries(testthriftfdreadcheck testgenc) +add_test(NAME testthriftfdreadcheck COMMAND testthriftfdreadcheck) + +add_executable(testthriftmemorybufferreadcheck testthriftmemorybufferreadcheck.c) +target_link_libraries(testthriftmemorybufferreadcheck testgenc) +add_test(NAME testthriftmemorybufferreadcheck COMMAND testthriftmemorybufferreadcheck) + include_directories("${PROJECT_SOURCE_DIR}/test/c_glib/src" "${CMAKE_CURRENT_BINARY_DIR}/gen-c_glib") add_executable(testthrifttest testthrifttest.c diff --git a/lib/c_glib/test/Makefile.am b/lib/c_glib/test/Makefile.am index 5e9d2ea415f..3ff48a3162e 100755 --- a/lib/c_glib/test/Makefile.am +++ b/lib/c_glib/test/Makefile.am @@ -16,7 +16,7 @@ # specific language governing permissions and limitations # under the License. # -AUTOMAKE_OPTIONS = subdir-objects serial-tests +AUTOMAKE_OPTIONS = subdir-objects serial-tests nostdinc SUBDIRS = @@ -36,7 +36,7 @@ BUILT_SOURCES = \ gen-c_glib/t_test_thrift_test.h \ gen-c_glib/t_test_thrift_test_types.h -AM_CPPFLAGS = -I../src -I./gen-c_glib +AM_CPPFLAGS = -I../src -I./gen-c_glib -I$(top_builddir)/lib/c_glib/src/thrift AM_CFLAGS = -g -Wall -Wextra -pedantic $(GLIB_CFLAGS) $(GOBJECT_CFLAGS) $(OPENSSL_INCLUDES) \ @GCOV_CFLAGS@ AM_CXXFLAGS = $(AM_CFLAGS) @@ -58,7 +58,13 @@ check_PROGRAMS = \ testsimpleserver \ testdebugproto \ testoptionalrequired \ - testthrifttest + testthrifttest \ + testthriftbinaryreadcheck \ + testthriftcompactreadcheck \ + testthriftbufferedreadcheck \ + testthriftfdreadcheck \ + testthriftframedreadcheck \ + testthriftmemorybufferreadcheck if WITH_CPP BUILT_SOURCES += gen-cpp/ThriftTest_types.cpp @@ -69,6 +75,7 @@ testserialization_SOURCES = testserialization.c testserialization_LDADD = \ $(top_builddir)/lib/c_glib/src/thrift/c_glib/protocol/libthrift_c_glib_la-thrift_protocol.o \ $(top_builddir)/lib/c_glib/src/thrift/c_glib/transport/libthrift_c_glib_la-thrift_transport.o \ + $(top_builddir)/lib/c_glib/src/thrift/c_glib/libthrift_c_glib_la-thrift_configuration.o \ libtestgenc.la testapplicationexception_SOURCES = testapplicationexception.c @@ -76,7 +83,8 @@ testapplicationexception_LDADD = \ $(top_builddir)/lib/c_glib/src/thrift/c_glib/libthrift_c_glib_la-thrift_application_exception.o \ $(top_builddir)/lib/c_glib/src/thrift/c_glib/protocol/libthrift_c_glib_la-thrift_protocol.o \ $(top_builddir)/lib/c_glib/src/thrift/c_glib/libthrift_c_glib_la-thrift_struct.o \ - $(top_builddir)/lib/c_glib/src/thrift/c_glib/transport/libthrift_c_glib_la-thrift_transport.o + $(top_builddir)/lib/c_glib/src/thrift/c_glib/transport/libthrift_c_glib_la-thrift_transport.o \ + $(top_builddir)/lib/c_glib/src/thrift/c_glib/libthrift_c_glib_la-thrift_configuration.o testcontainertest_SOURCES = testcontainertest.c testcontainertest_LDADD = \ @@ -92,14 +100,16 @@ testcontainertest_LDADD = \ $(top_builddir)/lib/c_glib/src/thrift/c_glib/transport/libthrift_c_glib_la-thrift_server_transport.o \ $(top_builddir)/lib/c_glib/src/thrift/c_glib/transport/libthrift_c_glib_la-thrift_server_socket.o \ $(top_builddir)/lib/c_glib/src/thrift/c_glib/server/libthrift_c_glib_la-thrift_server.o \ - libtestgenc.la + $(top_builddir)/lib/c_glib/src/thrift/c_glib/libthrift_c_glib_la-thrift_configuration.o \ + libtestgenc.la testtransportsocket_SOURCES = testtransportsocket.c testtransportsocket_LDADD = \ $(top_builddir)/lib/c_glib/src/thrift/c_glib/transport/libthrift_c_glib_la-thrift_transport.o \ $(top_builddir)/lib/c_glib/src/thrift/c_glib/transport/libthrift_c_glib_la-thrift_buffered_transport.o \ $(top_builddir)/lib/c_glib/src/thrift/c_glib/transport/libthrift_c_glib_la-thrift_server_transport.o \ - $(top_builddir)/lib/c_glib/src/thrift/c_glib/transport/libthrift_c_glib_la-thrift_server_socket.o + $(top_builddir)/lib/c_glib/src/thrift/c_glib/transport/libthrift_c_glib_la-thrift_server_socket.o \ + $(top_builddir)/lib/c_glib/src/thrift/c_glib/libthrift_c_glib_la-thrift_configuration.o testtransportsslsocket_SOURCES = testtransportsslsocket.c @@ -108,7 +118,8 @@ testtransportsslsocket_LDADD = \ $(top_builddir)/lib/c_glib/src/thrift/c_glib/transport/libthrift_c_glib_la-thrift_socket.o \ $(top_builddir)/lib/c_glib/src/thrift/c_glib/transport/libthrift_c_glib_la-thrift_buffered_transport.o \ $(top_builddir)/lib/c_glib/src/thrift/c_glib/transport/libthrift_c_glib_la-thrift_server_transport.o \ - $(top_builddir)/lib/c_glib/src/thrift/c_glib/transport/libthrift_c_glib_la-thrift_server_socket.o + $(top_builddir)/lib/c_glib/src/thrift/c_glib/transport/libthrift_c_glib_la-thrift_server_socket.o \ + $(top_builddir)/lib/c_glib/src/thrift/c_glib/libthrift_c_glib_la-thrift_configuration.o testbinaryprotocol_SOURCES = testbinaryprotocol.c @@ -118,7 +129,8 @@ testbinaryprotocol_LDADD = \ $(top_builddir)/lib/c_glib/src/thrift/c_glib/transport/libthrift_c_glib_la-thrift_framed_transport.o \ $(top_builddir)/lib/c_glib/src/thrift/c_glib/transport/libthrift_c_glib_la-thrift_socket.o \ $(top_builddir)/lib/c_glib/src/thrift/c_glib/transport/libthrift_c_glib_la-thrift_server_transport.o \ - $(top_builddir)/lib/c_glib/src/thrift/c_glib/transport/libthrift_c_glib_la-thrift_server_socket.o + $(top_builddir)/lib/c_glib/src/thrift/c_glib/transport/libthrift_c_glib_la-thrift_server_socket.o \ + $(top_builddir)/lib/c_glib/src/thrift/c_glib/libthrift_c_glib_la-thrift_configuration.o testcompactprotocol_SOURCES = testcompactprotocol.c testcompactprotocol_LDADD = \ @@ -127,35 +139,41 @@ testcompactprotocol_LDADD = \ $(top_builddir)/lib/c_glib/src/thrift/c_glib/transport/libthrift_c_glib_la-thrift_framed_transport.o \ $(top_builddir)/lib/c_glib/src/thrift/c_glib/transport/libthrift_c_glib_la-thrift_socket.o \ $(top_builddir)/lib/c_glib/src/thrift/c_glib/transport/libthrift_c_glib_la-thrift_server_transport.o \ - $(top_builddir)/lib/c_glib/src/thrift/c_glib/transport/libthrift_c_glib_la-thrift_server_socket.o + $(top_builddir)/lib/c_glib/src/thrift/c_glib/transport/libthrift_c_glib_la-thrift_server_socket.o \ + $(top_builddir)/lib/c_glib/src/thrift/c_glib/libthrift_c_glib_la-thrift_configuration.o testbufferedtransport_SOURCES = testbufferedtransport.c testbufferedtransport_LDADD = \ $(top_builddir)/lib/c_glib/src/thrift/c_glib/transport/libthrift_c_glib_la-thrift_transport.o \ $(top_builddir)/lib/c_glib/src/thrift/c_glib/transport/libthrift_c_glib_la-thrift_socket.o \ $(top_builddir)/lib/c_glib/src/thrift/c_glib/transport/libthrift_c_glib_la-thrift_server_transport.o \ - $(top_builddir)/lib/c_glib/src/thrift/c_glib/transport/libthrift_c_glib_la-thrift_server_socket.o + $(top_builddir)/lib/c_glib/src/thrift/c_glib/transport/libthrift_c_glib_la-thrift_server_socket.o \ + $(top_builddir)/lib/c_glib/src/thrift/c_glib/libthrift_c_glib_la-thrift_configuration.o testframedtransport_SOURCES = testframedtransport.c testframedtransport_LDADD = \ $(top_builddir)/lib/c_glib/src/thrift/c_glib/transport/libthrift_c_glib_la-thrift_transport.o \ $(top_builddir)/lib/c_glib/src/thrift/c_glib/transport/libthrift_c_glib_la-thrift_socket.o \ $(top_builddir)/lib/c_glib/src/thrift/c_glib/transport/libthrift_c_glib_la-thrift_server_transport.o \ - $(top_builddir)/lib/c_glib/src/thrift/c_glib/transport/libthrift_c_glib_la-thrift_server_socket.o + $(top_builddir)/lib/c_glib/src/thrift/c_glib/transport/libthrift_c_glib_la-thrift_server_socket.o \ + $(top_builddir)/lib/c_glib/src/thrift/c_glib/libthrift_c_glib_la-thrift_configuration.o testfdtransport_SOURCES = testfdtransport.c testfdtransport_LDADD = \ $(top_builddir)/lib/c_glib/src/thrift/c_glib/transport/libthrift_c_glib_la-thrift_transport.o \ - $(top_builddir)/lib/c_glib/src/thrift/c_glib/transport/libthrift_c_glib_la-thrift_fd_transport.o + $(top_builddir)/lib/c_glib/src/thrift/c_glib/transport/libthrift_c_glib_la-thrift_fd_transport.o \ + $(top_builddir)/lib/c_glib/src/thrift/c_glib/libthrift_c_glib_la-thrift_configuration.o testmemorybuffer_SOURCES = testmemorybuffer.c testmemorybuffer_LDADD = \ - $(top_builddir)/lib/c_glib/src/thrift/c_glib/transport/libthrift_c_glib_la-thrift_transport.o + $(top_builddir)/lib/c_glib/src/thrift/c_glib/transport/libthrift_c_glib_la-thrift_transport.o \ + $(top_builddir)/lib/c_glib/src/thrift/c_glib/libthrift_c_glib_la-thrift_configuration.o teststruct_SOURCES = teststruct.c teststruct_LDADD = \ $(top_builddir)/lib/c_glib/src/thrift/c_glib/protocol/libthrift_c_glib_la-thrift_protocol.o \ - $(top_builddir)/lib/c_glib/src/thrift/c_glib/transport/libthrift_c_glib_la-thrift_transport.o + $(top_builddir)/lib/c_glib/src/thrift/c_glib/transport/libthrift_c_glib_la-thrift_transport.o \ + $(top_builddir)/lib/c_glib/src/thrift/c_glib/libthrift_c_glib_la-thrift_configuration.o testsimpleserver_SOURCES = testsimpleserver.c testsimpleserver_LDADD = \ @@ -169,7 +187,8 @@ testsimpleserver_LDADD = \ $(top_builddir)/lib/c_glib/src/thrift/c_glib/transport/libthrift_c_glib_la-thrift_socket.o \ $(top_builddir)/lib/c_glib/src/thrift/c_glib/transport/libthrift_c_glib_la-thrift_server_transport.o \ $(top_builddir)/lib/c_glib/src/thrift/c_glib/transport/libthrift_c_glib_la-thrift_server_socket.o \ - $(top_builddir)/lib/c_glib/src/thrift/c_glib/server/libthrift_c_glib_la-thrift_server.o + $(top_builddir)/lib/c_glib/src/thrift/c_glib/server/libthrift_c_glib_la-thrift_server.o \ + $(top_builddir)/lib/c_glib/src/thrift/c_glib/libthrift_c_glib_la-thrift_configuration.o testdebugproto_SOURCES = testdebugproto.c testdebugproto_LDADD = libtestgenc.la @@ -178,6 +197,7 @@ testoptionalrequired_SOURCES = testoptionalrequired.c testoptionalrequired_LDADD = \ $(top_builddir)/lib/c_glib/src/thrift/c_glib/protocol/libthrift_c_glib_la-thrift_protocol.o \ $(top_builddir)/lib/c_glib/src/thrift/c_glib/transport/libthrift_c_glib_la-thrift_transport.o \ + $(top_builddir)/lib/c_glib/src/thrift/c_glib/libthrift_c_glib_la-thrift_configuration.o \ libtestgenc.la testthrifttest_SOURCES = testthrifttest.c @@ -185,6 +205,54 @@ testthrifttest_LDADD = libtestgenc.la \ $(top_builddir)/test/c_glib/src/thrift_test_handler.o testthrifttest_CFLAGS = -I$(top_srcdir)/test/c_glib/src -I./gen-c_glib $(GLIB_CFLAGS) +testthriftbinaryreadcheck_SOURCES = testthriftbinaryreadcheck.c +testthriftbinaryreadcheck_LDADD = \ + $(top_builddir)/lib/c_glib/src/thrift/c_glib/protocol/libthrift_c_glib_la-thrift_protocol.o \ + $(top_builddir)/lib/c_glib/src/thrift/c_glib/transport/libthrift_c_glib_la-thrift_transport.o \ + $(top_builddir)/lib/c_glib/src/thrift/c_glib/transport/libthrift_c_glib_la-thrift_framed_transport.o \ + $(top_builddir)/lib/c_glib/src/thrift/c_glib/transport/libthrift_c_glib_la-thrift_socket.o \ + $(top_builddir)/lib/c_glib/src/thrift/c_glib/transport/libthrift_c_glib_la-thrift_server_transport.o \ + $(top_builddir)/lib/c_glib/src/thrift/c_glib/transport/libthrift_c_glib_la-thrift_server_socket.o \ + $(top_builddir)/lib/c_glib/src/thrift/c_glib/libthrift_c_glib_la-thrift_configuration.o + +testthriftcompactreadcheck_SOURCES = testthriftcompactreadcheck.c +testthriftcompactreadcheck_LDADD = \ + $(top_builddir)/lib/c_glib/src/thrift/c_glib/protocol/libthrift_c_glib_la-thrift_protocol.o \ + $(top_builddir)/lib/c_glib/src/thrift/c_glib/transport/libthrift_c_glib_la-thrift_transport.o \ + $(top_builddir)/lib/c_glib/src/thrift/c_glib/transport/libthrift_c_glib_la-thrift_framed_transport.o \ + $(top_builddir)/lib/c_glib/src/thrift/c_glib/transport/libthrift_c_glib_la-thrift_socket.o \ + $(top_builddir)/lib/c_glib/src/thrift/c_glib/transport/libthrift_c_glib_la-thrift_server_transport.o \ + $(top_builddir)/lib/c_glib/src/thrift/c_glib/transport/libthrift_c_glib_la-thrift_server_socket.o \ + $(top_builddir)/lib/c_glib/src/thrift/c_glib/libthrift_c_glib_la-thrift_configuration.o + +testthriftbufferedreadcheck_SOURCES = testthriftbufferedreadcheck.c +testthriftbufferedreadcheck_LDADD = \ + $(top_builddir)/lib/c_glib/src/thrift/c_glib/transport/libthrift_c_glib_la-thrift_transport.o \ + $(top_builddir)/lib/c_glib/src/thrift/c_glib/transport/libthrift_c_glib_la-thrift_socket.o \ + $(top_builddir)/lib/c_glib/src/thrift/c_glib/transport/libthrift_c_glib_la-thrift_server_transport.o \ + $(top_builddir)/lib/c_glib/src/thrift/c_glib/transport/libthrift_c_glib_la-thrift_server_socket.o \ + $(top_builddir)/lib/c_glib/src/thrift/c_glib/libthrift_c_glib_la-thrift_configuration.o + +testthriftfdreadcheck_SOURCES = testthriftfdreadcheck.c +testthriftfdreadcheck_LDADD = \ + $(top_builddir)/lib/c_glib/src/thrift/c_glib/transport/libthrift_c_glib_la-thrift_transport.o \ + $(top_builddir)/lib/c_glib/src/thrift/c_glib/transport/libthrift_c_glib_la-thrift_fd_transport.o \ + $(top_builddir)/lib/c_glib/src/thrift/c_glib/libthrift_c_glib_la-thrift_configuration.o + +testthriftframedreadcheck_SOURCES = testthriftframedreadcheck.c +testthriftframedreadcheck_LDADD = \ + $(top_builddir)/lib/c_glib/src/thrift/c_glib/transport/libthrift_c_glib_la-thrift_transport.o \ + $(top_builddir)/lib/c_glib/src/thrift/c_glib/transport/libthrift_c_glib_la-thrift_socket.o \ + $(top_builddir)/lib/c_glib/src/thrift/c_glib/transport/libthrift_c_glib_la-thrift_server_transport.o \ + $(top_builddir)/lib/c_glib/src/thrift/c_glib/transport/libthrift_c_glib_la-thrift_server_socket.o \ + $(top_builddir)/lib/c_glib/src/thrift/c_glib/libthrift_c_glib_la-thrift_configuration.o + +testthriftmemorybufferreadcheck_SOURCES = testthriftmemorybufferreadcheck.c +testthriftmemorybufferreadcheck_LDADD = \ + $(top_builddir)/lib/c_glib/src/thrift/c_glib/transport/libthrift_c_glib_la-thrift_transport.o \ + $(top_builddir)/lib/c_glib/src/thrift/c_glib/libthrift_c_glib_la-thrift_configuration.o + + testthrifttestclient_SOURCES = testthrifttestclient.cpp testthrifttestclient_CPPFLAGS = -I../../cpp/src $(BOOST_CPPFLAGS) -I./gen-cpp -I../src -I./gen-c_glib $(GLIB_CFLAGS) testthrifttestclient_LDADD = ../../cpp/.libs/libthrift.la ../libthrift_c_glib.la libtestgenc.la libtestgencpp.la diff --git a/lib/c_glib/test/testbufferedtransport.c b/lib/c_glib/test/testbufferedtransport.c index c6e6b581d33..d01806d6132 100755 --- a/lib/c_glib/test/testbufferedtransport.c +++ b/lib/c_glib/test/testbufferedtransport.c @@ -175,11 +175,8 @@ test_read_and_write(void) static void thrift_socket_server_open (const int port, int times) { - int bytes = 0; ThriftServerTransport *transport = NULL; ThriftTransport *client = NULL; - guchar buf[10]; /* a buffer */ - guchar match[10] = TEST_DATA; int i; ThriftServerSocket *tsocket = g_object_new (THRIFT_TYPE_SERVER_SOCKET, "port", port, NULL); diff --git a/lib/c_glib/test/testframedtransport.c b/lib/c_glib/test/testframedtransport.c index 45397cef487..008e61e40cb 100755 --- a/lib/c_glib/test/testframedtransport.c +++ b/lib/c_glib/test/testframedtransport.c @@ -249,11 +249,8 @@ test_read_after_peer_close(void) static void thrift_socket_server_open (const int port, int times) { - int bytes = 0; ThriftServerTransport *transport = NULL; ThriftTransport *client = NULL; - guchar buf[10]; /* a buffer */ - guchar match[10] = TEST_DATA; int i; ThriftServerSocket *tsocket = g_object_new (THRIFT_TYPE_SERVER_SOCKET, diff --git a/lib/c_glib/test/testmemorybuffer.c b/lib/c_glib/test/testmemorybuffer.c index 9fb68b93d21..cc9c56c129c 100755 --- a/lib/c_glib/test/testmemorybuffer.c +++ b/lib/c_glib/test/testmemorybuffer.c @@ -186,7 +186,6 @@ static void test_read_and_write_external (void) { ThriftMemoryBuffer *tbuffer = NULL; - gchar *b; GError *error = NULL; GByteArray *buf = g_byte_array_new (); g_assert (buf != NULL); diff --git a/lib/c_glib/test/testthriftbinaryreadcheck.c b/lib/c_glib/test/testthriftbinaryreadcheck.c new file mode 100644 index 00000000000..f1caba85fcb --- /dev/null +++ b/lib/c_glib/test/testthriftbinaryreadcheck.c @@ -0,0 +1,279 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +#ifdef __GLIBC__ +#include +#define __NO_STRING_INLINES 1 +#endif + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#define TEST_BOOL TRUE +#define TEST_BYTE 123 +#define TEST_I16 12345 +#define TEST_I32 1234567890 +#define TEST_I64 G_GINT64_CONSTANT (123456789012345) +#define TEST_DOUBLE 1234567890.123 +#define TEST_STRING "this is a test string 1234567890!@#$%^&*()" +#define TEST_PORT 51199 + +#define MAX_MESSAGE_SIZE 4 + +static int transport_read_count = 0; +static int transport_read_error = 0; +static int transport_read_error_at = -1; +gint32 +my_thrift_transport_read_all (ThriftTransport *transport, gpointer buf, + guint32 len, GError **error) +{ + if (transport_read_count != transport_read_error_at + && transport_read_error == 0) + { + transport_read_count++; + return thrift_transport_read_all (transport, buf, len, error); + } + return -1; +} + +static int transport_write_count = 0; +static int transport_write_error = 0; +static int transport_write_error_at = -1; +gboolean +my_thrift_transport_write (ThriftTransport *transport, const gpointer buf, + const guint32 len, GError **error) +{ + if (transport_write_count != transport_write_error_at + && transport_write_error == 0) + { + transport_write_count++; + return thrift_transport_write (transport, buf, len, error); + } + return FALSE; +} + +#define thrift_transport_read_all my_thrift_transport_read_all +#define thrift_transport_write my_thrift_transport_write +#include "../src/thrift/c_glib/protocol/thrift_binary_protocol.c" +#undef thrift_transport_read_all +#undef thrift_transport_write + +static void thrift_server_complex_types (const int port); + +static void +test_create_and_destroy (void) +{ + GObject *object = NULL; + + /* create an object and then destroy it */ + object = g_object_new (THRIFT_TYPE_BINARY_PROTOCOL, NULL); + g_assert (object !=NULL); + g_object_unref (object); +} + +static void +test_initialize (void) +{ + ThriftConfiguration *tconfiguration = NULL; + ThriftSocket *tsocket = NULL; + ThriftBinaryProtocol *bprotocol = NULL; + ThriftSocket *temp = NULL; + ThriftConfiguration *tempconf = NULL; + + glong tempsize = 0; + + /* create a ThriftConfiguration */ + tconfiguration = g_object_new (THRIFT_TYPE_CONFIGURATION, "max_message_size", MAX_MESSAGE_SIZE, + "max_frame_size", MAX_MESSAGE_SIZE, NULL); + g_assert (tconfiguration != NULL); + /* create a ThriftTransport */ + tsocket = g_object_new (THRIFT_TYPE_SOCKET, "hostname", "localhost", + "port", 51188, "path", NULL, + "configuration", tconfiguration, + "remainingmessagesize", tconfiguration->maxMessageSize_, NULL); + g_assert (tsocket != NULL); + /* fetch the properties */ + g_object_get (G_OBJECT (tconfiguration), "max_message_size", &tempsize, NULL); + g_assert (tempsize == MAX_MESSAGE_SIZE); + /* fetch the properties */ + g_object_get (G_OBJECT (tsocket), "remainingmessagesize", &tempsize, NULL); + g_assert (tempsize == MAX_MESSAGE_SIZE); + /* fetch the properties */ + g_object_get (G_OBJECT (tsocket), "configuration", &tempconf, NULL); + g_object_unref (tempconf); + /* create a ThriftBinaryProtocol using Transport */ + bprotocol = g_object_new (THRIFT_TYPE_BINARY_PROTOCOL, "transport", tsocket, NULL); + g_assert (bprotocol != NULL); + /* fetch the properties */ + g_object_get (G_OBJECT (bprotocol), "transport", &temp, NULL); + g_object_unref (temp); + + /* clean up memory */ + g_object_unref (bprotocol); + g_object_unref (tsocket); + g_object_unref (tconfiguration); +} + +void +test_read_and_wirte_complex_types (void) +{ + int status; + pid_t pid; + ThriftConfiguration *tconfiguration = NULL; + ThriftSocket *tsocket = NULL; + ThriftTransport *transport = NULL; + ThriftBinaryProtocol *tb = NULL; + ThriftProtocol *protocol = NULL; + int port = TEST_PORT; + + /* fork a server from the client */ + pid = fork (); + g_assert (pid >= 0); + + if (pid == 0) + { + /* child listens */ + thrift_server_complex_types (port); + exit (0); + } else { + /* parent. wait a bit for the socket to be created. */ + sleep (1); + + /* create a ThriftConfiguration */ + tconfiguration = g_object_new (THRIFT_TYPE_CONFIGURATION, "max_message_size", MAX_MESSAGE_SIZE, + "max_frame_size", MAX_MESSAGE_SIZE, NULL); + g_assert (tconfiguration != NULL); + + /* create a ThriftSocket */ + tsocket = g_object_new (THRIFT_TYPE_SOCKET, "hostname", "localhost", + "port", port, "path", NULL, + "configuration", tconfiguration, NULL); + transport = THRIFT_TRANSPORT (tsocket); + THRIFT_TRANSPORT_GET_CLASS (tsocket)->resetConsumedMessageSize(THRIFT_TRANSPORT (tsocket), -1, NULL); + thrift_transport_open (transport, NULL); + g_assert (thrift_transport_is_open (transport)); + + /* create a ThriftBinaryTransport */ + tb = g_object_new (THRIFT_TYPE_BINARY_PROTOCOL, "transport", + tsocket, NULL); + protocol = THRIFT_PROTOCOL (tb); + g_assert (protocol != NULL); + + /* test 1st write failure on a map */ + g_assert (thrift_binary_protocol_write_map_begin (protocol, T_VOID, T_BYTE, + 1, NULL) > 0); + g_assert (thrift_binary_protocol_write_map_end (protocol, NULL) == 0); + + g_assert (thrift_binary_protocol_write_map_begin (protocol, T_I32, T_BYTE, + 1, NULL) > 0); + g_assert (thrift_binary_protocol_write_map_end (protocol, NULL) == 0); + + /* test list operations */ + g_assert (thrift_binary_protocol_write_list_begin (protocol, T_BYTE, + 1, NULL) > 0); + g_assert (thrift_binary_protocol_write_list_end (protocol, NULL) == 0); + + g_assert (thrift_binary_protocol_write_list_begin (protocol, T_I32, + 10, NULL) > 0); + g_assert (thrift_binary_protocol_write_list_end (protocol, NULL) == 0); + + /* clean up */ + thrift_transport_close (transport, NULL); + g_object_unref (tsocket); + g_object_unref (protocol); + g_object_unref (tconfiguration); + g_assert (wait (&status) == pid); + g_assert (status == 0); + } +} + +static void +thrift_server_complex_types (const int port) +{ + ThriftServerTransport *transport = NULL; + ThriftTransport *client = NULL; + ThriftBinaryProtocol *tbp = NULL; + ThriftProtocol *protocol = NULL; + ThriftType element_type = T_VOID, + key_type = T_VOID, + value_type = T_VOID; + guint32 size = 0; + + ThriftConfiguration *tconfiguration = g_object_new (THRIFT_TYPE_CONFIGURATION, + "max_message_size", MAX_MESSAGE_SIZE, + "max_frame_size", MAX_MESSAGE_SIZE, NULL); + + ThriftServerSocket *tsocket = g_object_new (THRIFT_TYPE_SERVER_SOCKET, "port", port, + "configuration", tconfiguration, NULL); + transport = THRIFT_SERVER_TRANSPORT (tsocket); + THRIFT_SERVER_TRANSPORT_GET_CLASS (tsocket)->resetConsumedMessageSize(transport, -1, NULL); + thrift_server_transport_listen (transport, NULL); + client = thrift_server_transport_accept (transport, NULL); + g_assert (client != NULL); + + tbp = g_object_new (THRIFT_TYPE_BINARY_PROTOCOL, "transport", + client, NULL); + protocol = THRIFT_PROTOCOL(tbp); + + g_assert (thrift_binary_protocol_read_map_begin (protocol, &key_type, &value_type, + &size, NULL) > 0); + g_assert (thrift_binary_protocol_read_map_end (protocol, NULL) == 0); + + g_assert (thrift_binary_protocol_read_map_begin (protocol, &key_type, &value_type, + &size, NULL) == -1); + g_assert (thrift_binary_protocol_read_map_end (protocol, NULL) == 0); + + /* test read failure */ + g_assert (thrift_binary_protocol_read_list_begin (protocol, &element_type, + &size, NULL) > 0); + g_assert (thrift_binary_protocol_read_list_end(protocol, NULL) == 0); + + g_assert (thrift_binary_protocol_read_list_begin (protocol, &element_type, + &size, NULL) == -1); + g_assert (thrift_binary_protocol_read_list_end(protocol, NULL) == 0); + + g_object_unref (client); + /* TODO: investigate g_object_unref (tbp); */ + g_object_unref (tsocket); + g_object_unref (tconfiguration); +} + +int +main (int argc, char *argv[]) +{ +#if (!GLIB_CHECK_VERSION (2, 36, 0)) + g_type_init (); +#endif + + g_test_init (&argc, &argv, NULL); + + g_test_add_func ("/testthriftbinaryreadcheck/CreateAndDestroy", test_create_and_destroy); + g_test_add_func ("/testthriftbinaryreadcheck/Initialize", test_initialize); + g_test_add_func ("/testthriftbinaryreadcheck/test_read_and_write_complex_types", test_read_and_wirte_complex_types); + + return g_test_run (); +} diff --git a/lib/c_glib/test/testthriftbufferedreadcheck.c b/lib/c_glib/test/testthriftbufferedreadcheck.c new file mode 100755 index 00000000000..4870ae8cf31 --- /dev/null +++ b/lib/c_glib/test/testthriftbufferedreadcheck.c @@ -0,0 +1,223 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#include +#include +#include + +#include +#include +#include +#include + +#define TEST_DATA { 'a', 'b', 'c' } + +#define MAX_MESSAGE_SIZE 3 + +#include "../src/thrift/c_glib/transport/thrift_buffered_transport.c" + +static void thrift_server (const int port); +static void thrift_socket_server_open (const int port, int times); + +static void +test_open_and_close(void) +{ + ThriftSocket *tsocket = NULL; + ThriftTransport *transport = NULL; + GError *err = NULL; + pid_t pid; + int port = 51199; + int status; + + pid = fork (); + g_assert ( pid >= 0 ); + + if ( pid == 0 ) + { + /* child listens */ + thrift_socket_server_open (port,1); + exit (0); + } else { + /* parent connects, wait a bit for the socket to be created */ + sleep (1); + /* create a ThriftSocket */ + tsocket = g_object_new (THRIFT_TYPE_SOCKET, "hostname", "localhost", + "port", port, NULL); + + /* create a BufferedTransport wrapper of the Socket */ + transport = g_object_new (THRIFT_TYPE_BUFFERED_TRANSPORT, + "transport", THRIFT_TRANSPORT (tsocket), + NULL); + + /* this shouldn't work */ + g_assert (thrift_buffered_transport_open (transport, NULL) == TRUE); + g_assert (thrift_buffered_transport_is_open (transport) == TRUE); + g_assert (thrift_buffered_transport_close (transport, NULL) == TRUE); + g_object_unref (transport); + g_object_unref (tsocket); + + /* try and underlying socket failure */ + tsocket = g_object_new (THRIFT_TYPE_SOCKET, "hostname", "localhost.broken", + NULL); + + /* create a BufferedTransport wrapper of the Socket */ + transport = g_object_new (THRIFT_TYPE_BUFFERED_TRANSPORT, + "transport", THRIFT_TRANSPORT (tsocket), NULL); + + g_assert (thrift_buffered_transport_open (transport, &err) == FALSE); + g_object_unref (transport); + g_object_unref (tsocket); + g_error_free (err); + err = NULL; + g_assert ( wait (&status) == pid ); + g_assert ( status == 0 ); + } +} + +static void +test_read_and_write(void) +{ + int status; + pid_t pid; + ThriftSocket *tsocket = NULL; + ThriftTransport *transport = NULL; + int port = 51199; + guchar buf[3] = TEST_DATA; /* a buffer */ + + pid = fork (); + g_assert ( pid >= 0 ); + + if ( pid == 0 ) + { + /* child listens */ + thrift_server (port); + exit (0); + } else { + /* parent connects, wait a bit for the socket to be created */ + sleep (1); + + tsocket = g_object_new (THRIFT_TYPE_SOCKET, "hostname", "localhost", + "port", port, NULL); + transport = g_object_new (THRIFT_TYPE_BUFFERED_TRANSPORT, + "transport", THRIFT_TRANSPORT (tsocket), + "w_buf_size", 4, NULL); + + g_assert (thrift_buffered_transport_open (transport, NULL) == TRUE); + g_assert (thrift_buffered_transport_is_open (transport)); + + /* write 3 bytes */ + thrift_buffered_transport_write (transport, buf, 3, NULL); + + /* write 4 bytes */ + thrift_buffered_transport_write (transport, buf, 4, NULL); + + thrift_buffered_transport_write_end (transport, NULL); + thrift_buffered_transport_flush (transport, NULL); + thrift_buffered_transport_close (transport, NULL); + + g_object_unref (transport); + g_object_unref (tsocket); + + g_assert ( wait (&status) == pid ); + g_assert ( status == 0 ); + } +} + + +static void +thrift_socket_server_open (const int port, int times) +{ + ThriftServerTransport *transport = NULL; + ThriftTransport *client = NULL; + int i; + + ThriftConfiguration *tconfiguration = g_object_new (THRIFT_TYPE_CONFIGURATION, "max_message_size", MAX_MESSAGE_SIZE, + "max_frame_size", MAX_MESSAGE_SIZE, NULL); + + ThriftServerSocket *tsocket = g_object_new (THRIFT_TYPE_SERVER_SOCKET, "port", port, + "configuration", tconfiguration, NULL); + + transport = THRIFT_SERVER_TRANSPORT (tsocket); + THRIFT_SERVER_TRANSPORT_GET_CLASS (tsocket)->resetConsumedMessageSize(transport, -1, NULL); + thrift_server_transport_listen (transport, NULL); + for(i=0;i +#endif + +#ifdef __GLIBC__ +#define __NO_STRING_INLINES 1 +#endif + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#define TEST_BOOL TRUE +#define TEST_BYTE 123 +#define TEST_I16 12345 +#define TEST_I32 1234567890 +#define TEST_I64 123456789012345 +#define TEST_NI16 (-12345) +#define TEST_NI32 (-1234567890) +#define TEST_NI64 (-123456789012345) +#define TEST_DOUBLE 1234567890.123 +#define TEST_STRING "this is a test string 1234567890!@#$%^&*()" +#define TEST_PORT 51199 + +#define MAX_MESSAGE_SIZE 2 + +static int transport_read_count = 0; +static int transport_read_error = 0; +static int transport_read_error_at = -1; +gint32 +my_thrift_transport_read_all (ThriftTransport *transport, gpointer buf, + guint32 len, GError **error) +{ + if (transport_read_count != transport_read_error_at + && transport_read_error == 0) + { + transport_read_count++; + return thrift_transport_read_all (transport, buf, len, error); + } + return -1; +} + +static int transport_write_count = 0; +static int transport_write_error = 0; +static int transport_write_error_at = -1; +gboolean +my_thrift_transport_write (ThriftTransport *transport, const gpointer buf, + const guint32 len, GError **error) +{ + if (transport_write_count != transport_write_error_at + && transport_write_error == 0) + { + transport_write_count++; + return thrift_transport_write (transport, buf, len, error); + } + return FALSE; +} + +#define thrift_transport_read_all my_thrift_transport_read_all +#define thrift_transport_write my_thrift_transport_write +#include "../src/thrift/c_glib/protocol/thrift_compact_protocol.c" +#undef thrift_transport_read_all +#undef thrift_transport_write + +static void thrift_server_complex_types (const int port); + +static void +test_create_and_destroy (void) +{ + GObject *object = NULL; + + /* create an object and then destroy it */ + object = g_object_new (THRIFT_TYPE_COMPACT_PROTOCOL, NULL); + g_assert (object != NULL); + g_object_unref (object); +} + +static void +test_initialize (void) +{ + ThriftSocket *tsocket = NULL; + ThriftCompactProtocol *protocol = NULL; + ThriftSocket *temp = NULL; + ThriftConfiguration *tconfiguration = NULL; + ThriftConfiguration *tempconf = NULL; + glong tempsize = 0; + + /* create a ThriftConfiguration */ + tconfiguration = g_object_new (THRIFT_TYPE_CONFIGURATION, "max_message_size", MAX_MESSAGE_SIZE, + "max_frame_size", MAX_MESSAGE_SIZE, NULL); + /* create a ThriftTransport */ + tsocket = g_object_new (THRIFT_TYPE_SOCKET, "hostname", "localhost", + "port", 51188, "configuration", tconfiguration, + "remainingmessagesize", MAX_MESSAGE_SIZE, NULL); + g_assert (tsocket != NULL); + /* fetch the properties */ + g_object_get (G_OBJECT (tconfiguration), "max_message_size", &tempsize, NULL); + g_assert (tempsize == MAX_MESSAGE_SIZE); + /* fetch the properties */ + g_object_get (G_OBJECT (tsocket), "remainingmessagesize", &tempsize, NULL); + g_assert (tempsize == MAX_MESSAGE_SIZE); + /* fetch the properties */ + g_object_get (G_OBJECT (tsocket), "configuration", &tempconf, NULL); + g_object_unref (tempconf); + /* create a ThriftCompactProtocol using the Transport */ + protocol = g_object_new (THRIFT_TYPE_COMPACT_PROTOCOL, "transport", + tsocket, NULL); + g_assert (protocol != NULL); + /* fetch the properties */ + g_object_get (G_OBJECT (protocol), "transport", &temp, NULL); + g_object_unref (temp); + + /* clean up memory */ + g_object_unref (protocol); + g_object_unref (tsocket); +} + + +static void +test_read_and_write_complex_types (void) +{ + int status; + pid_t pid; + ThriftSocket *tsocket = NULL; + ThriftTransport *transport = NULL; + ThriftCompactProtocol *tc = NULL; + ThriftProtocol *protocol = NULL; + int port = TEST_PORT; + + /* fork a server from the client */ + pid = fork (); + g_assert (pid >= 0); + + if (pid == 0) + { + /* child listens */ + thrift_server_complex_types (port); + exit (0); + } else { + /* parent. wait a bit for the socket to be created. */ + sleep (1); + + /* create a ThriftSocket */ + tsocket = g_object_new (THRIFT_TYPE_SOCKET, "hostname", "localhost", + "port", port, NULL); + transport = THRIFT_TRANSPORT (tsocket); + thrift_transport_open (transport, NULL); + g_assert (thrift_transport_is_open (transport)); + + /* create a ThriftCompactTransport */ + tc = g_object_new (THRIFT_TYPE_COMPACT_PROTOCOL, "transport", + tsocket, NULL); + protocol = THRIFT_PROTOCOL (tc); + g_assert (protocol != NULL); + + g_assert (thrift_compact_protocol_write_map_begin (protocol, T_VOID, T_BYTE, + 1, NULL) > 0); + g_assert (thrift_compact_protocol_write_map_end (protocol, NULL) == 0); + + g_assert (thrift_compact_protocol_write_map_begin (protocol, T_VOID, T_BYTE, + 3, NULL) > 0); + g_assert (thrift_compact_protocol_write_map_end (protocol, NULL) == 0); + + g_assert (thrift_compact_protocol_write_list_begin (protocol, T_BYTE, + 1, NULL) > 0); + g_assert (thrift_compact_protocol_write_list_end (protocol, NULL) == 0); + + g_assert (thrift_compact_protocol_write_list_begin (protocol, T_I32, + 3, NULL) > 0); + g_assert (thrift_compact_protocol_write_list_end (protocol, NULL) == 0); + + /* clean up */ + thrift_transport_close (transport, NULL); + g_object_unref (tsocket); + g_object_unref (protocol); + g_assert (wait (&status) == pid); + g_assert (status == 0); + } +} + + +static void +thrift_server_complex_types (const int port) +{ + ThriftServerTransport *transport = NULL; + ThriftTransport *client = NULL; + ThriftCompactProtocol *tc = NULL; + ThriftProtocol *protocol = NULL; + ThriftType element_type, key_type, value_type; + guint32 size = 0; + + ThriftConfiguration *tconfiguration = g_object_new (THRIFT_TYPE_CONFIGURATION, "max_message_size", MAX_MESSAGE_SIZE, + "max_frame_size", MAX_MESSAGE_SIZE, NULL); + ThriftServerSocket *tsocket = g_object_new (THRIFT_TYPE_SERVER_SOCKET, + "port", port, "configuration", tconfiguration, NULL); + transport = THRIFT_SERVER_TRANSPORT (tsocket); + THRIFT_SERVER_TRANSPORT_GET_CLASS (tsocket)->resetConsumedMessageSize(transport, -1, NULL); + thrift_server_transport_listen (transport, NULL); + client = thrift_server_transport_accept (transport, NULL); + g_assert (client != NULL); + + tc = g_object_new (THRIFT_TYPE_COMPACT_PROTOCOL, "transport", + client, NULL); + protocol = THRIFT_PROTOCOL (tc); + + g_assert (thrift_compact_protocol_read_map_begin (protocol, &key_type, &value_type, + &size, NULL) > 0); + g_assert (thrift_compact_protocol_read_map_end (protocol, NULL) == 0); + + g_assert (thrift_compact_protocol_read_map_begin (protocol, &key_type, &value_type, + &size, NULL) == -1); + g_assert (thrift_compact_protocol_read_map_end (protocol, NULL) == 0); + + g_assert (thrift_compact_protocol_read_list_begin (protocol, &element_type, + &size, NULL) > 0); + g_assert (thrift_compact_protocol_read_list_end (protocol, NULL) == 0); + + g_assert (thrift_compact_protocol_read_list_begin (protocol, &element_type, + &size, NULL) == -1); + g_assert (thrift_compact_protocol_read_list_end (protocol, NULL) == 0); + + g_object_unref (client); + g_object_unref (tsocket); + g_object_unref (tconfiguration); +} + + +int +main (int argc, char *argv[]) +{ +#if (!GLIB_CHECK_VERSION (2, 36, 0)) + g_type_init (); +#endif + + g_test_init (&argc, &argv, NULL); + + g_test_add_func ("/testthriftcompactreadcheck/CreateAndDestroy", + test_create_and_destroy); + g_test_add_func ("/testthriftcompactreadcheck/Initialize", test_initialize); + g_test_add_func ("/testthriftcompactreadcheck/ReadAndWriteComplexTypes", + test_read_and_write_complex_types); + + return g_test_run (); +} diff --git a/lib/c_glib/test/testthriftfdreadcheck.c b/lib/c_glib/test/testthriftfdreadcheck.c new file mode 100755 index 00000000000..986b70d7140 --- /dev/null +++ b/lib/c_glib/test/testthriftfdreadcheck.c @@ -0,0 +1,182 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + + +#include +#include +#include +#include + +#include +#include + +#include +#include + +#define MAX_MESSAGE_SIZE 2 + +static const gchar TEST_DATA[12] = "abcde01234!"; + +static void +test_open_and_close (void) +{ + ThriftConfiguration *configuration; + ThriftTransport *transport; + ThriftTransportClass *klass; + GError *error; + gint fd; + gchar *filename; + + error = NULL; + filename = NULL; + + fd = g_file_open_tmp (NULL, &filename, &error); + g_assert (fd >= 0); + + configuration = g_object_new (THRIFT_TYPE_CONFIGURATION, "max_message_size", MAX_MESSAGE_SIZE, + "max_frame_size", MAX_MESSAGE_SIZE, NULL); + + transport = THRIFT_TRANSPORT (g_object_new (THRIFT_TYPE_FD_TRANSPORT, + "configuration", configuration, "fd", fd, + NULL)); + klass = THRIFT_TRANSPORT_GET_CLASS (transport); + + /* open is no-op */ + g_assert (klass->is_open (transport)); + g_assert (klass->peek (transport, &error)); + g_assert (klass->open (transport, &error)); + g_assert (klass->is_open (transport)); + g_assert (klass->peek (transport, &error)); + + g_assert (klass->close (transport, &error)); + g_assert (! klass->open (transport, &error)); + g_assert (! klass->is_open (transport)); + g_assert (! klass->peek (transport, &error)); + + /* already closed */ + g_assert (close (fd) != 0); + g_assert (errno == EBADF); + + g_object_unref (transport); + g_object_unref (configuration); + + g_remove (filename); + g_free (filename); + + /* test bad fd */ + transport = THRIFT_TRANSPORT (g_object_new (THRIFT_TYPE_FD_TRANSPORT, + "fd", -1, + NULL)); + klass = THRIFT_TRANSPORT_GET_CLASS (transport); + + g_assert (! klass->is_open (transport)); + error = NULL; + g_assert (! klass->peek (transport, &error)); + error = NULL; + g_assert (! klass->open (transport, &error)); + error = NULL; + g_assert (! klass->close (transport, &error)); + + g_object_unref (transport); +} + +static void +test_read_and_write (void) +{ + gchar out_buf[8]; + gchar *b; + gint want, got; + ThriftConfiguration *configuration; + ThriftTransport *transport; + ThriftTransportClass *klass; + GError *error; + gint fd; + gchar *filename; + + error = NULL; + filename = NULL; + + fd = g_file_open_tmp (NULL, &filename, &error); + g_assert (fd >= 0); + + configuration = g_object_new (THRIFT_TYPE_CONFIGURATION, "max_message_size", MAX_MESSAGE_SIZE, + "max_frame_size", MAX_MESSAGE_SIZE, NULL); + /* write */ + transport = THRIFT_TRANSPORT (g_object_new (THRIFT_TYPE_FD_TRANSPORT, + "configuration", configuration, "fd", fd, + NULL)); + klass = THRIFT_TRANSPORT_GET_CLASS (transport); + g_assert (klass->is_open (transport)); + g_assert (klass->write (transport, (gpointer) TEST_DATA, 11, &error)); + g_assert (klass->flush (transport, &error)); + g_assert (klass->close (transport, &error)); + g_object_unref (transport); + + /* read */ + fd = open(filename, O_RDONLY, S_IRUSR | S_IWUSR); + g_assert (fd >= 0); + + transport = THRIFT_TRANSPORT (g_object_new (THRIFT_TYPE_FD_TRANSPORT, + "configuration", configuration, + "remainingmessagesize", MAX_MESSAGE_SIZE, + "fd", fd, + NULL)); + klass = THRIFT_TRANSPORT_GET_CLASS (transport); + + memset(out_buf, 0, 8); + b = out_buf; + want = 2; + while (want > 0) { + got = klass->read (transport, (gpointer) b, want, &error); + g_assert (got > 0 && got <= want); + b += got; + want -= got; + } + g_assert (memcmp (out_buf, TEST_DATA, 2) == 0); + + memset(out_buf, 0, 8); + b = out_buf; + want = 4; + got = klass->read (transport, (gpointer) b, want, &error); + g_assert (got < 0); + + g_assert (klass->close (transport, &error)); + g_object_unref (transport); + g_object_unref (configuration); + + /* clean up */ + + g_remove (filename); + g_free (filename); +} + +int +main (int argc, char *argv[]) +{ +#if (!GLIB_CHECK_VERSION (2, 36, 0)) + g_type_init (); +#endif + + g_test_init (&argc, &argv, NULL); + + g_test_add_func ("/testfdtransport/OpenAndClose", test_open_and_close); + g_test_add_func ("/testfdtransport/ReadAndWrite", test_read_and_write); + + return g_test_run (); +} diff --git a/lib/c_glib/test/testthriftframedreadcheck.c b/lib/c_glib/test/testthriftframedreadcheck.c new file mode 100755 index 00000000000..95e853f8766 --- /dev/null +++ b/lib/c_glib/test/testthriftframedreadcheck.c @@ -0,0 +1,222 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#include +#include + +#include +#include +#include +#include + +#define TEST_DATA { 'a', 'b' } +#define MAX_MESSAGE_SIZE 2 + +#include "../src/thrift/c_glib/transport/thrift_framed_transport.c" + +static void thrift_server (const int port); +static void thrift_socket_server_open (const int port, int times); + +static void +test_open_and_close(void) +{ + ThriftSocket *tsocket = NULL; + ThriftTransport *transport = NULL; + GError *err = NULL; + pid_t pid; + int port = 51199; + int status; + + pid = fork (); + g_assert ( pid >= 0 ); + + if ( pid == 0 ) + { + /* child listens */ + thrift_socket_server_open (port,1); + exit (0); + } else { + /* parent connects, wait a bit for the socket to be created */ + sleep (1); + /* create a ThriftSocket */ + tsocket = g_object_new (THRIFT_TYPE_SOCKET, "hostname", "localhost", + "port", port, NULL); + + /* create a BufferedTransport wrapper of the Socket */ + transport = g_object_new (THRIFT_TYPE_FRAMED_TRANSPORT, + "transport", THRIFT_TRANSPORT (tsocket), NULL); + + /* this shouldn't work */ + g_assert (thrift_framed_transport_open (transport, NULL) == TRUE); + g_assert (thrift_framed_transport_is_open (transport) == TRUE); + g_assert (thrift_framed_transport_close (transport, NULL) == TRUE); + g_object_unref (transport); + g_object_unref (tsocket); + + /* try and underlying socket failure */ + tsocket = g_object_new (THRIFT_TYPE_SOCKET, "hostname", "localhost.broken", + NULL); + + /* create a BufferedTransport wrapper of the Socket */ + transport = g_object_new (THRIFT_TYPE_FRAMED_TRANSPORT, + "transport", THRIFT_TRANSPORT (tsocket), NULL); + + g_assert (thrift_framed_transport_open (transport, &err) == FALSE); + g_object_unref (transport); + g_object_unref (tsocket); + g_error_free (err); + err = NULL; + + g_assert ( wait (&status) == pid ); + g_assert ( status == 0 ); + } +} + +static void +test_read_and_write(void) +{ + int status; + pid_t pid; + ThriftSocket *tsocket = NULL; + ThriftTransport *transport = NULL; + int port = 51199; + guchar buf[10] = TEST_DATA; /* a buffer */ + + pid = fork (); + g_assert ( pid >= 0 ); + + if ( pid == 0 ) + { + /* child listens */ + thrift_server (port); + exit (0); + } else { + /* parent connects, wait a bit for the socket to be created */ + sleep (1); + + tsocket = g_object_new (THRIFT_TYPE_SOCKET, "hostname", "localhost", + "port", port, NULL); + transport = g_object_new (THRIFT_TYPE_FRAMED_TRANSPORT, + "transport", THRIFT_TRANSPORT (tsocket), + "w_buf_size", 4, NULL); + + g_assert (thrift_framed_transport_open (transport, NULL) == TRUE); + g_assert (thrift_framed_transport_is_open (transport)); + + /* write 2 bytes */ + thrift_framed_transport_write (transport, buf, 2, NULL); + thrift_framed_transport_flush (transport, NULL); + + thrift_framed_transport_write (transport, buf, 3, NULL); + thrift_framed_transport_flush (transport, NULL); + + thrift_framed_transport_write_end (transport, NULL); + thrift_framed_transport_flush (transport, NULL); + thrift_framed_transport_close (transport, NULL); + + g_object_unref (transport); + g_object_unref (tsocket); + + g_assert ( wait (&status) == pid ); + g_assert ( status == 0 ); + } +} + +static void +thrift_socket_server_open (const int port, int times) +{ + ThriftServerTransport *transport = NULL; + ThriftTransport *client = NULL; + int i; + + ThriftConfiguration *tconfiguration = g_object_new (THRIFT_TYPE_CONFIGURATION, + "max_message_size", MAX_MESSAGE_SIZE, + "max_frame_size", MAX_MESSAGE_SIZE, NULL); + + ThriftServerSocket *tsocket = g_object_new (THRIFT_TYPE_SERVER_SOCKET, + "port", port, "configuration", tconfiguration, NULL); + + transport = THRIFT_SERVER_TRANSPORT (tsocket); + thrift_server_transport_listen (transport, NULL); + for(i=0;i + +#include +#include +#include +#include + +static const gchar TEST_DATA[11] = "abcdefghij"; + +#include "../src/thrift/c_glib/transport/thrift_memory_buffer.c" + +#define MAX_MESSAGE_SIZE 2 + +static void +test_open_and_close (void) +{ + ThriftMemoryBuffer *tbuffer = NULL; + ThriftConfiguration *tconfiguration = NULL; + + /* create a ThriftConfiguration */ + tconfiguration = g_object_new (THRIFT_TYPE_CONFIGURATION, + "max_message_size", MAX_MESSAGE_SIZE, + "max_frame_size", MAX_MESSAGE_SIZE, + NULL); + /* create a ThriftMemoryBuffer */ + tbuffer = g_object_new (THRIFT_TYPE_MEMORY_BUFFER, "configuration", tconfiguration, NULL); + + /* no-ops */ + g_assert (thrift_memory_buffer_open (THRIFT_TRANSPORT (tbuffer), NULL) == TRUE); + g_assert (thrift_memory_buffer_is_open (THRIFT_TRANSPORT (tbuffer)) == TRUE); + g_assert (thrift_memory_buffer_close (THRIFT_TRANSPORT (tbuffer), NULL) == TRUE); + + g_object_unref (tbuffer); + g_object_unref (tconfiguration); +} + +static void +test_read_and_write (void) +{ + ThriftConfiguration *tconfiguration = NULL; + ThriftMemoryBuffer *tbuffer = NULL; + gint got, want; + gchar read[10]; + gchar *b; + GError *error = NULL; + + tconfiguration = g_object_new (THRIFT_TYPE_CONFIGURATION, "max_message_size", MAX_MESSAGE_SIZE, NULL); + tbuffer = g_object_new (THRIFT_TYPE_MEMORY_BUFFER, "buf_size", 15, "configuration", tconfiguration, NULL); + THRIFT_TRANSPORT_GET_CLASS (tbuffer)->resetConsumedMessageSize(THRIFT_TRANSPORT(tbuffer), -1, NULL); + g_assert (thrift_memory_buffer_write (THRIFT_TRANSPORT (tbuffer), + (gpointer) TEST_DATA, 10, &error) == TRUE); + g_assert (error == NULL); + + memset(read, 0, 10); + b = read; + want = 10; + got = thrift_memory_buffer_read (THRIFT_TRANSPORT (tbuffer), + (gpointer) b, want, &error); + g_assert (got < 0); + g_object_unref (tbuffer); + g_object_unref (tconfiguration); +} + +int +main(int argc, char *argv[]) +{ +#if (!GLIB_CHECK_VERSION (2, 36, 0)) + g_type_init (); +#endif + + g_test_init (&argc, &argv, NULL); + + g_test_add_func ("/testthriftmemorybufferreadcheck/OpenAndClose", test_open_and_close); + g_test_add_func ("/testthriftmemorybufferreadcheck/ReadAndWrite", test_read_and_write); + + return g_test_run (); +} diff --git a/lib/c_glib/test/testthrifttestclient.cpp b/lib/c_glib/test/testthrifttestclient.cpp old mode 100755 new mode 100644 index 5b06883ebcf..5732996ef73 --- a/lib/c_glib/test/testthrifttestclient.cpp +++ b/lib/c_glib/test/testthrifttestclient.cpp @@ -25,7 +25,7 @@ #include #include #include -#include +#include #include #include "ThriftTest.h" #include "ThriftTest_types.h" @@ -67,58 +67,58 @@ bool Insanity::operator<(thrift::test::Insanity const& other) const { class TestHandler : public ThriftTestIf { public: - TestHandler() {} + TestHandler() = default; - void testVoid() { + void testVoid() override { cout << "[C -> C++] testVoid()" << endl; } - void testString(string& out, const string &thing) { + void testString(string& out, const string &thing) override { cout << "[C -> C++] testString(\"" << thing << "\")" << endl; out = thing; } - bool testBool(const bool thing) { + bool testBool(const bool thing) override { cout << "[C -> C++] testBool(" << (thing ? "true" : "false") << ")" << endl; return thing; } - int8_t testByte(const int8_t thing) { + int8_t testByte(const int8_t thing) override { cout << "[C -> C++] testByte(" << (int)thing << ")" << endl; return thing; } - int32_t testI32(const int32_t thing) { + int32_t testI32(const int32_t thing) override { cout << "[C -> C++] testI32(" << thing << ")" << endl; return thing; } - int64_t testI64(const int64_t thing) { + int64_t testI64(const int64_t thing) override { cout << "[C -> C++] testI64(" << thing << ")" << endl; return thing; } - double testDouble(const double thing) { + double testDouble(const double thing) override { cout.precision(6); cout << "[C -> C++] testDouble(" << fixed << thing << ")" << endl; return thing; } - void testBinary(string& out, const string &thing) { + void testBinary(string& out, const string &thing) override { cout << "[C -> C++] testBinary(\"" << thing << "\")" << endl; out = thing; } - void testStruct(Xtruct& out, const Xtruct &thing) { + void testStruct(Xtruct& out, const Xtruct &thing) override { cout << "[C -> C++] testStruct({\"" << thing.string_thing << "\", " << (int)thing.byte_thing << ", " << thing.i32_thing << ", " << thing.i64_thing << "})" << endl; out = thing; } - void testNest(Xtruct2& out, const Xtruct2& nest) { + void testNest(Xtruct2& out, const Xtruct2& nest) override { const Xtruct &thing = nest.struct_thing; cout << "[C -> C++] testNest({" << (int)nest.byte_thing << ", {\"" << thing.string_thing << "\", " << (int)thing.byte_thing << ", " << thing.i32_thing << ", " << thing.i64_thing << "}, " << nest.i32_thing << "})" << endl; out = nest; } - void testMap(map &out, const map &thing) { + void testMap(map &out, const map &thing) override { cout << "[C -> C++] testMap({"; map::const_iterator m_iter; bool first = true; @@ -134,7 +134,7 @@ class TestHandler : public ThriftTestIf { out = thing; } - void testStringMap(map &out, const map &thing) { + void testStringMap(map &out, const map &thing) override { cout << "[C -> C++] testStringMap({"; map::const_iterator m_iter; bool first = true; @@ -151,7 +151,7 @@ class TestHandler : public ThriftTestIf { } - void testSet(set &out, const set &thing) { + void testSet(set &out, const set &thing) override { cout << "[C -> C++] testSet({"; set::const_iterator s_iter; bool first = true; @@ -167,7 +167,7 @@ class TestHandler : public ThriftTestIf { out = thing; } - void testList(vector &out, const vector &thing) { + void testList(vector &out, const vector &thing) override { cout << "[C -> C++] testList({"; vector::const_iterator l_iter; bool first = true; @@ -183,16 +183,16 @@ class TestHandler : public ThriftTestIf { out = thing; } - Numberz::type testEnum(const Numberz::type thing) { + Numberz::type testEnum(const Numberz::type thing) override { cout << "[C -> C++] testEnum(" << thing << ")" << endl; return thing; } - UserId testTypedef(const UserId thing) { + UserId testTypedef(const UserId thing) override { cout << "[C -> C++] testTypedef(" << thing << ")" << endl; return thing; } - void testMapMap(map > &mapmap, const int32_t hello) { + void testMapMap(map > &mapmap, const int32_t hello) override { cout << "[C -> C++] testMapMap(" << hello << ")" << endl; map pos; @@ -207,7 +207,7 @@ class TestHandler : public ThriftTestIf { } - void testInsanity(map > &insane, const Insanity &argument) { + void testInsanity(map > &insane, const Insanity &argument) override { THRIFT_UNUSED_VARIABLE (argument); cout << "[C -> C++] testInsanity()" << endl; @@ -277,7 +277,7 @@ class TestHandler : public ThriftTestIf { } - void testMulti(Xtruct &hello, const int8_t arg0, const int32_t arg1, const int64_t arg2, const std::map &arg3, const Numberz::type arg4, const UserId arg5) { + void testMulti(Xtruct &hello, const int8_t arg0, const int32_t arg1, const int64_t arg2, const std::map &arg3, const Numberz::type arg4, const UserId arg5) override { THRIFT_UNUSED_VARIABLE (arg3); THRIFT_UNUSED_VARIABLE (arg4); THRIFT_UNUSED_VARIABLE (arg5); @@ -291,7 +291,7 @@ class TestHandler : public ThriftTestIf { } void testException(const std::string &arg) - throw(Xception, apache::thrift::TException) + throw(Xception, apache::thrift::TException) override { cout << "[C -> C++] testException(" << arg << ")" << endl; if (arg.compare("Xception") == 0) { @@ -309,7 +309,7 @@ class TestHandler : public ThriftTestIf { } } - void testMultiException(Xtruct &result, const std::string &arg0, const std::string &arg1) throw(Xception, Xception2) { + void testMultiException(Xtruct &result, const std::string &arg0, const std::string &arg1) throw(Xception, Xception2) override { cout << "[C -> C++] testMultiException(" << arg0 << ", " << arg1 << ")" << endl; @@ -329,7 +329,7 @@ class TestHandler : public ThriftTestIf { } } - void testOneway(int sleepFor) { + void testOneway(int sleepFor) override { cout << "testOneway(" << sleepFor << "): Sleeping..." << endl; sleep(sleepFor); cout << "testOneway(" << sleepFor << "): done sleeping!" << endl; @@ -350,12 +350,12 @@ extern "C" { static void test_thrift_client (void) { - ThriftSocket *tsocket = NULL; - ThriftBinaryProtocol *protocol = NULL; - TTestThriftTestClient *client = NULL; - TTestThriftTestIf *iface = NULL; - GError *error = NULL; - gchar *string = NULL; + ThriftSocket *tsocket = nullptr; + ThriftBinaryProtocol *protocol = nullptr; + TTestThriftTestClient *client = nullptr; + TTestThriftTestIf *iface = nullptr; + GError *error = nullptr; + gchar *string = nullptr; gint8 byte = 0; gint16 i16 = 0; gint32 i32 = 0, another_i32 = 56789; @@ -363,18 +363,18 @@ test_thrift_client (void) double dbl = 0.0; TTestXtruct *xtruct_in, *xtruct_out; TTestXtruct2 *xtruct2_in, *xtruct2_out; - GHashTable *map_in = NULL, *map_out = NULL; - GHashTable *set_in = NULL, *set_out = NULL; - GArray *list_in = NULL, *list_out = NULL; + GHashTable *map_in = nullptr, *map_out = nullptr; + GHashTable *set_in = nullptr, *set_out = nullptr; + GArray *list_in = nullptr, *list_out = nullptr; TTestNumberz enum_in, enum_out; TTestUserId user_id_in, user_id_out; - GHashTable *insanity_in = NULL; + GHashTable *insanity_in = nullptr; TTestXtruct *xtruct1, *xtruct2; - TTestInsanity *insanity_out = NULL; - TTestXtruct *multi_in = NULL; - GHashTable *multi_map_out = NULL; - TTestXception *xception = NULL; - TTestXception2 *xception2 = NULL; + TTestInsanity *insanity_out = nullptr; + TTestXtruct *multi_in = nullptr; + GHashTable *multi_map_out = nullptr; + TTestXception *xception = nullptr; + TTestXception2 *xception2 = nullptr; #if (!GLIB_CHECK_VERSION (2, 36, 0)) // initialize gobject @@ -384,41 +384,41 @@ test_thrift_client (void) // create a C client tsocket = (ThriftSocket *) g_object_new (THRIFT_TYPE_SOCKET, "hostname", "localhost", - "port", TEST_PORT, NULL); + "port", TEST_PORT, nullptr); protocol = (ThriftBinaryProtocol *) g_object_new (THRIFT_TYPE_BINARY_PROTOCOL, "transport", - tsocket, NULL); - client = (TTestThriftTestClient *) g_object_new (T_TEST_TYPE_THRIFT_TEST_CLIENT, "input_protocol", protocol, "output_protocol", protocol, NULL); + tsocket, nullptr); + client = (TTestThriftTestClient *) g_object_new (T_TEST_TYPE_THRIFT_TEST_CLIENT, "input_protocol", protocol, "output_protocol", protocol, nullptr); iface = T_TEST_THRIFT_TEST_IF (client); // open and send - thrift_transport_open (THRIFT_TRANSPORT(tsocket), NULL); + thrift_transport_open (THRIFT_TRANSPORT(tsocket), nullptr); assert (t_test_thrift_test_client_test_void (iface, &error) == TRUE); - assert (error == NULL); + assert (error == nullptr); assert (t_test_thrift_test_client_test_string (iface, &string, "test123", &error) == TRUE); assert (strcmp (string, "test123") == 0); g_free (string); - assert (error == NULL); + assert (error == nullptr); assert (t_test_thrift_test_client_test_byte (iface, &byte, (gint8) 5, &error) == TRUE); assert (byte == 5); - assert (error == NULL); + assert (error == nullptr); assert (t_test_thrift_test_client_test_i32 (iface, &i32, 123, &error) == TRUE); assert (i32 == 123); - assert (error == NULL); + assert (error == nullptr); assert (t_test_thrift_test_client_test_i64 (iface, &i64, 12345, &error) == TRUE); assert (i64 == 12345); - assert (error == NULL); + assert (error == nullptr); assert (t_test_thrift_test_client_test_double (iface, &dbl, 5.6, &error) == TRUE); assert (dbl == 5.6); - assert (error == NULL); + assert (error == nullptr); - xtruct_out = (TTestXtruct *) g_object_new (T_TEST_TYPE_XTRUCT, NULL); + xtruct_out = (TTestXtruct *) g_object_new (T_TEST_TYPE_XTRUCT, nullptr); xtruct_out->byte_thing = 1; xtruct_out->__isset_byte_thing = TRUE; xtruct_out->i32_thing = 15; @@ -427,50 +427,50 @@ test_thrift_client (void) xtruct_out->__isset_i64_thing = TRUE; xtruct_out->string_thing = g_strdup ("abc123"); xtruct_out->__isset_string_thing = TRUE; - xtruct_in = (TTestXtruct *) g_object_new(T_TEST_TYPE_XTRUCT, NULL); + xtruct_in = (TTestXtruct *) g_object_new(T_TEST_TYPE_XTRUCT, nullptr); assert (t_test_thrift_test_client_test_struct (iface, &xtruct_in, xtruct_out, &error) == TRUE); - assert (error == NULL); + assert (error == nullptr); - xtruct2_out = (TTestXtruct2 *) g_object_new (T_TEST_TYPE_XTRUCT2, NULL); + xtruct2_out = (TTestXtruct2 *) g_object_new (T_TEST_TYPE_XTRUCT2, nullptr); xtruct2_out->byte_thing = 1; xtruct2_out->__isset_byte_thing = TRUE; - if (xtruct2_out->struct_thing != NULL) + if (xtruct2_out->struct_thing != nullptr) g_object_unref(xtruct2_out->struct_thing); xtruct2_out->struct_thing = xtruct_out; xtruct2_out->__isset_struct_thing = TRUE; xtruct2_out->i32_thing = 123; xtruct2_out->__isset_i32_thing = TRUE; - xtruct2_in = (TTestXtruct2 *) g_object_new (T_TEST_TYPE_XTRUCT2, NULL); + xtruct2_in = (TTestXtruct2 *) g_object_new (T_TEST_TYPE_XTRUCT2, nullptr); assert (t_test_thrift_test_client_test_nest (iface, &xtruct2_in, xtruct2_out, &error) == TRUE); - assert (error == NULL); + assert (error == nullptr); g_object_unref (xtruct2_out); g_object_unref (xtruct2_in); g_object_unref (xtruct_in); - map_out = g_hash_table_new (NULL, NULL); - map_in = g_hash_table_new (NULL, NULL); g_hash_table_insert (map_out, &i32, &i32); + map_out = g_hash_table_new (nullptr, nullptr); + map_in = g_hash_table_new (nullptr, nullptr); g_hash_table_insert (map_out, &i32, &i32); assert (t_test_thrift_test_client_test_map (iface, &map_in, map_out, &error) == TRUE); - assert (error == NULL); + assert (error == nullptr); g_hash_table_destroy (map_out); g_hash_table_destroy (map_in); - map_out = g_hash_table_new (NULL, NULL); - map_in = g_hash_table_new (NULL, NULL); + map_out = g_hash_table_new (nullptr, nullptr); + map_in = g_hash_table_new (nullptr, nullptr); g_hash_table_insert (map_out, g_strdup ("a"), g_strdup ("123")); g_hash_table_insert (map_out, g_strdup ("a b"), g_strdup ("with spaces ")); g_hash_table_insert (map_out, g_strdup ("same"), g_strdup ("same")); g_hash_table_insert (map_out, g_strdup ("0"), g_strdup ("numeric key")); assert (t_test_thrift_test_client_test_string_map (iface, &map_in, map_out, &error) == TRUE); - assert (error == NULL); + assert (error == nullptr); g_hash_table_destroy (map_out); g_hash_table_destroy (map_in); - set_out = g_hash_table_new (NULL, NULL); - set_in = g_hash_table_new (NULL, NULL); + set_out = g_hash_table_new (nullptr, nullptr); + set_in = g_hash_table_new (nullptr, nullptr); g_hash_table_insert (set_out, &i32, &i32); assert (t_test_thrift_test_client_test_set (iface, &set_in, set_out, &error) == TRUE); - assert (error == NULL); + assert (error == nullptr); g_hash_table_destroy (set_out); g_hash_table_destroy (set_in); @@ -480,31 +480,31 @@ test_thrift_client (void) g_array_append_val (list_out, i32); g_array_append_val (list_out, another_i32); assert (t_test_thrift_test_client_test_list (iface, &list_in, list_out, &error) == TRUE); - assert (error == NULL); + assert (error == nullptr); g_array_free (list_out, TRUE); g_array_free (list_in, TRUE); enum_out = T_TEST_NUMBERZ_ONE; assert (t_test_thrift_test_client_test_enum (iface, &enum_in, enum_out, &error) == TRUE); assert (enum_in == enum_out); - assert (error == NULL); + assert (error == nullptr); user_id_out = 12345; assert (t_test_thrift_test_client_test_typedef (iface, &user_id_in, user_id_out, &error) == TRUE); assert (user_id_in == user_id_out); - assert (error == NULL); + assert (error == nullptr); - map_in = g_hash_table_new (NULL, NULL); + map_in = g_hash_table_new (nullptr, nullptr); assert (t_test_thrift_test_client_test_map_map (iface, &map_in, i32, &error) == TRUE); - assert (error == NULL); + assert (error == nullptr); g_hash_table_destroy (map_in); // insanity - insanity_out = (TTestInsanity *) g_object_new (T_TEST_TYPE_INSANITY, NULL); - insanity_out->userMap = g_hash_table_new (NULL, NULL); + insanity_out = (TTestInsanity *) g_object_new (T_TEST_TYPE_INSANITY, nullptr); + insanity_out->userMap = g_hash_table_new (nullptr, nullptr); g_hash_table_insert (insanity_out->userMap, GINT_TO_POINTER (enum_out), &user_id_out); - xtruct1 = (TTestXtruct *) g_object_new (T_TEST_TYPE_XTRUCT, NULL); + xtruct1 = (TTestXtruct *) g_object_new (T_TEST_TYPE_XTRUCT, nullptr); xtruct1->byte_thing = 1; xtruct1->__isset_byte_thing = TRUE; xtruct1->i32_thing = 15; @@ -513,7 +513,7 @@ test_thrift_client (void) xtruct1->__isset_i64_thing = TRUE; xtruct1->string_thing = g_strdup ("abc123"); xtruct1->__isset_string_thing = TRUE; - xtruct2 = (TTestXtruct *) g_object_new (T_TEST_TYPE_XTRUCT, NULL); + xtruct2 = (TTestXtruct *) g_object_new (T_TEST_TYPE_XTRUCT, nullptr); xtruct2->byte_thing = 1; xtruct2->__isset_byte_thing = TRUE; xtruct2->i32_thing = 15; @@ -523,7 +523,7 @@ test_thrift_client (void) xtruct2->string_thing = g_strdup ("abc123"); xtruct2->__isset_string_thing = TRUE; - insanity_in = g_hash_table_new (NULL, NULL); + insanity_in = g_hash_table_new (nullptr, nullptr); g_ptr_array_add (insanity_out->xtructs, xtruct1); g_ptr_array_add (insanity_out->xtructs, xtruct2); assert (t_test_thrift_test_client_test_insanity (iface, &insanity_in, insanity_out, &error) == TRUE); @@ -531,10 +531,10 @@ test_thrift_client (void) g_hash_table_unref (insanity_in); g_ptr_array_free (insanity_out->xtructs, TRUE); - multi_map_out = g_hash_table_new (NULL, NULL); + multi_map_out = g_hash_table_new (nullptr, nullptr); string = g_strdup ("abc123"); g_hash_table_insert (multi_map_out, &i16, string); - multi_in = (TTestXtruct *) g_object_new (T_TEST_TYPE_XTRUCT, NULL); + multi_in = (TTestXtruct *) g_object_new (T_TEST_TYPE_XTRUCT, nullptr); assert (t_test_thrift_test_client_test_multi (iface, &multi_in, byte, i32, i64, multi_map_out, enum_out, user_id_out, &error) == TRUE); assert (multi_in->i32_thing == i32); assert (multi_in->i64_thing == i64); @@ -545,53 +545,53 @@ test_thrift_client (void) assert (t_test_thrift_test_client_test_exception (iface, "Xception", &xception, &error) == FALSE); assert (xception->errorCode == 1001); g_error_free (error); - error = NULL; + error = nullptr; g_object_unref (xception); - xception = NULL; + xception = nullptr; assert (t_test_thrift_test_client_test_exception (iface, "ApplicationException", &xception, &error) == FALSE); g_error_free (error); - error = NULL; - assert (xception == NULL); + error = nullptr; + assert (xception == nullptr); assert (t_test_thrift_test_client_test_exception (iface, "Test", &xception, &error) == TRUE); - assert (error == NULL); + assert (error == nullptr); - multi_in = (TTestXtruct*) g_object_new (T_TEST_TYPE_XTRUCT, NULL); - assert (t_test_thrift_test_client_test_multi_exception (iface, &multi_in, "Xception", NULL, &xception, &xception2, &error) == FALSE); + multi_in = (TTestXtruct*) g_object_new (T_TEST_TYPE_XTRUCT, nullptr); + assert (t_test_thrift_test_client_test_multi_exception (iface, &multi_in, "Xception", nullptr, &xception, &xception2, &error) == FALSE); assert (xception->errorCode == 1001); - assert (xception2 == NULL); + assert (xception2 == nullptr); g_error_free (error); - error = NULL; + error = nullptr; g_object_unref (xception); g_object_unref (multi_in); - xception = NULL; - multi_in = NULL; + xception = nullptr; + multi_in = nullptr; - multi_in = (TTestXtruct*) g_object_new (T_TEST_TYPE_XTRUCT, NULL); - assert (t_test_thrift_test_client_test_multi_exception (iface, &multi_in, "Xception2", NULL, &xception, &xception2, &error) == FALSE); + multi_in = (TTestXtruct*) g_object_new (T_TEST_TYPE_XTRUCT, nullptr); + assert (t_test_thrift_test_client_test_multi_exception (iface, &multi_in, "Xception2", nullptr, &xception, &xception2, &error) == FALSE); assert (xception2->errorCode == 2002); - assert (xception == NULL); + assert (xception == nullptr); g_error_free (error); - error = NULL; + error = nullptr; g_object_unref (xception2); g_object_unref (multi_in); - xception2 = NULL; - multi_in = NULL; + xception2 = nullptr; + multi_in = nullptr; - multi_in = (TTestXtruct*) g_object_new (T_TEST_TYPE_XTRUCT, NULL); - assert (t_test_thrift_test_client_test_multi_exception (iface, &multi_in, NULL , NULL, &xception, &xception2, &error) == TRUE); - assert (error == NULL); + multi_in = (TTestXtruct*) g_object_new (T_TEST_TYPE_XTRUCT, nullptr); + assert (t_test_thrift_test_client_test_multi_exception (iface, &multi_in, nullptr , nullptr, &xception, &xception2, &error) == TRUE); + assert (error == nullptr); g_object_unref(multi_in); - multi_in = NULL; + multi_in = nullptr; assert (t_test_thrift_test_client_test_oneway (iface, 1, &error) == TRUE); - assert (error == NULL); + assert (error == nullptr); /* sleep to let the oneway call go through */ sleep (5); - thrift_transport_close (THRIFT_TRANSPORT(tsocket), NULL); + thrift_transport_close (THRIFT_TRANSPORT(tsocket), nullptr); g_object_unref (client); g_object_unref (protocol); g_object_unref (tsocket); @@ -618,11 +618,11 @@ main (void) if (pid == 0) /* child */ { - stdcxx::shared_ptr protocolFactory(new TBinaryProtocolFactory()); - stdcxx::shared_ptr testHandler(new TestHandler()); - stdcxx::shared_ptr testProcessor(new ThriftTestProcessor(testHandler)); - stdcxx::shared_ptr serverSocket(new TServerSocket(TEST_PORT)); - stdcxx::shared_ptr transportFactory(new TBufferedTransportFactory()); + std::shared_ptr protocolFactory(new TBinaryProtocolFactory()); + std::shared_ptr testHandler(new TestHandler()); + std::shared_ptr testProcessor(new ThriftTestProcessor(testHandler)); + std::shared_ptr serverSocket(new TServerSocket(TEST_PORT)); + std::shared_ptr transportFactory(new TBufferedTransportFactory()); TSimpleServer simpleServer(testProcessor, serverSocket, transportFactory, protocolFactory); signal (SIGALRM, bailout); alarm (60); diff --git a/lib/c_glib/test/testtransportsocket.c b/lib/c_glib/test/testtransportsocket.c index fedbad6b69a..89c61b91019 100755 --- a/lib/c_glib/test/testtransportsocket.c +++ b/lib/c_glib/test/testtransportsocket.c @@ -291,11 +291,8 @@ test_peek(void) static void thrift_socket_server_open (const int port, int times) { - int bytes = 0; ThriftServerTransport *transport = NULL; ThriftTransport *client = NULL; - guchar buf[10]; /* a buffer */ - guchar match[10] = TEST_DATA; int i; ThriftServerSocket *tsocket = g_object_new (THRIFT_TYPE_SERVER_SOCKET, "port", port, NULL); diff --git a/lib/c_glib/test/testtransportsslsocket.c b/lib/c_glib/test/testtransportsslsocket.c index 3c2644d8d34..ba9ffdcaedf 100644 --- a/lib/c_glib/test/testtransportsslsocket.c +++ b/lib/c_glib/test/testtransportsslsocket.c @@ -103,7 +103,7 @@ test_ssl_create_and_set_properties(void) GError *error=NULL; GObject *object = NULL; - object = thrift_ssl_socket_new(SSLTLS, &error); + object = (GObject *)thrift_ssl_socket_new(SSLTLS, &error); g_object_get (G_OBJECT(object), "hostname", &hostname, "port", &port, "ssl_context", &ssl_ctx, NULL); g_assert (ssl_ctx!=NULL); @@ -180,7 +180,6 @@ test_ssl_write_invalid_socket(void) ThriftSSLSocket *tSSLSocket = NULL; ThriftTransport *transport = NULL; GError *error=NULL; - char buffer[] = "this must not break"; /* open a connection and close it */ tSSLSocket = thrift_ssl_socket_new_with_host(SSLTLS, "localhost", 51188+1, &error); @@ -267,13 +266,13 @@ int verify_ip(char * hostname, struct sockaddr_storage *addr) /* loop through all the results and connect to the first we can */ char dnshost[INET6_ADDRSTRLEN]; /* bigger addr supported IPV6 */ char socket_ip[INET6_ADDRSTRLEN]; - if(inet_ntop(addr->ss_family, get_in_addr(addr), socket_ip, INET6_ADDRSTRLEN)==socket_ip){ + if(inet_ntop(addr->ss_family, get_in_addr((struct sockaddr*)addr), socket_ip, INET6_ADDRSTRLEN)==socket_ip){ g_debug("We are connected to host %s checking against certificate...", socket_ip); int sizeip = socket_ip!=NULL ? strlen(socket_ip) : 0; for(p = addr_info; p != NULL; p = p->ai_next) { if(inet_ntop(p->ai_family, get_in_addr((struct sockaddr *)p->ai_addr), dnshost, INET6_ADDRSTRLEN)==dnshost){ if(dnshost!=NULL){ - g_info("DNS address [%i -> %s]", p->ai_addr, dnshost); + g_info("DNS address [%i -> %s]", ((guint32)(p->ai_addrlen)), dnshost); if(!strncmp(dnshost, socket_ip, sizeip)){ retval=1; break; /* if we get here, we must have connected successfully */ @@ -289,7 +288,7 @@ int verify_ip(char * hostname, struct sockaddr_storage *addr) return retval; } -static void +static void read_from_file(char *buffer, long size, const char *file_name) { char ch; @@ -330,7 +329,7 @@ gboolean verify_certificate_sn(X509 *cert, const unsigned char *serial_number) } char *tmp = BN_bn2dec(bn); if (!tmp) { - g_warning(stderr, "unable to convert BN to decimal string.\n"); + g_warning((const char*)stderr, "unable to convert BN to decimal string.\n"); BN_free(bn); return EXIT_FAILURE; } @@ -342,7 +341,7 @@ gboolean verify_certificate_sn(X509 *cert, const unsigned char *serial_number) return EXIT_FAILURE; } */ - if(!strncmp(serial_number, tmp, strlen(serial_number))){ + if(!strncmp((const char*)serial_number, tmp, strlen((const char*)serial_number))){ retval=TRUE; }else{ g_warning("Serial number is not valid"); @@ -356,6 +355,8 @@ gboolean verify_certificate_sn(X509 *cert, const unsigned char *serial_number) gboolean my_access_manager(ThriftTransport * transport, X509 *cert, struct sockaddr_storage *addr, GError **error) { ThriftSSLSocket *sslSocket = THRIFT_SSL_SOCKET (transport); + THRIFT_UNUSED_VAR (error); + THRIFT_UNUSED_VAR (sslSocket); g_info("Processing access to the server"); X509_NAME* iname = cert ? X509_get_issuer_name(cert) : NULL; @@ -368,11 +369,11 @@ gboolean my_access_manager(ThriftTransport * transport, X509 *cert, struct socka g_info("Issuer (cn) %s", issuer); /* Issuer pinning */ - if(strncmp(ISSUER_CN_PINNING, issuer, strlen(ISSUER_CN_PINNING))){ + if(strncmp(ISSUER_CN_PINNING, (const char*)issuer, strlen(ISSUER_CN_PINNING))){ g_warning("The Issuer of the certificate is not valid"); valid=FALSE; } - OPENSSL_free(issuer); + OPENSSL_free((void*)issuer); if(!valid) return valid; } @@ -385,7 +386,7 @@ gboolean my_access_manager(ThriftTransport * transport, X509 *cert, struct socka gboolean valid = TRUE; /* Subject pinning */ - if(strncmp(SUBJECT_CN_PINNING, subject, strlen(SUBJECT_CN_PINNING))){ + if(strncmp(SUBJECT_CN_PINNING, (const char*)subject, strlen(SUBJECT_CN_PINNING))){ g_warning("The subject of the certificate is not valid"); valid=FALSE; } @@ -394,19 +395,19 @@ gboolean my_access_manager(ThriftTransport * transport, X509 *cert, struct socka return valid; /* Host pinning */ - if(verify_ip(subject, addr)){ + if(verify_ip((char*)subject, addr)){ g_info("Verified subject"); }else{ g_info("Cannot verify subject"); valid=FALSE; } - OPENSSL_free(subject); + OPENSSL_free((void*)subject); if(!valid) return valid; } - if(!verify_certificate_sn(cert, CERT_SERIAL_NUMBER)){ + if(!verify_certificate_sn(cert, (const unsigned char*)CERT_SERIAL_NUMBER)){ return FALSE; }else{ g_info("Verified serial number"); @@ -487,7 +488,7 @@ thrift_socket_server (const int port) ThriftServerTransport *transport = NULL; ThriftTransport *client = NULL; guchar buf[10]; /* a buffer */ - guchar match[10] = TEST_DATA; + guchar match[] = TEST_DATA; ThriftServerSocket *tsocket = g_object_new (THRIFT_TYPE_SERVER_SOCKET, "port", port, NULL); diff --git a/lib/cocoa/README.md b/lib/cocoa/README.md deleted file mode 100644 index bbe3c934fba..00000000000 --- a/lib/cocoa/README.md +++ /dev/null @@ -1,21 +0,0 @@ -Thrift Cocoa 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. diff --git a/lib/cocoa/coding_standards.md b/lib/cocoa/coding_standards.md deleted file mode 100644 index fa0390bb577..00000000000 --- a/lib/cocoa/coding_standards.md +++ /dev/null @@ -1 +0,0 @@ -Please follow [General Coding Standards](/doc/coding_standards.md) diff --git a/lib/cocoa/src/TApplicationError.h b/lib/cocoa/src/TApplicationError.h deleted file mode 100644 index 079881a60a9..00000000000 --- a/lib/cocoa/src/TApplicationError.h +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -#import "TProtocol.h" - -extern NSString *TApplicationErrorDomain; - -typedef NS_ENUM (int, TApplicationError) { - TApplicationErrorUnknown = 0, - TApplicationErrorUnknownMethod = 1, - TApplicationErrorInvalidMessageType = 2, - TApplicationErrorWrongMethodName = 3, - TApplicationErrorBadSequenceId = 4, - TApplicationErrorMissingResult = 5, - TApplicationErrorInternalError = 6, - TApplicationErrorProtocolError = 7, - TApplicationErrorInvalidTransform = 8, - TApplicationErrorInvalidProtocol = 9, - TApplicationErrorUnsupportedClientType = 10, -}; - - -extern NSString *TApplicationErrorNameKey; -extern NSString *TApplicationErrorReasonKey; -extern NSString *TApplicationErrorMethodKey; - - -@interface NSError (TApplicationError) - -@property (readonly, copy) NSString *name; -@property (readonly, copy) NSString *reason; - -+(instancetype) errorWithType:(TApplicationError)type reason:(NSString *)reason; - -+(instancetype) read:(id)protocol; - --(BOOL) write:(id)outProtocol error:(NSError *__autoreleasing *)error; - -@end diff --git a/lib/cocoa/src/TApplicationError.m b/lib/cocoa/src/TApplicationError.m deleted file mode 100644 index 080bc0b7c70..00000000000 --- a/lib/cocoa/src/TApplicationError.m +++ /dev/null @@ -1,231 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -#import "TApplicationError.h" -#import "TProtocolUtil.h" - - -NSString *TApplicationErrorDomain = @"TApplicationErrorDomain"; - - -NSString *TApplicationErrorNameKey = @"name"; -NSString *TApplicationErrorReasonKey = @"reason"; -NSString *TApplicationErrorMethodKey = @"method"; - - -@implementation NSError (TApplicationError) - --(NSString *) reason -{ - return self.userInfo[TApplicationErrorReasonKey]; -} - --(NSString *) name -{ - return self.userInfo[TApplicationErrorNameKey]; -} - -+(instancetype) errorWithType:(TApplicationError)type reason:(NSString *)reason -{ - NSString *name; - switch (type) { - case TApplicationErrorUnknownMethod: - name = @"Unknown Method"; - break; - - case TApplicationErrorInvalidMessageType: - name = @"Invalid Message Type"; - break; - - case TApplicationErrorWrongMethodName: - name = @"Wrong Method Name"; - break; - - case TApplicationErrorBadSequenceId: - name = @"Bad Sequence ID"; - break; - - case TApplicationErrorMissingResult: - name = @"Missing Result"; - break; - - case TApplicationErrorInternalError: - name = @"Internal Error"; - break; - - case TApplicationErrorProtocolError: - name = @"Protocol Error"; - break; - - case TApplicationErrorInvalidTransform: - name = @"Invalid Transform"; - break; - - case TApplicationErrorInvalidProtocol: - name = @"Invalid Protocol"; - break; - - case TApplicationErrorUnsupportedClientType: - name = @"Unsupported Client Type"; - break; - - default: - name = @"Unknown"; - break; - } - - NSDictionary *userInfo; - if (reason) { - userInfo = @{TApplicationErrorNameKey:name, - TApplicationErrorReasonKey:reason}; - } - else { - userInfo = @{TApplicationErrorNameKey:name}; - } - - return [NSError errorWithDomain:TApplicationErrorDomain - code:type - userInfo:userInfo]; -} - - -+(instancetype) read:(id)protocol -{ - NSString *reason = nil; - SInt32 type = TApplicationErrorUnknown; - SInt32 fieldType; - SInt32 fieldID; - - NSError *error; - if (![protocol readStructBeginReturningName:NULL error:&error]) { - return error; - } - - while (true) { - - if (![protocol readFieldBeginReturningName:NULL - type:&fieldType - fieldID:&fieldID - error:&error]) - { - return error; - } - - if (fieldType == TTypeSTOP) { - break; - } - - switch (fieldID) { - case 1: - if (fieldType == TTypeSTRING) { - if (![protocol readString:&reason error:&error]) { - return error; - } - } - else { - if (![TProtocolUtil skipType:fieldType onProtocol:protocol error:&error]) { - return error; - } - } - break; - - case 2: - if (fieldType == TTypeI32) { - if (![protocol readI32:&type error:&error]) { - return error; - } - } - else { - if (![TProtocolUtil skipType:fieldType onProtocol:protocol error:&error]) { - return error; - } - } - break; - - default: - if (![TProtocolUtil skipType:fieldType onProtocol:protocol error:&error]) { - return error; - } - break; - } - if (![protocol readFieldEnd:&error]) { - return error; - } - - } - - if (![protocol readStructEnd:&error]) { - return error; - } - - return [NSError errorWithType:type reason:reason]; -} - - --(BOOL) write:(id)protocol error:(NSError *__autoreleasing *)error -{ - if (![protocol writeStructBeginWithName:@"TApplicationException" error:error]) { - return NO; - } - - if (self.localizedDescription != nil) { - if (![protocol writeFieldBeginWithName:@"message" - type:TTypeSTRING - fieldID:1 error:error]) - { - return NO; - } - - if (![protocol writeString:self.localizedDescription error:error]) { - return NO; - } - - if (![protocol writeFieldEnd:error]) { - return NO; - } - } - - if (![protocol writeFieldBeginWithName:@"type" - type:TTypeI32 - fieldID:2 - error:error]) - { - return NO; - } - - if (![protocol writeI32:(SInt32)self.code error:error]) { - return NO; - } - - if (![protocol writeFieldEnd:error]) { - return NO; - } - - if (![protocol writeFieldStop:error]) { - return NO; - } - - if (![protocol writeStructEnd:error]) { - return NO; - } - - return YES; -} - -@end diff --git a/lib/cocoa/src/TBinary.swift b/lib/cocoa/src/TBinary.swift deleted file mode 100644 index c8a366075a7..00000000000 --- a/lib/cocoa/src/TBinary.swift +++ /dev/null @@ -1,122 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import Foundation - - -public struct TBinary : TSerializable { - - public static var thriftType : TType { return .STRING } - - private var storage : NSData - - public init() { - self.storage = NSData() - } - - public init(contentsOfFile file: String, options: NSDataReadingOptions = []) throws { - self.storage = try NSData(contentsOfFile: file, options: options) - } - - public init(contentsOfURL URL: NSURL, options: NSDataReadingOptions = []) throws { - self.storage = try NSData(contentsOfURL: URL, options: options) - } - - public init?(base64EncodedData base64Data: NSData, options: NSDataBase64DecodingOptions = []) { - guard let storage = NSData(base64EncodedData: base64Data, options: options) else { - return nil - } - self.storage = storage - } - - public init(data: NSData) { - self.storage = data - } - - public var length : Int { - return storage.length - } - - public var hashValue : Int { - return storage.hashValue - } - - public var bytes : UnsafePointer { - return storage.bytes - } - - public func getBytes(buffer: UnsafeMutablePointer, length: Int) { - storage.getBytes(buffer, length: length) - } - - public func getBytes(buffer: UnsafeMutablePointer, range: Range) { - storage.getBytes(buffer, range: NSRange(range)) - } - - public func subBinaryWithRange(range: Range) -> TBinary { - return TBinary(data: storage.subdataWithRange(NSRange(range))) - } - - public func writeToFile(path: String, options: NSDataWritingOptions = []) throws { - try storage.writeToFile(path, options: options) - } - - public func writeToURL(url: NSURL, options: NSDataWritingOptions = []) throws { - try storage.writeToURL(url, options: options) - } - - public func rangeOfData(dataToFind data: NSData, options: NSDataSearchOptions, range: Range) -> Range? { - return storage.rangeOfData(data, options: options, range: NSRange(range)).toRange() - } - - public func enumerateByteRangesUsingBlock(block: (UnsafePointer, Range, inout Bool) -> Void) { - storage.enumerateByteRangesUsingBlock { bytes, range, stop in - var stopTmp = Bool(stop.memory) - block(bytes, range.toRange()!, &stopTmp) - stop.memory = ObjCBool(stopTmp) - } - } - - public static func readValueFromProtocol(proto: TProtocol) throws -> TBinary { - var data : NSData? - try proto.readBinary(&data) - return TBinary(data: data!) - } - - public static func writeValue(value: TBinary, toProtocol proto: TProtocol) throws { - try proto.writeBinary(value.storage) - } - -} - -extension TBinary : CustomStringConvertible, CustomDebugStringConvertible { - - public var description : String { - return storage.description - } - - public var debugDescription : String { - return storage.debugDescription - } - -} - -public func ==(lhs: TBinary, rhs: TBinary) -> Bool { - return lhs.storage == rhs.storage -} diff --git a/lib/cocoa/src/TList.swift b/lib/cocoa/src/TList.swift deleted file mode 100644 index 005bd817d03..00000000000 --- a/lib/cocoa/src/TList.swift +++ /dev/null @@ -1,148 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import Foundation - - - -public struct TList : MutableCollectionType, Hashable, ArrayLiteralConvertible, TSerializable { - - public static var thriftType : TType { return .LIST } - - typealias Storage = Array - - public typealias Index = Storage.Index - - private var storage = Storage() - - public var startIndex : Index { - return storage.startIndex - } - - public var endIndex : Index { - return storage.endIndex - } - - public subscript (position: Index) -> Element { - get { - return storage[position] - } - set { - storage[position] = newValue - } - } - - public var hashValue : Int { - let prime = 31 - var result = 1 - for element in storage { - result = prime * result + element.hashValue - } - return result - } - - public init(arrayLiteral elements: Element...) { - self.storage = Storage(storage) - } - - public init() { - self.storage = Storage() - } - - public mutating func append(newElement: Element) { - self.storage.append(newElement) - } - - public mutating func appendContentsOf(newstorage: C) { - self.storage.appendContentsOf(newstorage) - } - - public mutating func insert(newElement: Element, atIndex index: Int) { - self.storage.insert(newElement, atIndex: index) - } - - public mutating func insertContentsOf(newElements: C, at index: Int) { - self.storage.insertContentsOf(newElements, at: index) - } - - public mutating func removeAll(keepCapacity keepCapacity: Bool = true) { - self.storage.removeAll(keepCapacity: keepCapacity) - } - - public mutating func removeAtIndex(index: Index) { - self.storage.removeAtIndex(index) - } - - public mutating func removeFirst(n: Int = 0) { - self.storage.removeFirst(n) - } - - public mutating func removeLast() -> Element { - return self.storage.removeLast() - } - - public mutating func removeRange(subRange: Range) { - self.storage.removeRange(subRange) - } - - public mutating func reserveCapacity(minimumCapacity: Int) { - self.storage.reserveCapacity(minimumCapacity) - } - - public static func readValueFromProtocol(proto: TProtocol) throws -> TList { - let (elementType, size) = try proto.readListBegin() - if elementType != Element.thriftType { - throw NSError( - domain: TProtocolErrorDomain, - code: Int(TProtocolError.InvalidData.rawValue), - userInfo: [TProtocolErrorExtendedErrorKey: NSNumber(int: TProtocolExtendedError.UnexpectedType.rawValue)]) - } - var list = TList() - for _ in 0..(lhs: TList, rhs: TList) -> Bool { - return lhs.storage == rhs.storage -} diff --git a/lib/cocoa/src/TMap.swift b/lib/cocoa/src/TMap.swift deleted file mode 100644 index e96e7474e3a..00000000000 --- a/lib/cocoa/src/TMap.swift +++ /dev/null @@ -1,158 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import Foundation - - -public struct TMap : CollectionType, DictionaryLiteralConvertible, TSerializable { - - public static var thriftType : TType { return .MAP } - - typealias Storage = Dictionary - - public typealias Index = Storage.Index - - public typealias Element = Storage.Element - - private var storage : Storage - - public var startIndex : Index { - return storage.startIndex - } - - public var endIndex: Index { - return storage.endIndex - } - - public var keys: LazyMapCollection<[Key : Value], Key> { - return storage.keys - } - - public var values: LazyMapCollection<[Key : Value], Value> { - return storage.values - } - - public init() { - storage = Storage() - } - - public init(dictionaryLiteral elements: (Key, Value)...) { - storage = Storage() - for (key, value) in elements { - storage[key] = value - } - } - - public init(minimumCapacity: Int) { - storage = Storage(minimumCapacity: minimumCapacity) - } - - public subscript (position: Index) -> Element { - get { - return storage[position] - } - } - - public func indexForKey(key: Key) -> Index? { - return storage.indexForKey(key) - } - - public subscript (key: Key) -> Value? { - get { - return storage[key] - } - set { - storage[key] = newValue - } - } - - public mutating func updateValue(value: Value, forKey key: Key) -> Value? { - return updateValue(value, forKey: key) - } - - public mutating func removeAtIndex(index: DictionaryIndex) -> (Key, Value) { - return removeAtIndex(index) - } - - public mutating func removeValueForKey(key: Key) -> Value? { - return storage.removeValueForKey(key) - } - - public mutating func removeAll(keepCapacity keepCapacity: Bool = false) { - storage.removeAll(keepCapacity: keepCapacity) - } - - public var hashValue : Int { - let prime = 31 - var result = 1 - for (key, value) in storage { - result = prime * result + key.hashValue - result = prime * result + value.hashValue - } - return result - } - - public static func readValueFromProtocol(proto: TProtocol) throws -> TMap { - let (keyType, valueType, size) = try proto.readMapBegin() - if keyType != Key.thriftType || valueType != Value.thriftType { - throw NSError( - domain: TProtocolErrorDomain, - code: Int(TProtocolError.InvalidData.rawValue), - userInfo: [TProtocolErrorExtendedErrorKey: NSNumber(int: TProtocolExtendedError.UnexpectedType.rawValue)]) - } - var map = TMap() - for _ in 0..(lhs: TMap, rhs: TMap) -> Bool { - if lhs.count != rhs.count { - return false - } - return lhs.storage == rhs.storage -} diff --git a/lib/cocoa/src/TProtocol.swift b/lib/cocoa/src/TProtocol.swift deleted file mode 100644 index 1775849790a..00000000000 --- a/lib/cocoa/src/TProtocol.swift +++ /dev/null @@ -1,190 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import Foundation - - -public extension TProtocol { - - public func readMessageBegin() throws -> (String, TMessageType, Int) { - - var name : NSString? - var type : Int32 = -1 - var sequenceID : Int32 = -1 - - try readMessageBeginReturningName(&name, type: &type, sequenceID: &sequenceID) - - return (name as String!, TMessageType(rawValue: type)!, Int(sequenceID)) - } - - public func writeMessageBeginWithName(name: String, type: TMessageType, sequenceID: Int) throws { - try writeMessageBeginWithName(name, type: type.rawValue, sequenceID: Int32(sequenceID)) - } - - public func readStructBegin() throws -> (String?) { - - var name : NSString? = nil - - try readStructBeginReturningName(&name) - - return (name as String?) - } - - public func readFieldBegin() throws -> (String?, TType, Int) { - - var name : NSString? = nil - var type : Int32 = -1 - var fieldID : Int32 = -1 - - try readFieldBeginReturningName(&name, type: &type, fieldID: &fieldID) - - return (name as String?, TType(rawValue: type)!, Int(fieldID)) - } - - public func writeFieldBeginWithName(name: String, type: TType, fieldID: Int) throws { - try writeFieldBeginWithName(name, type: type.rawValue, fieldID: Int32(fieldID)) - } - - public func readMapBegin() throws -> (TType, TType, Int32) { - - var keyType : Int32 = -1 - var valueType : Int32 = -1 - var size : Int32 = 0 - - try readMapBeginReturningKeyType(&keyType, valueType: &valueType, size: &size) - - return (TType(rawValue: keyType)!, TType(rawValue: valueType)!, size) - } - - public func writeMapBeginWithKeyType(keyType: TType, valueType: TType, size: Int) throws { - try writeMapBeginWithKeyType(keyType.rawValue, valueType: valueType.rawValue, size: Int32(size)) - } - - public func readSetBegin() throws -> (TType, Int32) { - - var elementType : Int32 = -1 - var size : Int32 = 0 - - try readSetBeginReturningElementType(&elementType, size: &size) - - return (TType(rawValue: elementType)!, size) - } - - public func writeSetBeginWithElementType(elementType: TType, size: Int) throws { - try writeSetBeginWithElementType(elementType.rawValue, size: Int32(size)) - } - - public func readListBegin() throws -> (TType, Int32) { - - var elementType : Int32 = -1 - var size : Int32 = 0 - - try readListBeginReturningElementType(&elementType, size: &size) - - return (TType(rawValue: elementType)!, size) - } - - public func writeListBeginWithElementType(elementType: TType, size: Int) throws { - try writeListBeginWithElementType(elementType.rawValue, size: Int32(size)) - } - - public func writeFieldValue(value: T, name: String, type: TType, id: Int32) throws { - try writeFieldBeginWithName(name, type: type.rawValue, fieldID: id) - try writeValue(value) - try writeFieldEnd() - } - - public func readValue() throws -> T { - return try T.readValueFromProtocol(self) - } - - public func writeValue(value: T) throws { - try T.writeValue(value, toProtocol: self) - } - - public func readResultMessageBegin() throws { - - let (_, type, _) = try readMessageBegin(); - - if type == .EXCEPTION { - let x = try readException() - throw x - } - - return - } - - public func validateValue(value: Any?, named name: String) throws { - - if value == nil { - throw NSError( - domain: TProtocolErrorDomain, - code: Int(TProtocolError.Unknown.rawValue), - userInfo: [TProtocolErrorFieldNameKey: name]) - } - - } - - public func readException() throws -> ErrorType { - - var reason : String? - var type = TApplicationError.Unknown - - try readStructBegin() - - fields: while (true) { - - let (_, fieldType, fieldID) = try readFieldBegin() - - switch (fieldID, fieldType) { - case (_, .STOP): - break fields - - case (1, .STRING): - reason = try readValue() as String - - case (2, .I32): - let typeVal = try readValue() as Int32 - if let tmp = TApplicationError(rawValue: typeVal) { - type = tmp - } - - case let (_, unknownType): - try skipType(unknownType) - } - - try readFieldEnd() - } - - try readStructEnd() - - return NSError(type:type, reason:reason ?? "") - } - - public func writeExceptionForMessageName(name: String, sequenceID: Int, ex: NSError) throws { - try writeMessageBeginWithName(name, type: .EXCEPTION, sequenceID: sequenceID) - try ex.write(self) - try writeMessageEnd() - } - - public func skipType(type: TType) throws { - try TProtocolUtil.skipType(type.rawValue, onProtocol: self) - } - -} diff --git a/lib/cocoa/src/TSerializable.swift b/lib/cocoa/src/TSerializable.swift deleted file mode 100644 index 3fdfd41b070..00000000000 --- a/lib/cocoa/src/TSerializable.swift +++ /dev/null @@ -1,178 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import Foundation - - -public protocol TSerializable : Hashable { - - static var thriftType : TType { get } - - init() - - static func readValueFromProtocol(proto: TProtocol) throws -> Self - - static func writeValue(value: Self, toProtocol proto: TProtocol) throws - -} - - - -infix operator ?== {} - -public func ?==(lhs: T?, rhs: T?) -> Bool { - if let l = lhs, r = rhs { - return l == r - } - return lhs == rhs -} - -public func ?==(lhs: T, rhs: T) -> Bool { - return lhs == rhs -} - - - -extension Bool : TSerializable { - - public static let thriftType = TType.BOOL - - public static func readValueFromProtocol(proto: TProtocol) throws -> Bool { - var value : ObjCBool = false - try proto.readBool(&value) - return value.boolValue - } - - public static func writeValue(value: Bool, toProtocol proto: TProtocol) throws { - try proto.writeBool(value) - } - -} - -extension Int8 : TSerializable { - - public static let thriftType = TType.BYTE - - public static func readValueFromProtocol(proto: TProtocol) throws -> Int8 { - var value = UInt8() - try proto.readByte(&value) - return Int8(value) - } - - public static func writeValue(value: Int8, toProtocol proto: TProtocol) throws { - try proto.writeByte(UInt8(value)) - } - -} - -extension Int16 : TSerializable { - - public static let thriftType = TType.I16 - - public static func readValueFromProtocol(proto: TProtocol) throws -> Int16 { - var value = Int16() - try proto.readI16(&value) - return value - } - - public static func writeValue(value: Int16, toProtocol proto: TProtocol) throws { - try proto.writeI16(value) - } - -} - -extension Int : TSerializable { - - public static let thriftType = TType.I32 - - public static func readValueFromProtocol(proto: TProtocol) throws -> Int { - var value = Int32() - try proto.readI32(&value) - return Int(value) - } - - public static func writeValue(value: Int, toProtocol proto: TProtocol) throws { - try proto.writeI32(Int32(value)) - } - -} - -extension Int32 : TSerializable { - - public static let thriftType = TType.I32 - - public static func readValueFromProtocol(proto: TProtocol) throws -> Int32 { - var value = Int32() - try proto.readI32(&value) - return value - } - - public static func writeValue(value: Int32, toProtocol proto: TProtocol) throws { - try proto.writeI32(value) - } - -} - -extension Int64 : TSerializable { - - public static let thriftType = TType.I64 - - public static func readValueFromProtocol(proto: TProtocol) throws -> Int64 { - var value = Int64() - try proto.readI64(&value) - return value - } - - public static func writeValue(value: Int64, toProtocol proto: TProtocol) throws { - try proto.writeI64(value) - } - -} - -extension Double : TSerializable { - - public static let thriftType = TType.DOUBLE - - public static func readValueFromProtocol(proto: TProtocol) throws -> Double { - var value = Double() - try proto.readDouble(&value) - return value - } - - public static func writeValue(value: Double, toProtocol proto: TProtocol) throws { - try proto.writeDouble(value) - } - -} - -extension String : TSerializable { - - public static let thriftType = TType.STRING - - public static func readValueFromProtocol(proto: TProtocol) throws -> String { - var value : NSString? - try proto.readString(&value) - return value as! String - } - - public static func writeValue(value: String, toProtocol proto: TProtocol) throws { - try proto.writeString(value) - } - -} diff --git a/lib/cocoa/src/TSet.swift b/lib/cocoa/src/TSet.swift deleted file mode 100644 index 85833e5c078..00000000000 --- a/lib/cocoa/src/TSet.swift +++ /dev/null @@ -1,161 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import Foundation - - -public struct TSet : CollectionType, ArrayLiteralConvertible, TSerializable { - - public static var thriftType : TType { return .SET } - - public typealias Index = Storage.Index - - typealias Storage = Set - - private var storage : Storage - - public init() { - storage = Storage() - } - - public init(arrayLiteral elements: Element...) { - storage = Storage(elements) - } - - public init(_ sequence: S) { - storage = Storage(sequence) - } - - public var startIndex : Index { return storage.startIndex } - - public var endIndex : Index { return storage.endIndex } - - public mutating func insert(member: Element) { - return storage.insert(member) - } - - public mutating func remove(element: Element) -> Element? { - return storage.remove(element) - } - - public mutating func removeAll(keepCapacity keepCapacity: Bool = false) { - return storage.removeAll(keepCapacity: keepCapacity) - } - - public mutating func removeAtIndex(index: SetIndex) -> Element { - return storage.removeAtIndex(index) - } - - public subscript (position: SetIndex) -> Element { - return storage[position] - } - - public func union(other: TSet) -> TSet { - return TSet(storage.union(other)) - } - - public func intersect(other: TSet) -> TSet { - return TSet(storage.intersect(other)) - } - - public func exclusiveOr(other: TSet) -> TSet { - return TSet(storage.exclusiveOr(other)) - } - - public func subtract(other: TSet) -> TSet { - return TSet(storage.subtract(other)) - } - - public mutating func intersectInPlace(other: TSet) { - storage.intersectInPlace(other) - } - - public mutating func exclusiveOrInPlace(other: TSet) { - storage.exclusiveOrInPlace(other) - } - - public mutating func subtractInPlace(other: TSet) { - storage.subtractInPlace(other) - } - - public func isSubsetOf(other: TSet) -> Bool { - return storage.isSubsetOf(other) - } - - public func isDisjointWith(other: TSet) -> Bool { - return storage.isDisjointWith(other) - } - - public func isSupersetOf(other: TSet) -> Bool { - return storage.isSupersetOf(other) - } - - public var isEmpty: Bool { return storage.isEmpty } - - public var hashValue : Int { - let prime = 31 - var result = 1 - for element in storage { - result = prime * result + element.hashValue - } - return result - } - - public static func readValueFromProtocol(proto: TProtocol) throws -> TSet { - let (elementType, size) = try proto.readSetBegin() - if elementType != Element.thriftType { - throw NSError( - domain: TProtocolErrorDomain, - code: Int(TProtocolError.InvalidData.rawValue), - userInfo: [TProtocolErrorExtendedErrorKey: NSNumber(int: elementType.rawValue)]) - } - var set = TSet() - for _ in 0..(lhs: TSet, rhs: TSet) -> Bool { - return lhs.storage == rhs.storage -} diff --git a/lib/cocoa/src/TSharedProcessorFactory.h b/lib/cocoa/src/TSharedProcessorFactory.h deleted file mode 100644 index c75fad1ee47..00000000000 --- a/lib/cocoa/src/TSharedProcessorFactory.h +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -#import -#import "TProcessorFactory.h" - - -@interface TSharedProcessorFactory : NSObject - --(id) initWithSharedProcessor:(id)sharedProcessor; - -@end diff --git a/lib/cocoa/src/protocol/TBinaryProtocol.h b/lib/cocoa/src/protocol/TBinaryProtocol.h deleted file mode 100644 index bb90fadcb1b..00000000000 --- a/lib/cocoa/src/protocol/TBinaryProtocol.h +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -#import "TProtocol.h" -#import "TTransport.h" -#import "TProtocolFactory.h" - -NS_ASSUME_NONNULL_BEGIN - - -@interface TBinaryProtocol : NSObject - -@property (assign, nonatomic) UInt32 messageSizeLimit; - --(id) initWithTransport:(id )transport; - --(id) initWithTransport:(id )transport - strictRead:(BOOL)strictRead - strictWrite:(BOOL)strictWrite; - -@end; - - -@interface TBinaryProtocolFactory : NSObject - -+(TBinaryProtocolFactory *) sharedFactory; - --(TBinaryProtocol *) newProtocolOnTransport:(id )transport; - -@end - - -NS_ASSUME_NONNULL_END \ No newline at end of file diff --git a/lib/cocoa/src/protocol/TBinaryProtocol.m b/lib/cocoa/src/protocol/TBinaryProtocol.m deleted file mode 100644 index 1f9e57ac8be..00000000000 --- a/lib/cocoa/src/protocol/TBinaryProtocol.m +++ /dev/null @@ -1,740 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -#import "TBinaryProtocol.h" -#import "TProtocolError.h" - - -static SInt32 VERSION_1 = 0x80010000; -static SInt32 VERSION_MASK = 0xffff0000; - - -static TBinaryProtocolFactory *gSharedFactory = nil; - - -@implementation TBinaryProtocolFactory - -+(TBinaryProtocolFactory *) sharedFactory -{ - if (gSharedFactory == nil) { - gSharedFactory = [[TBinaryProtocolFactory alloc] init]; - } - - return gSharedFactory; -} - --(NSString *) protocolName -{ - return @"binary"; -} - --(TBinaryProtocol *) newProtocolOnTransport:(id )transport -{ - return [[TBinaryProtocol alloc] initWithTransport:transport]; -} - -@end - - -@interface TBinaryProtocol () - -@property(strong, nonatomic) id transport; - -@property(assign, nonatomic) BOOL strictRead; -@property(assign, nonatomic) BOOL strictWrite; - -@property(strong, nonatomic) NSString *currentMessageName; -@property(strong, nonatomic) NSString *currentFieldName; - -@end - - -@implementation TBinaryProtocol - --(id) initWithTransport:(id )aTransport -{ - return [self initWithTransport:aTransport strictRead:NO strictWrite:YES]; -} - --(id) initWithTransport:(id )transport - strictRead:(BOOL)strictRead - strictWrite:(BOOL)strictWrite -{ - self = [super init]; - if (self) { - _transport = transport; - _strictRead = strictRead; - _strictWrite = strictWrite; - } - return self; -} - --(id ) transport -{ - return _transport; -} - --(NSString *) readStringBody:(int)size error:(NSError **)error -{ - NSMutableData *data = [NSMutableData dataWithLength:size]; - if (!data) { - PROTOCOL_ERROR(nil, Unknown, @"Unable to allocate %d bytes", size); - } - - if (![_transport readAll:data.mutableBytes offset:0 length:size error:error]) { - PROTOCOL_TRANSPORT_ERROR(nil, error, @"Transport read failed"); - } - - return [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]; -} - - --(BOOL) readMessageBeginReturningName:(NSString **)name - type:(SInt32 *)type - sequenceID:(SInt32 *)sequenceID - error:(NSError *__autoreleasing *)error -{ - SInt32 size; - if (![self readI32:&size error:error]) { - return NO; - } - ; - - if (size < 0) { - int version = size & VERSION_MASK; - if (version != VERSION_1) { - PROTOCOL_ERROR(NO, BadVersion, @"Bad message version"); - } - if (type != NULL) { - *type = size & 0x00FF; - } - NSString *messageName; - if (![self readString:&messageName error:error]) { - return NO; - } - if (name != nil) { - *name = messageName; - } - } - else { - - if (_strictRead) { - PROTOCOL_ERROR(NO, InvalidData, @"Missing message version, old client?"); - } - - if (_messageSizeLimit > 0 && size > _messageSizeLimit) { - PROTOCOL_ERROR(NO, SizeLimit, @"Message exceeeds size limit of %d", (int)size); - } - - NSString *messageName = [self readStringBody:size error:error]; - if (!messageName) { - return NO; - } - - if (name != NULL) { - *name = messageName; - } - - UInt8 messageType; - if (![self readByte:&messageType error:error]) { - return NO; - } - - if (type != NULL) { - *type = messageType; - } - } - - SInt32 seqID; - if (![self readI32:&seqID error:error]) { - return NO; - } - if (sequenceID != NULL) { - *sequenceID = seqID; - } - - return YES; -} - - --(BOOL) readMessageEnd:(NSError *__autoreleasing *)error -{ - return YES; -} - - --(BOOL) readStructBeginReturningName:(NSString *__autoreleasing *)name error:(NSError *__autoreleasing *)error -{ - return YES; -} - - --(BOOL) readStructEnd:(NSError *__autoreleasing *)error -{ - return YES; -} - - --(BOOL) readFieldBeginReturningName:(NSString *__autoreleasing *)name - type:(SInt32 *)fieldType - fieldID:(SInt32 *)fieldID - error:(NSError *__autoreleasing *)error -{ - if (name != nil) { - *name = nil; - } - - UInt8 ft; - if (![self readByte:&ft error:error]) { - return NO; - } - if (fieldType != NULL) { - *fieldType = ft; - } - if (ft != TTypeSTOP) { - SInt16 fid; - if (![self readI16:&fid error:error]) { - return NO; - } - if (fieldID != NULL) { - *fieldID = fid; - } - } - return YES; -} - - --(BOOL) readFieldEnd:(NSError *__autoreleasing *)error -{ - return YES; -} - - --(BOOL) readString:(NSString *__autoreleasing *)value error:(NSError *__autoreleasing *)error -{ - SInt32 size; - if (![self readI32:&size error:error]) { - return NO; - } - - NSString *string = [self readStringBody:size error:error]; - if (!string) { - return NO; - } - - *value = string; - - return YES; -} - - --(BOOL) readBool:(BOOL *)value error:(NSError *__autoreleasing *)error -{ - UInt8 byte; - if (![self readByte:&byte error:error]) { - return NO; - } - - *value = byte == 1; - - return YES; -} - - --(BOOL) readByte:(UInt8 *)value error:(NSError *__autoreleasing *)error -{ - UInt8 buff[1]; - if (![_transport readAll:buff offset:0 length:1 error:error]) { - PROTOCOL_TRANSPORT_ERROR(NO, error, @"Transport read failed"); - } - - *value = buff[0]; - - return YES; -} - - --(BOOL) readI16:(SInt16 *)value error:(NSError *__autoreleasing *)error -{ - UInt8 buff[2]; - if (![_transport readAll:buff offset:0 length:2 error:error]) { - PROTOCOL_TRANSPORT_ERROR(NO, error, @"Transport read failed"); - } - - *value = - ((SInt16)(buff[0] & 0xff) << 8) | - ((SInt16)(buff[1] & 0xff)); - - return YES; -} - - --(BOOL) readI32:(SInt32 *)value error:(NSError *__autoreleasing *)error -{ - UInt8 i32rd[4]; - if (![_transport readAll:i32rd offset:0 length:4 error:error]) { - PROTOCOL_TRANSPORT_ERROR(NO, error, @"Transport read failed"); - } - - *value = - ((i32rd[0] & 0xff) << 24) | - ((i32rd[1] & 0xff) << 16) | - ((i32rd[2] & 0xff) << 8) | - ((i32rd[3] & 0xff)); - - return YES; -} - - --(BOOL) readI64:(SInt64 *)value error:(NSError *__autoreleasing *)error -{ - UInt8 buff[8]; - if (![_transport readAll:buff offset:0 length:8 error:error]) { - PROTOCOL_TRANSPORT_ERROR(NO, error, @"Transport read failed"); - } - - *value = - ((SInt64)(buff[0] & 0xff) << 56) | - ((SInt64)(buff[1] & 0xff) << 48) | - ((SInt64)(buff[2] & 0xff) << 40) | - ((SInt64)(buff[3] & 0xff) << 32) | - ((SInt64)(buff[4] & 0xff) << 24) | - ((SInt64)(buff[5] & 0xff) << 16) | - ((SInt64)(buff[6] & 0xff) << 8) | - ((SInt64)(buff[7] & 0xff)); - - return YES; -} - - --(BOOL) readDouble:(double *)value error:(NSError *__autoreleasing *)error -{ - // FIXME - will this get us into trouble on PowerPC? - return [self readI64:(SInt64 *)value error:error]; -} - - --(BOOL) readBinary:(NSData *__autoreleasing *)value error:(NSError *__autoreleasing *)error -{ - SInt32 size; - if (![self readI32:&size error:error]) { - return NO; - } - - NSMutableData *data = [NSMutableData dataWithLength:size]; - if (!data) { - PROTOCOL_ERROR(NO, Unknown, @"Unable to allocate %d bytes", (int)size); - } - - if (![_transport readAll:data.mutableBytes offset:0 length:size error:error]) { - PROTOCOL_TRANSPORT_ERROR(NO, error, @"Transport read failed"); - } - - *value = data; - - return YES; -} - - --(BOOL) readMapBeginReturningKeyType:(SInt32 *)keyType - valueType:(SInt32 *)valueType - size:(SInt32 *)size - error:(NSError *__autoreleasing *)error -{ - UInt8 kt; - if (![self readByte:&kt error:error]) { - return NO; - } - - UInt8 vt; - if (![self readByte:&vt error:error]) { - return NO; - } - - SInt32 s; - if (![self readI32:&s error:error]) { - return NO; - } - - if (keyType != NULL) { - *keyType = kt; - } - - if (valueType != NULL) { - *valueType = vt; - } - - if (size != NULL) { - *size = s; - } - - return YES; -} - - --(BOOL) readMapEnd:(NSError *__autoreleasing *)error -{ - return YES; -} - - --(BOOL) readSetBeginReturningElementType:(SInt32 *)elementType - size:(SInt32 *)size - error:(NSError *__autoreleasing *)error -{ - UInt8 et; - if (![self readByte:&et error:error]) { - return NO; - } - - SInt32 s; - if (![self readI32:&s error:error]) { - return NO; - } - - if (elementType != NULL) { - *elementType = et; - } - - if (size != NULL) { - *size = s; - } - - return YES; -} - - --(BOOL) readSetEnd:(NSError *__autoreleasing *)error -{ - return YES; -} - - --(BOOL) readListBeginReturningElementType:(SInt32 *)elementType - size:(SInt32 *)size - error:(NSError *__autoreleasing *)error -{ - UInt8 et; - if (![self readByte:&et error:error]) { - return NO; - } - - SInt32 s; - if (![self readI32:&s error:error]) { - return NO; - } - - if (elementType != NULL) { - *elementType = et; - } - - if (size != NULL) { - *size = s; - } - - return YES; -} - - --(BOOL) readListEnd:(NSError *__autoreleasing *)error -{ - return YES; -} - - - --(BOOL) writeMessageBeginWithName:(NSString *)name - type:(SInt32)messageType - sequenceID:(SInt32)sequenceID - error:(NSError *__autoreleasing *)error -{ - if (_strictWrite) { - - int version = VERSION_1 | messageType; - - if (![self writeI32:version error:error]) { - return NO; - } - - if (![self writeString:name error:error]) { - return NO; - } - - if (![self writeI32:sequenceID error:error]) { - return NO; - } - } - else { - - if (![self writeString:name error:error]) { - return NO; - } - - if (![self writeByte:messageType error:error]) { - return NO; - } - - if (![self writeI32:sequenceID error:error]) { - return NO; - } - } - - _currentMessageName = name; - - return YES; -} - - --(BOOL) writeMessageEnd:(NSError *__autoreleasing *)error -{ - _currentMessageName = nil; - return YES; -} - - --(BOOL) writeStructBeginWithName:(NSString *)name - error:(NSError *__autoreleasing *)error -{ - return YES; -} - - --(BOOL) writeStructEnd:(NSError *__autoreleasing *)error -{ - return YES; -} - - --(BOOL) writeFieldBeginWithName:(NSString *)name - type:(SInt32)fieldType - fieldID:(SInt32)fieldID - error:(NSError *__autoreleasing *)error -{ - if (![self writeByte:fieldType error:error]) { - return NO; - } - - if (![self writeI16:fieldID error:error]) { - return NO; - } - - return YES; -} - - --(BOOL) writeBool:(BOOL)value error:(NSError *__autoreleasing *)error -{ - return [self writeByte:(value ? 1 : 0) error:error]; -} - - --(BOOL) writeByte:(UInt8)value error:(NSError *__autoreleasing *)error -{ - if (![_transport write:&value offset:0 length:1 error:error]) { - PROTOCOL_TRANSPORT_ERROR(NO, error, @"Transport write failed"); - } - return YES; -} - - --(BOOL) writeI16:(short)value error:(NSError *__autoreleasing *)error -{ - UInt8 buff[2]; - buff[0] = 0xff & (value >> 8); - buff[1] = 0xff & value; - - if (![_transport write:buff offset:0 length:2 error:error]) { - PROTOCOL_TRANSPORT_ERROR(NO, error, @"Transport write failed"); - } - - return YES; -} - - --(BOOL) writeI32:(SInt32)value error:(NSError *__autoreleasing *)error -{ - UInt8 buff[4]; - buff[0] = 0xFF & (value >> 24); - buff[1] = 0xFF & (value >> 16); - buff[2] = 0xFF & (value >> 8); - buff[3] = 0xFF & value; - - if (![_transport write:buff offset:0 length:4 error:error]) { - PROTOCOL_TRANSPORT_ERROR(NO, error, @"Transport write failed"); - } - - return YES; -} - - --(BOOL) writeI64:(SInt64)value error:(NSError *__autoreleasing *)error -{ - UInt8 buff[8]; - buff[0] = 0xFF & (value >> 56); - buff[1] = 0xFF & (value >> 48); - buff[2] = 0xFF & (value >> 40); - buff[3] = 0xFF & (value >> 32); - buff[4] = 0xFF & (value >> 24); - buff[5] = 0xFF & (value >> 16); - buff[6] = 0xFF & (value >> 8); - buff[7] = 0xFF & value; - - if (![_transport write:buff offset:0 length:8 error:error]) { - PROTOCOL_TRANSPORT_ERROR(NO, error, @"Transport write failed"); - } - - return YES; -} - - --(BOOL) writeDouble:(double)value error:(NSError *__autoreleasing *)error -{ - // FIXME - will this get us in trouble on PowerPC? - if (![self writeI64:*(SInt64 *)&value error:error]) { - return NO; - } - - return YES; -} - - --(BOOL) writeString:(NSString *)value error:(NSError *__autoreleasing *)error -{ - if (value != nil) { - - const char *utf8Bytes = [value UTF8String]; - - SInt32 length = (SInt32)strlen(utf8Bytes); - if (![self writeI32:length error:error]) { - return NO; - } - - if (![_transport write:(UInt8 *)utf8Bytes offset:0 length:(int)length error:error]) { - PROTOCOL_TRANSPORT_ERROR(NO, error, @"Transport write failed"); - } - - } - else { - - // instead of crashing when we get null, let's write out a zero - // length string - if (![self writeI32:0 error:error]) { - return NO; - } - - } - - return YES; -} - - --(BOOL) writeBinary:(NSData *)data error:(NSError *__autoreleasing *)error -{ - if (![self writeI32:(SInt32)data.length error:error]) { - return NO; - } - - if (![_transport write:data.bytes offset:0 length:(UInt32)data.length error:error]) { - PROTOCOL_TRANSPORT_ERROR(NO, error, @"Transport write failed"); - } - - return YES; -} - - --(BOOL) writeFieldStop:(NSError *__autoreleasing *)error -{ - if (![self writeByte:TTypeSTOP error:error]) { - return NO; - } - - return YES; -} - - --(BOOL) writeFieldEnd:(NSError *__autoreleasing *)error -{ - return YES; -} - - --(BOOL) writeMapBeginWithKeyType:(SInt32)keyType - valueType:(SInt32)valueType - size:(SInt32)size - error:(NSError *__autoreleasing *)error -{ - if (![self writeByte:keyType error:error]) { - return NO; - } - if (![self writeByte:valueType error:error]) { - return NO; - } - if (![self writeI32:(int)size error:error]) { - return NO; - } - return YES; -} - - --(BOOL) writeMapEnd:(NSError *__autoreleasing *)error -{ - return YES; -} - - --(BOOL) writeSetBeginWithElementType:(SInt32)elementType - size:(SInt32)size - error:(NSError *__autoreleasing *)error -{ - if (![self writeByte:elementType error:error]) { - return NO; - } - if (![self writeI32:size error:error]) { - return NO; - } - return YES; -} - - --(BOOL) writeSetEnd:(NSError *__autoreleasing *)error -{ - return YES; -} - - --(BOOL) writeListBeginWithElementType:(SInt32)elementType - size:(SInt32)size - error:(NSError *__autoreleasing *)error -{ - if (![self writeByte:elementType error:error]) { - return NO; - } - if (![self writeI32:size error:error]) { - return NO; - } - return YES; -} - - --(BOOL) writeListEnd:(NSError *__autoreleasing *)error -{ - return YES; -} - -@end diff --git a/lib/cocoa/src/protocol/TCompactProtocol.h b/lib/cocoa/src/protocol/TCompactProtocol.h deleted file mode 100644 index 3f6accc5aa9..00000000000 --- a/lib/cocoa/src/protocol/TCompactProtocol.h +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -#import "TProtocol.h" -#import "TTransport.h" -#import "TProtocolFactory.h" - -NS_ASSUME_NONNULL_BEGIN - - -@interface TCompactProtocol : NSObject - --(id) initWithTransport:(id )transport; - -@end - -@interface TCompactProtocolFactory : NSObject - -+(TCompactProtocolFactory *) sharedFactory; - --(TCompactProtocol *) newProtocolOnTransport:(id )transport; - -@end - - -NS_ASSUME_NONNULL_END \ No newline at end of file diff --git a/lib/cocoa/src/protocol/TCompactProtocol.m b/lib/cocoa/src/protocol/TCompactProtocol.m deleted file mode 100644 index 9b0ebb2a78a..00000000000 --- a/lib/cocoa/src/protocol/TCompactProtocol.m +++ /dev/null @@ -1,983 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -#import "TCompactProtocol.h" -#import "TProtocolError.h" - -static const UInt8 COMPACT_PROTOCOL_ID = 0x82; -static const UInt8 COMPACT_VERSION = 1; -static const UInt8 COMPACT_VERSION_MASK = 0x1F; // 0001 1111 -static const UInt8 COMPACT_TYPE_MASK = 0xE0; // 1110 0000 -static const UInt8 COMPACT_TYPE_BITS = 0x07; // 0000 0111 -static const int COMPACT_TYPE_SHIFT_AMOUNT = 5; - -enum { - TCType_STOP = 0x00, - TCType_BOOLEAN_TRUE = 0x01, - TCType_BOOLEAN_FALSE = 0x02, - TCType_BYTE = 0x03, - TCType_I16 = 0x04, - TCType_I32 = 0x05, - TCType_I64 = 0x06, - TCType_DOUBLE = 0x07, - TCType_BINARY = 0x08, - TCType_LIST = 0x09, - TCType_SET = 0x0A, - TCType_MAP = 0x0B, - TCType_STRUCT = 0x0C, -}; - -@implementation TCompactProtocolFactory - -+(TCompactProtocolFactory *) sharedFactory -{ - static TCompactProtocolFactory *gSharedFactory = nil; - if (gSharedFactory == nil) { - gSharedFactory = [[TCompactProtocolFactory alloc] init]; - } - - return gSharedFactory; -} - --(NSString *) protocolName -{ - return @"compact"; -} - --(TCompactProtocol *) newProtocolOnTransport:(id )transport -{ - return [[TCompactProtocol alloc] initWithTransport:transport]; -} - -@end - - -@interface TCompactProtocol () - -@property(strong, nonatomic) id transport; - -@property(strong, nonatomic) NSMutableArray *lastField; -@property(assign, nonatomic) short lastFieldId; - -@property(strong, nonatomic) NSString *boolFieldName; -@property(strong, nonatomic) NSNumber *boolFieldType; -@property(strong, nonatomic) NSNumber *boolFieldId; -@property(strong, nonatomic) NSNumber *booleanValue; - -@property(strong, nonatomic) NSString *currentMessageName; - -@end - - -@implementation TCompactProtocol - --(id) init -{ - self = [super init]; - - if (self != nil) { - _lastField = [[NSMutableArray alloc] init]; - } - - return self; -} - --(id) initWithTransport:(id )aTransport -{ - self = [self init]; - - if (self != nil) { - _transport = aTransport; - } - - return self; -} - --(id ) transport -{ - return _transport; -} - --(BOOL) writeByteDirect:(UInt8)n error:(NSError *__autoreleasing *)error -{ - if (![_transport write:(UInt8 *)&n offset:0 length:1 error:error]) { - PROTOCOL_TRANSPORT_ERROR(NO, error, @"Transport write failed"); - } - return YES; -} - --(BOOL) writeVarint32:(UInt32)n error:(NSError *__autoreleasing *)error -{ - UInt8 i32buf[5] = {0}; - UInt32 idx = 0; - - while (true) { - if ((n & ~0x7F) == 0) { - i32buf[idx++] = (UInt8)n; - break; - } - else { - i32buf[idx++] = (UInt8)((n & 0x7F) | 0x80); - n >>= 7; - } - } - - if (![_transport write:i32buf offset:0 length:idx error:error]) { - PROTOCOL_TRANSPORT_ERROR(NO, error, @"Transport write failed"); - } - - return YES; -} - --(BOOL) writeMessageBeginWithName:(NSString *)name - type:(SInt32)messageType - sequenceID:(SInt32)sequenceID - error:(NSError *__autoreleasing *)error -{ - if (![self writeByteDirect:COMPACT_PROTOCOL_ID error:error]) { - return NO; - } - if (![self writeByteDirect:(UInt8)((COMPACT_VERSION & COMPACT_VERSION_MASK) | - ((((UInt32)messageType) << COMPACT_TYPE_SHIFT_AMOUNT) & COMPACT_TYPE_MASK)) error:error]) - { - return NO; - } - if (![self writeVarint32:(UInt32)sequenceID error:error]) { - return NO; - } - if (![self writeString:name error:error]) { - return NO; - } - - _currentMessageName = name; - - return YES; -} - --(BOOL) writeStructBeginWithName:(NSString *)name error:(NSError *__autoreleasing *)error -{ - [_lastField addObject:@(_lastFieldId)]; - _lastFieldId = 0; - return YES; -} - --(BOOL) writeStructEnd:(NSError *__autoreleasing *)error -{ - _lastFieldId = [_lastField.lastObject shortValue]; - [_lastField removeLastObject]; - return YES; -} - --(BOOL) writeFieldBeginWithName:(NSString *)name - type:(SInt32)fieldType - fieldID:(SInt32)fieldID - error:(NSError *__autoreleasing *)error -{ - if (fieldType == TTypeBOOL) { - _boolFieldName = [name copy]; - _boolFieldType = @(fieldType); - _boolFieldId = @(fieldID); - return YES; - } - else { - return [self writeFieldBeginInternalWithName:name - type:fieldType - fieldID:fieldID - typeOverride:0xFF - error:error]; - } -} - --(BOOL) writeFieldBeginInternalWithName:(NSString *)name - type:(SInt32)fieldType - fieldID:(SInt32)fieldID - typeOverride:(UInt8)typeOverride - error:(NSError *__autoreleasing *)error -{ - UInt8 typeToWrite = typeOverride == 0xFF ? [self compactTypeForTType:fieldType] : typeOverride; - - // check if we can use delta encoding for the field id - if (fieldID > _lastFieldId && fieldID - _lastFieldId <= 15) { - // Write them together - if (![self writeByteDirect:(fieldID - _lastFieldId) << 4 | typeToWrite error:error]) { - return NO; - } - } - else { - // Write them separate - if (![self writeByteDirect:typeToWrite error:error]) { - return NO; - } - if (![self writeI16:fieldID error:error]) { - return NO; - } - } - - _lastFieldId = fieldID; - - return YES; -} - --(BOOL) writeFieldStop:(NSError *__autoreleasing *)error -{ - return [self writeByteDirect:TCType_STOP error:error]; -} - --(BOOL) writeMapBeginWithKeyType:(SInt32)keyType - valueType:(SInt32)valueType - size:(SInt32)size - error:(NSError *__autoreleasing *)error -{ - if (size == 0) { - if (![self writeByteDirect:0 error:error]) { - return NO; - } - } - else { - if (![self writeVarint32:(UInt32)size error:error]) { - return NO; - } - if (![self writeByteDirect:[self compactTypeForTType:keyType] << 4 | [self compactTypeForTType:valueType] error:error]) { - return NO; - } - } - return YES; -} - --(BOOL) writeListBeginWithElementType:(SInt32)elementType - size:(SInt32)size - error:(NSError *__autoreleasing *)error -{ - return [self writeCollectionBeginWithElementType:elementType size:size error:error]; -} - --(BOOL) writeSetBeginWithElementType:(SInt32)elementType - size:(SInt32)size - error:(NSError *__autoreleasing *)error -{ - return [self writeCollectionBeginWithElementType:elementType size:size error:error]; -} - --(BOOL) writeBool:(BOOL)b error:(NSError *__autoreleasing *)error -{ - BOOL result; - if (_boolFieldId != nil && _boolFieldName != nil && _boolFieldType != nil) { - // we haven't written the field header yet - result = [self writeFieldBeginInternalWithName:_boolFieldName - type:_boolFieldType.intValue - fieldID:_boolFieldId.intValue - typeOverride:b ? TCType_BOOLEAN_TRUE : TCType_BOOLEAN_FALSE - error:error]; - _boolFieldId = nil; - _boolFieldName = nil; - _boolFieldType = nil; - } - else { - // we're not part of a field, so just Write the value. - result = [self writeByteDirect:b ? TCType_BOOLEAN_TRUE : TCType_BOOLEAN_FALSE error:error]; - } - return result; -} - --(BOOL) writeByte:(UInt8)value error:(NSError *__autoreleasing *)error -{ - return [self writeByteDirect:value error:error]; -} - --(BOOL) writeI16:(SInt16)value error:(NSError *__autoreleasing *)error -{ - return [self writeVarint32:[self i32ToZigZag:value] error:error]; -} - --(BOOL) writeI32:(SInt32)value error:(NSError *__autoreleasing *)error -{ - return [self writeVarint32:[self i32ToZigZag:value] error:error]; -} - --(BOOL) writeI64:(SInt64)value error:(NSError *__autoreleasing *)error -{ - return [self writeVarint64:[self i64ToZigZag:value] error:error]; -} - --(BOOL) writeDouble:(double)value error:(NSError *__autoreleasing *)error -{ - // Safe bit-casting double->uint64 - - UInt64 bits = 0; - memcpy(&bits, &value, 8); - - bits = OSSwapHostToLittleInt64(bits); - - if (![_transport write:(UInt8 *)&bits offset:0 length:8 error:error]) { - PROTOCOL_TRANSPORT_ERROR(NO, error, @"Transport write failed"); - } - - return YES; -} - --(BOOL) writeString:(NSString *)value error:(NSError *__autoreleasing *)error -{ - return [self writeBinary:[value dataUsingEncoding:NSUTF8StringEncoding] error:error]; -} - --(BOOL) writeBinary:(NSData *)data error:(NSError *__autoreleasing *)error -{ - if (![self writeVarint32:(UInt32)data.length error:error]) { - return NO; - } - if (![_transport write:data.bytes offset:0 length:(UInt32)data.length error:error]) { - PROTOCOL_TRANSPORT_ERROR(NO, error, @"Transport write failed"); - } - return YES; -} - --(BOOL) writeMessageEnd:(NSError *__autoreleasing *)error -{ - _currentMessageName = nil; - return YES; -} - --(BOOL) writeMapEnd:(NSError *__autoreleasing *)error -{ - return YES; -} - --(BOOL) writeListEnd:(NSError *__autoreleasing *)error -{ - return YES; -} - --(BOOL) writeSetEnd:(NSError *__autoreleasing *)error -{ - return YES; -} - --(BOOL) writeFieldEnd:(NSError *__autoreleasing *)error -{ - return YES; -} - --(BOOL) writeCollectionBeginWithElementType:(SInt32)elementType - size:(SInt32)size - error:(NSError *__autoreleasing *)error -{ - UInt8 ctypeElement = [self compactTypeForTType:elementType]; - - if (size <= 14) { - if (![self writeByteDirect:size << 4 | ctypeElement error:error]) { - return NO; - } - } - else { - if (![self writeByteDirect:0xf0 | ctypeElement error:error]) { - return NO; - } - if (![self writeVarint32:(UInt32)size error:error]) { - return NO; - } - } - return YES; -} - --(BOOL) writeVarint64:(UInt64)n error:(NSError *__autoreleasing *)error -{ - UInt8 varint64out[10] = {0}; - int idx = 0; - - while (true) { - if ((n & ~0x7FL) == 0) { - varint64out[idx++] = (UInt8)n; - break; - } - else { - varint64out[idx++] = (UInt8)((n & 0x7F) | 0x80); - n >>= 7; - } - } - - if (![_transport write:varint64out offset:0 length:idx error:error]) { - PROTOCOL_TRANSPORT_ERROR(NO, error, @"Transport write failed"); - } - - return YES; -} - --(UInt32) i32ToZigZag:(SInt32)n -{ - /* - ZigZag encoding maps signed integers to unsigned integers so that - numbers with a small absolute value (for instance, -1) have - a small varint encoded value too. It does this in a way that - "zig-zags" back and forth through the positive and negative integers, - so that -1 is encoded as 1, 1 is encoded as 2, -2 is encoded as 3, and so - on - */ - return (UInt32)(n << 1) ^ (UInt32)(n >> 31); -} - --(UInt64) i64ToZigZag:(SInt64)n -{ - return (UInt64)(n << 1) ^ (UInt64)(n >> 63); -} - --(BOOL) readMessageBeginReturningName:(NSString **)pname - type:(SInt32 *)ptype - sequenceID:(SInt32 *)psequenceID - error:(NSError *__autoreleasing *)error -{ - UInt8 protocolId; - if (![self readByte:&protocolId error:error]) { - return NO; - } - - if (protocolId != COMPACT_PROTOCOL_ID) { - if (error) { - *error = [NSError errorWithDomain:TProtocolErrorDomain - code:TProtocolErrorUnknown - userInfo:@{TProtocolErrorExtendedErrorKey: @(TProtocolExtendedErrorMismatchedProtocol), - TProtocolErrorExpectedIdKey: @(COMPACT_PROTOCOL_ID)}]; - } - return NO; - } - - UInt8 versionAndType; - if (![self readByte:&versionAndType error:error]) { - return NO; - } - - UInt8 version = versionAndType & COMPACT_VERSION_MASK; - if (version != COMPACT_VERSION) { - if (error) { - *error = [NSError errorWithDomain:TProtocolErrorDomain - code:TProtocolErrorBadVersion - userInfo:@{TProtocolErrorExpectedVersionKey: @(COMPACT_VERSION)}]; - } - return NO; - } - - int type = (versionAndType >> COMPACT_TYPE_SHIFT_AMOUNT) & COMPACT_TYPE_BITS; - UInt32 sequenceID; - if (![self readVarint32:&sequenceID error:error]) { - return NO; - } - NSString *name; - if (![self readString:&name error:error]) { - return NO; - } - - if (ptype != NULL) { - *ptype = type; - } - if (psequenceID != NULL) { - *psequenceID = sequenceID; - } - if (pname != NULL) { - *pname = name; - } - return YES; -} - --(BOOL) readStructBeginReturningName:(NSString **)pname error:(NSError *__autoreleasing *)error -{ - [_lastField addObject:@(_lastFieldId)]; - _lastFieldId = 0; - - if (pname != NULL) { - *pname = @""; - } - - return YES; -} - --(BOOL) readStructEnd:(NSError *__autoreleasing *)error -{ - _lastFieldId = [_lastField.lastObject shortValue]; - [_lastField removeLastObject]; - return YES; -} - --(BOOL) readFieldBeginReturningName:(NSString **)pname - type:(SInt32 *)pfieldType - fieldID:(SInt32 *)pfieldID - error:(NSError *__autoreleasing *)error -{ - UInt8 byte; - if (![self readByte:&byte error:error]) { - return NO; - } - - UInt8 type = byte & 0x0f; - - // if it's a stop, then we can return immediately, as the struct is over. - if (type == TCType_STOP) { - if (pname != NULL) { - *pname = @""; - } - if (pfieldType != NULL) { - *pfieldType = TTypeSTOP; - } - if (pfieldID != NULL) { - *pfieldID = 0; - } - return YES; - } - - short fieldId = 0; - - // mask off the 4 MSB of the type header. it could contain a field id delta. - short modifier = (byte & 0xf0) >> 4; - if (modifier == 0) { - // not a delta. look ahead for the zigzag varint field id. - if (![self readI16:&fieldId error:error]) { - return NO; - } - } - else { - // has a delta. add the delta to the last Read field id. - fieldId = _lastFieldId + modifier; - } - - UInt8 fieldType; - if (![self ttype:&fieldType forCompactType:type error:error]) { - return NO; - } - - if (pname != NULL) { - *pname = @""; - } - if (pfieldType != NULL) { - *pfieldType = fieldType; - } - if (pfieldID != NULL) { - *pfieldID = fieldId; - } - - // if this happens to be a boolean field, the value is encoded in the type - if (type == TCType_BOOLEAN_TRUE || - type == TCType_BOOLEAN_FALSE) - { - // save the boolean value in a special instance variable. - _booleanValue = [NSNumber numberWithBool:type == TCType_BOOLEAN_TRUE]; - } - - // push the new field onto the field stack so we can keep the deltas going. - _lastFieldId = fieldId; - - return YES; -} - --(BOOL) readMapBeginReturningKeyType:(SInt32 *)pkeyType - valueType:(SInt32 *)pvalueType - size:(SInt32 *)psize - error:(NSError *__autoreleasing *)error -{ - UInt8 keyAndValueType = 0; - UInt32 size; - if (![self readVarint32:&size error:error]) { - return NO; - } - if (size != 0) { - if (![self readByte:&keyAndValueType error:error]) { - return NO; - } - } - - UInt8 keyType; - if (![self ttype:&keyType forCompactType:keyAndValueType >> 4 error:error]) { - return NO; - } - - UInt8 valueType; - if (![self ttype:&valueType forCompactType:keyAndValueType & 0xf error:error]) { - return NO; - } - - if (pkeyType != NULL) { - *pkeyType = keyType; - } - if (pvalueType != NULL) { - *pvalueType = valueType; - } - if (psize != NULL) { - *psize = size; - } - - return YES; -} - --(BOOL) readListBeginReturningElementType:(SInt32 *)pelementType - size:(SInt32 *)psize - error:(NSError *__autoreleasing *)error -{ - UInt8 sizeAndType; - if (![self readByte:&sizeAndType error:error]) { - return NO; - } - - UInt32 size = (sizeAndType >> 4) & 0x0f; - if (size == 15) { - if (![self readVarint32:&size error:error]) { - return NO; - } - } - - UInt8 elementType; - if (![self ttype:&elementType forCompactType:sizeAndType & 0x0f error:error]) { - return NO; - } - - if (pelementType != NULL) { - *pelementType = elementType; - } - if (psize != NULL) { - *psize = size; - } - - return YES; -} - --(BOOL) readSetBeginReturningElementType:(SInt32 *)pelementType - size:(SInt32 *)psize - error:(NSError *__autoreleasing *)error -{ - return [self readListBeginReturningElementType:pelementType size:psize error:error]; -} - --(BOOL) readBool:(BOOL *)value error:(NSError *__autoreleasing *)error -{ - if (_booleanValue != nil) { - - BOOL result = _booleanValue.boolValue; - _booleanValue = nil; - - *value = result; - } - else { - - UInt8 result; - if (![self readByte:&result error:error]) { - return NO; - } - - *value = result == TCType_BOOLEAN_TRUE; - } - - return YES; -} - --(BOOL) readByte:(UInt8 *)value error:(NSError *__autoreleasing *)error -{ - if (![_transport readAll:value offset:0 length:1 error:error]) { - PROTOCOL_TRANSPORT_ERROR(NO, error, @"Transport read failed"); - } - return YES; -} - --(BOOL) readI16:(SInt16 *)value error:(NSError *__autoreleasing *)error -{ - UInt32 v; - if (![self readVarint32:&v error:error]) { - return NO; - } - - if (value) { - *value = (SInt16)[self zigZagToi32:v]; - } - - return YES; -} - --(BOOL) readI32:(SInt32 *)value error:(NSError *__autoreleasing *)error -{ - UInt32 v; - if (![self readVarint32:&v error:error]) { - return NO; - } - - if (value) { - *value = [self zigZagToi32:v]; - } - - return YES; -} - --(BOOL) readI64:(SInt64 *)value error:(NSError *__autoreleasing *)error -{ - UInt64 v; - if (![self readVarint64:&v error:error]) { - return NO; - } - - if (value) { - *value = [self zigZagToi64:v]; - } - - return YES; -} - --(BOOL) readDouble:(double *)value error:(NSError *__autoreleasing *)error -{ - UInt64 bits; - if (![_transport readAll:(UInt8 *)&bits offset:0 length:8 error:error]) { - PROTOCOL_TRANSPORT_ERROR(NO, error, @"Transport read failed"); - } - - bits = OSSwapLittleToHostInt64(bits); - - if (value) { - memcpy(value, &bits, sizeof(bits)); - } - - return YES; -} - --(BOOL) readString:(NSString *__autoreleasing *)value error:(NSError *__autoreleasing *)error -{ - UInt32 length; - if (![self readVarint32:&length error:error]) { - return NO; - } - - NSString *result; - - if (length != 0) { - - NSData *data; - if (![self readBinaryOfLength:length data:&data error:error]) { - return NO; - } - - result = [[NSString alloc] initWithData:data - encoding:NSUTF8StringEncoding]; - } - else { - result = @""; - } - - if (value) { - *value = result; - } - - return YES; -} - --(BOOL) readBinary:(NSData *__autoreleasing *)value error:(NSError *__autoreleasing *)error -{ - UInt32 length; - if (![self readVarint32:&length error:error]) { - return NO; - } - - return [self readBinaryOfLength:length data:value error:error]; -} - --(BOOL) readBinaryOfLength:(UInt32)length data:(NSData *__autoreleasing *)value error:(NSError *__autoreleasing *)error -{ - NSData *result; - - if (length != 0) { - - NSMutableData *buf = [NSMutableData dataWithLength:length]; - if (![_transport readAll:buf.mutableBytes offset:0 length:length error:error]) { - PROTOCOL_TRANSPORT_ERROR(NO, error, @"Transport read failed"); - } - - result = buf; - } - else { - - result = [NSData data]; - - } - - if (value) { - *value = result; - } - - return YES; -} - --(BOOL) readMessageEnd:(NSError *__autoreleasing *)error -{ - return YES; -} --(BOOL) readFieldEnd:(NSError *__autoreleasing *)error -{ - return YES; -} --(BOOL) readMapEnd:(NSError *__autoreleasing *)error -{ - return YES; -} --(BOOL) readListEnd:(NSError *__autoreleasing *)error -{ - return YES; -} --(BOOL) readSetEnd:(NSError *__autoreleasing *)error -{ - return YES; -} - --(BOOL) readVarint32:(UInt32 *)value error:(NSError *__autoreleasing *)error -{ - UInt32 result = 0; - int shift = 0; - - while (true) { - - UInt8 byte; - if (![self readByte:&byte error:error]) { - return NO; - } - - result |= (UInt32)(byte & 0x7f) << shift; - if (!(byte & 0x80)) { - break; - } - - shift += 7; - } - - if (value) { - *value = result; - } - - return YES; -} - --(BOOL) readVarint64:(UInt64 *)value error:(NSError *__autoreleasing *)error -{ - int shift = 0; - UInt64 result = 0; - - while (true) { - - UInt8 byte; - if (![self readByte:&byte error:error]) { - return NO; - } - - result |= (UInt64)(byte & 0x7f) << shift; - if (!(byte & 0x80)) { - break; - } - - shift += 7; - } - - if (value) { - *value = result; - } - - return YES; -} - --(SInt32) zigZagToi32:(UInt32)n -{ - return (SInt32)(n >> 1) ^ (-(SInt32)(n & 1)); -} - --(SInt64) zigZagToi64:(UInt64)n -{ - return (SInt64)(n >> 1) ^ (-(SInt64)(n & 1)); -} - --(BOOL) ttype:(UInt8 *)ttype forCompactType:(UInt8)ctype error:(NSError *__autoreleasing *)error -{ - switch (ctype & 0x0f) { - case TCType_STOP: - *ttype = TTypeSTOP; - return YES; - - case TCType_BOOLEAN_FALSE: - case TCType_BOOLEAN_TRUE: - *ttype = TTypeBOOL; - return YES; - - case TCType_BYTE: - *ttype = TTypeBYTE; - return YES; - - case TCType_I16: - *ttype = TTypeI16; - return YES; - - case TCType_I32: - *ttype = TTypeI32; - return YES; - - case TCType_I64: - *ttype = TTypeI64; - return YES; - - case TCType_DOUBLE: - *ttype = TTypeDOUBLE; - return YES; - - case TCType_BINARY: - *ttype = TTypeSTRING; - return YES; - - case TCType_LIST: - *ttype = TTypeLIST; - return YES; - - case TCType_SET: - *ttype = TTypeSET; - return YES; - - case TCType_MAP: - *ttype = TTypeMAP; - return YES; - - case TCType_STRUCT: - *ttype = TTypeSTRUCT; - return YES; - - default: - if (error) { - *error = [NSError errorWithDomain:TProtocolErrorDomain - code:TProtocolErrorUnknown - userInfo:@{TProtocolErrorTypeKey: @((UInt8)(ctype & 0x0F))}]; - } - return NO; - } -} - --(UInt8) compactTypeForTType:(UInt8)ttype -{ - static UInt8 ttypeToCompactType[] = { - [TTypeSTOP] = TCType_STOP, - [TTypeBOOL] = TCType_BOOLEAN_FALSE, - [TTypeBYTE] = TCType_BYTE, - [TTypeDOUBLE] = TCType_DOUBLE, - [TTypeI16] = TCType_I16, - [TTypeI32] = TCType_I32, - [TTypeI64] = TCType_I64, - [TTypeSTRING] = TCType_BINARY, - [TTypeSTRUCT] = TCType_STRUCT, - [TTypeMAP] = TCType_MAP, - [TTypeSET] = TCType_SET, - [TTypeLIST] = TCType_LIST - }; - - return ttypeToCompactType[ttype]; -} - -@end diff --git a/lib/cocoa/src/protocol/TMultiplexedProtocol.h b/lib/cocoa/src/protocol/TMultiplexedProtocol.h deleted file mode 100644 index b8ce361f513..00000000000 --- a/lib/cocoa/src/protocol/TMultiplexedProtocol.h +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -#import - -#import "TProtocolDecorator.h" - -NS_ASSUME_NONNULL_BEGIN - - -extern NSString *TMultiplexedProtocolSeperator; - - -@interface TMultiplexedProtocol : TProtocolDecorator - --(id) initWithProtocol:(id )protocol - serviceName:(NSString *)name; - -@end - - -NS_ASSUME_NONNULL_END \ No newline at end of file diff --git a/lib/cocoa/src/protocol/TMultiplexedProtocol.m b/lib/cocoa/src/protocol/TMultiplexedProtocol.m deleted file mode 100644 index 5838c576b05..00000000000 --- a/lib/cocoa/src/protocol/TMultiplexedProtocol.m +++ /dev/null @@ -1,66 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -#import "TMultiplexedProtocol.h" - -#import "TProtocol.h" - -NSString *TMultiplexedProtocolSeperator = @":"; - - -@interface TMultiplexedProtocol () - -@property(strong, nonatomic) NSString *serviceName; - -@end - - -@implementation TMultiplexedProtocol - --(id) initWithProtocol:(id )protocol - serviceName:(NSString *)name -{ - self = [super initWithProtocol:protocol]; - if (self) { - _serviceName = name; - } - return self; -} - --(BOOL) writeMessageBeginWithName:(NSString *)name - type:(SInt32)messageType - sequenceID:(SInt32)sequenceID - error:(NSError *__autoreleasing *)error -{ - switch (messageType) { - case TMessageTypeCALL: - case TMessageTypeONEWAY: { - NSMutableString *serviceFunction = [[NSMutableString alloc] initWithString:_serviceName]; - [serviceFunction appendString:TMultiplexedProtocolSeperator]; - [serviceFunction appendString:name]; - return [super writeMessageBeginWithName:serviceFunction type:messageType sequenceID:sequenceID error:error]; - } - break; - - default: - return [super writeMessageBeginWithName:name type:messageType sequenceID:sequenceID error:error]; - } -} - -@end diff --git a/lib/cocoa/src/protocol/TProtocol.h b/lib/cocoa/src/protocol/TProtocol.h deleted file mode 100644 index 841059f5102..00000000000 --- a/lib/cocoa/src/protocol/TProtocol.h +++ /dev/null @@ -1,164 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -#import - -#import "TTransport.h" - - -NS_ASSUME_NONNULL_BEGIN - - -typedef NS_ENUM (int, TMessageType) { - TMessageTypeCALL = 1, - TMessageTypeREPLY = 2, - TMessageTypeEXCEPTION = 3, - TMessageTypeONEWAY = 4 -}; - -typedef NS_ENUM (int, TType) { - TTypeSTOP = 0, - TTypeVOID = 1, - TTypeBOOL = 2, - TTypeBYTE = 3, - TTypeDOUBLE = 4, - TTypeI16 = 6, - TTypeI32 = 8, - TTypeI64 = 10, - TTypeSTRING = 11, - TTypeSTRUCT = 12, - TTypeMAP = 13, - TTypeSET = 14, - TTypeLIST = 15 -}; - - -@protocol TProtocol - --(id ) transport; - --(BOOL) readMessageBeginReturningName:(NSString *__nullable __autoreleasing *__nullable)name - type:(nullable SInt32 *)type - sequenceID:(nullable SInt32 *)sequenceID - error:(NSError *__autoreleasing *)error; --(BOOL) readMessageEnd:(NSError *__autoreleasing *)error; - --(BOOL) readStructBeginReturningName:(NSString *__nullable __autoreleasing *__nullable)name - error:(NSError *__autoreleasing *)error; --(BOOL) readStructEnd:(NSError *__autoreleasing *)error; - --(BOOL) readFieldBeginReturningName:(NSString *__nullable __autoreleasing *__nullable)name - type:(SInt32 *)fieldType - fieldID:(nullable SInt32 *)fieldID - error:(NSError *__autoreleasing *)error; --(BOOL) readFieldEnd:(NSError *__autoreleasing *)error; - --(BOOL) readString:(NSString *__nonnull __autoreleasing *__nonnull)value error:(NSError **)error; - --(BOOL) readBool:(BOOL *)value error:(NSError *__autoreleasing *)error; - --(BOOL) readByte:(UInt8 *)value error:(NSError *__autoreleasing *)error; - --(BOOL) readI16:(SInt16 *)value error:(NSError *__autoreleasing *)error; - --(BOOL) readI32:(SInt32 *)value error:(NSError *__autoreleasing *)error; - --(BOOL) readI64:(SInt64 *)value error:(NSError *__autoreleasing *)error; - --(BOOL) readDouble:(double *)value error:(NSError *__autoreleasing *)error; - --(BOOL) readBinary:(NSData *__nonnull __autoreleasing *__nonnull)value error:(NSError **)error; - --(BOOL) readMapBeginReturningKeyType:(nullable SInt32 *)keyType - valueType:(nullable SInt32 *)valueType - size:(SInt32 *)size - error:(NSError *__autoreleasing *)error; --(BOOL) readMapEnd:(NSError *__autoreleasing *)error; - - --(BOOL) readSetBeginReturningElementType:(nullable SInt32 *)elementType - size:(SInt32 *)size - error:(NSError *__autoreleasing *)error; --(BOOL) readSetEnd:(NSError *__autoreleasing *)error; - - --(BOOL) readListBeginReturningElementType:(nullable SInt32 *)elementType - size:(SInt32 *)size - error:(NSError *__autoreleasing *)error; --(BOOL) readListEnd:(NSError *__autoreleasing *)error; - - --(BOOL) writeMessageBeginWithName:(NSString *)name - type:(SInt32)messageType - sequenceID:(SInt32)sequenceID - error:(NSError *__autoreleasing *)error; --(BOOL) writeMessageEnd:(NSError *__autoreleasing *)error; - --(BOOL) writeStructBeginWithName:(NSString *)name error:(NSError **)error; --(BOOL) writeStructEnd:(NSError *__autoreleasing *)error; - --(BOOL) writeFieldBeginWithName:(NSString *)name - type:(SInt32)fieldType - fieldID:(SInt32)fieldID - error:(NSError *__autoreleasing *)error; - --(BOOL) writeI32:(SInt32)value error:(NSError *__autoreleasing *)error; - --(BOOL) writeI64:(SInt64)value error:(NSError *__autoreleasing *)error; - --(BOOL) writeI16:(short)value error:(NSError *__autoreleasing *)error; - --(BOOL) writeByte:(UInt8)value error:(NSError *__autoreleasing *)error; - --(BOOL) writeString:(NSString *)value error:(NSError *__autoreleasing *)error; - --(BOOL) writeDouble:(double)value error:(NSError *__autoreleasing *)error; - --(BOOL) writeBool:(BOOL)value error:(NSError *__autoreleasing *)error; - --(BOOL) writeBinary:(NSData *)data error:(NSError *__autoreleasing *)error; - --(BOOL) writeFieldStop:(NSError *__autoreleasing *)error; - --(BOOL) writeFieldEnd:(NSError *__autoreleasing *)error; - --(BOOL) writeMapBeginWithKeyType:(SInt32)keyType - valueType:(SInt32)valueType - size:(SInt32)size - error:(NSError *__autoreleasing *)error; --(BOOL) writeMapEnd:(NSError *__autoreleasing *)error; - - --(BOOL) writeSetBeginWithElementType:(SInt32)elementType - size:(SInt32)size - error:(NSError *__autoreleasing *)error; --(BOOL) writeSetEnd:(NSError *__autoreleasing *)error; - - --(BOOL) writeListBeginWithElementType:(SInt32)elementType - size:(SInt32)size - error:(NSError *__autoreleasing *)error; - --(BOOL) writeListEnd:(NSError *__autoreleasing *)error; - - -@end - - -NS_ASSUME_NONNULL_END diff --git a/lib/cocoa/src/protocol/TProtocolDecorator.h b/lib/cocoa/src/protocol/TProtocolDecorator.h deleted file mode 100644 index 369b6a2335b..00000000000 --- a/lib/cocoa/src/protocol/TProtocolDecorator.h +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -#import - -#import "TProtocol.h" - -NS_ASSUME_NONNULL_BEGIN - - -@interface TProtocolDecorator : NSObject - --(id) initWithProtocol:(id )protocol; - -@end - - -NS_ASSUME_NONNULL_END \ No newline at end of file diff --git a/lib/cocoa/src/protocol/TProtocolDecorator.m b/lib/cocoa/src/protocol/TProtocolDecorator.m deleted file mode 100644 index 218f900c41e..00000000000 --- a/lib/cocoa/src/protocol/TProtocolDecorator.m +++ /dev/null @@ -1,295 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -#import "TProtocolDecorator.h" - - -@interface TProtocolDecorator () - -@property(strong, nonatomic) id concreteProtocol; - -@end - - -@implementation TProtocolDecorator - --(id) initWithProtocol:(id )protocol -{ - self = [super init]; - if (self) { - _concreteProtocol = protocol; - } - return self; -} - --(id ) transport -{ - return [_concreteProtocol transport]; -} - --(BOOL) readMessageBeginReturningName:(NSString **)name - type:(SInt32 *)type - sequenceID:(SInt32 *)sequenceID - error:(NSError *__autoreleasing *)error -{ - return [_concreteProtocol readMessageBeginReturningName:name - type:type - sequenceID:sequenceID - error:error]; -} - --(BOOL) readMessageEnd:(NSError *__autoreleasing *)error -{ - return [_concreteProtocol readMessageEnd:error]; -} - --(BOOL) readStructBeginReturningName:(NSString **)name - error:(NSError *__autoreleasing *)error -{ - return [_concreteProtocol readStructBeginReturningName:name error:error]; -} - --(BOOL) readStructEnd:(NSError *__autoreleasing *)error -{ - return [_concreteProtocol readStructEnd:error]; -} - --(BOOL) readFieldBeginReturningName:(NSString **)name - type:(SInt32 *)fieldType - fieldID:(SInt32 *)fieldID - error:(NSError *__autoreleasing *)error -{ - return [_concreteProtocol readFieldBeginReturningName:name - type:fieldType - fieldID:fieldID - error:error]; -} --(BOOL) readFieldEnd:(NSError *__autoreleasing *)error -{ - return [_concreteProtocol readFieldEnd:error]; -} - --(BOOL) readString:(NSString *__autoreleasing *)value error:(NSError *__autoreleasing *)error -{ - return [_concreteProtocol readString:value error:error]; -} - --(BOOL) readBool:(BOOL *)value error:(NSError *__autoreleasing *)error -{ - return [_concreteProtocol readBool:value error:error]; -} - --(BOOL) readByte:(UInt8 *)value error:(NSError *__autoreleasing *)error -{ - return [_concreteProtocol readByte:value error:error]; -} - --(BOOL) readI16:(SInt16 *)value error:(NSError *__autoreleasing *)error -{ - return [_concreteProtocol readI16:value error:error]; -} - --(BOOL) readI32:(SInt32 *)value error:(NSError *__autoreleasing *)error -{ - return [_concreteProtocol readI32:value error:error]; -} - --(BOOL) readI64:(SInt64 *)value error:(NSError *__autoreleasing *)error -{ - return [_concreteProtocol readI64:value error:error]; -} - --(BOOL) readDouble:(double *)value error:(NSError *__autoreleasing *)error -{ - return [_concreteProtocol readDouble:value error:error]; -} - --(BOOL) readBinary:(NSData *__autoreleasing *)value error:(NSError *__autoreleasing *)error -{ - return [_concreteProtocol readBinary:value error:error]; -} - --(BOOL) readMapBeginReturningKeyType:(SInt32 *)keyType - valueType:(SInt32 *)valueType - size:(SInt32 *)size - error:(NSError *__autoreleasing *)error -{ - return [_concreteProtocol readMapBeginReturningKeyType:keyType - valueType:valueType - size:size - error:error]; -} --(BOOL) readMapEnd:(NSError *__autoreleasing *)error -{ - return [_concreteProtocol readMapEnd:error]; -} - - --(BOOL) readSetBeginReturningElementType:(SInt32 *)elementType - size:(SInt32 *)size - error:(NSError *__autoreleasing *)error -{ - return [_concreteProtocol readSetBeginReturningElementType:elementType - size:size - error:error]; -} --(BOOL) readSetEnd:(NSError *__autoreleasing *)error -{ - return [_concreteProtocol readSetEnd:error]; -} - --(BOOL) readListBeginReturningElementType:(SInt32 *)elementType - size:(SInt32 *)size - error:(NSError *__autoreleasing *)error -{ - return [_concreteProtocol readListBeginReturningElementType:elementType - size:size - error:error]; -} --(BOOL) readListEnd:(NSError *__autoreleasing *)error -{ - return [_concreteProtocol readListEnd:error]; -} - --(BOOL) writeMessageBeginWithName:(NSString *)name - type:(SInt32)messageType - sequenceID:(SInt32)sequenceID - error:(NSError *__autoreleasing *)error -{ - return [_concreteProtocol writeMessageBeginWithName:name - type:messageType - sequenceID:sequenceID - error:error]; -} --(BOOL) writeMessageEnd:(NSError *__autoreleasing *)error -{ - return [_concreteProtocol writeMessageEnd:error]; -} - --(BOOL) writeStructBeginWithName:(NSString *)name error:(NSError *__autoreleasing *)error -{ - return [_concreteProtocol writeStructBeginWithName:name error:error]; -} --(BOOL) writeStructEnd:(NSError *__autoreleasing *)error -{ - return [_concreteProtocol writeStructEnd:error]; -} - --(BOOL) writeFieldBeginWithName:(NSString *)name - type:(SInt32)fieldType - fieldID:(SInt32)fieldID - error:(NSError *__autoreleasing *)error -{ - return [_concreteProtocol writeFieldBeginWithName:name - type:fieldType - fieldID:fieldID - error:error]; -} - --(BOOL) writeI32:(SInt32)value error:(NSError *__autoreleasing *)error -{ - return [_concreteProtocol writeI32:value error:error]; -} - --(BOOL) writeI64:(SInt64)value error:(NSError *__autoreleasing *)error -{ - return [_concreteProtocol writeI64:value error:error]; -} - --(BOOL) writeI16:(SInt16)value error:(NSError *__autoreleasing *)error -{ - return [_concreteProtocol writeI16:value error:error]; -} - --(BOOL) writeByte:(UInt8)value error:(NSError *__autoreleasing *)error -{ - return [_concreteProtocol writeByte:value error:error]; -} - --(BOOL) writeString:(NSString *)value error:(NSError *__autoreleasing *)error -{ - return [_concreteProtocol writeString:value error:error]; -} - --(BOOL) writeDouble:(double)value error:(NSError *__autoreleasing *)error -{ - return [_concreteProtocol writeDouble:value error:error]; -} - --(BOOL) writeBool:(BOOL)value error:(NSError *__autoreleasing *)error -{ - return [_concreteProtocol writeBool:value error:error]; -} - --(BOOL) writeBinary:(NSData *)data error:(NSError *__autoreleasing *)error -{ - return [_concreteProtocol writeBinary:data error:error]; -} - --(BOOL) writeFieldStop:(NSError *__autoreleasing *)error -{ - return [_concreteProtocol writeFieldStop:error]; -} - --(BOOL) writeFieldEnd:(NSError *__autoreleasing *)error -{ - return [_concreteProtocol writeFieldEnd:error]; -} - --(BOOL) writeMapBeginWithKeyType:(SInt32)keyType - valueType:(SInt32)valueType - size:(SInt32)size - error:(NSError *__autoreleasing *)error -{ - return [_concreteProtocol writeMapBeginWithKeyType:keyType - valueType:valueType - size:size - error:error]; -} - --(BOOL) writeMapEnd:(NSError *__autoreleasing *)error -{ - return [_concreteProtocol writeMapEnd:error]; -} - --(BOOL) writeSetBeginWithElementType:(SInt32)elementType - size:(SInt32)size - error:(NSError *__autoreleasing *)error -{ - return [_concreteProtocol writeSetBeginWithElementType:elementType size:size error:error]; -} - --(BOOL) writeSetEnd:(NSError *__autoreleasing *)error -{ - return [_concreteProtocol writeSetEnd:error]; -} - --(BOOL) writeListBeginWithElementType:(SInt32)elementType - size:(SInt32)size - error:(NSError *__autoreleasing *)error -{ - return [_concreteProtocol writeListBeginWithElementType:elementType size:size error:error]; -} - --(BOOL) writeListEnd:(NSError *__autoreleasing *)error -{ - return [_concreteProtocol writeListEnd:error]; -} - -@end diff --git a/lib/cocoa/src/protocol/TProtocolError.h b/lib/cocoa/src/protocol/TProtocolError.h deleted file mode 100644 index ab0bc40dab0..00000000000 --- a/lib/cocoa/src/protocol/TProtocolError.h +++ /dev/null @@ -1,76 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -#import "TError.h" - - -extern NSString *TProtocolErrorDomain; - -typedef NS_ENUM (int, TProtocolError) { - TProtocolErrorUnknown = 0, - TProtocolErrorInvalidData = 1, - TProtocolErrorNegativeSize = 2, - TProtocolErrorSizeLimit = 3, - TProtocolErrorBadVersion = 4, - TProtocolErrorNotImplemented = 5, - TProtocolErrorDepthLimit = 6, -}; - - -typedef NS_ENUM(int, TProtocolExtendedError) { - TProtocolExtendedErrorMissingRequiredField = 1001, - TProtocolExtendedErrorUnexpectedType = 1002, - TProtocolExtendedErrorMismatchedProtocol = 1003, -}; - -extern NSString *TProtocolErrorExtendedErrorKey; -extern NSString *TProtocolErrorFieldNameKey; -extern NSString *TProtocolErrorExpectedIdKey; -extern NSString *TProtocolErrorExpectedVersionKey; -extern NSString *TProtocolErrorTypeKey; -extern NSString *TProtocolErrorSourceLineKey; -extern NSString *TProtocolErrorSourceFileKey; -extern NSString *TProtocolErrorSourceMethodKey; -extern NSString *TProtocolErrorMessageNameKey; - - -#define PROTOCOL_ERROR(ret, err, ...) \ - if (error) { \ - *error = [NSError errorWithDomain:TProtocolErrorDomain \ - code:TProtocolError ## err \ - userInfo:@{NSLocalizedDescriptionKey: [NSString stringWithFormat:__VA_ARGS__], \ - @"SourceFile": [NSString stringWithUTF8String:__FILE__], \ - @"SourceLine": @(__LINE__), \ - @"SourceFunction": [NSString stringWithUTF8String:__PRETTY_FUNCTION__], \ - @"Message": self.currentMessageName ? self.currentMessageName : @""}]; \ - } \ - return ret - -#define PROTOCOL_TRANSPORT_ERROR(ret, errorPtr, ...) \ - if (errorPtr) { \ - *error = [NSError errorWithDomain:TProtocolErrorDomain \ - code:TProtocolErrorUnknown \ - userInfo:@{NSLocalizedDescriptionKey: [[NSString stringWithFormat:__VA_ARGS__] stringByAppendingFormat:@": %@", [(*errorPtr) localizedDescription]], \ - TProtocolErrorSourceFileKey: [NSString stringWithUTF8String:__FILE__], \ - TProtocolErrorSourceLineKey: @(__LINE__), \ - TProtocolErrorSourceMethodKey: [NSString stringWithUTF8String:__PRETTY_FUNCTION__], \ - TProtocolErrorMessageNameKey: self.currentMessageName ? self.currentMessageName : @"", \ - NSUnderlyingErrorKey: *errorPtr}]; \ - } \ - return ret diff --git a/lib/cocoa/src/protocol/TProtocolError.m b/lib/cocoa/src/protocol/TProtocolError.m deleted file mode 100644 index 953673b0afe..00000000000 --- a/lib/cocoa/src/protocol/TProtocolError.m +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -#import "TProtocolError.h" - - -NSString *TProtocolErrorDomain = @"TProtocolErrorDomain"; - -NSString *TProtocolErrorExtendedErrorKey = @"extendedError"; -NSString *TProtocolErrorFieldNameKey = @"field"; -NSString *TProtocolErrorExpectedIdKey = @"expectedId"; -NSString *TProtocolErrorExpectedVersionKey = @"expectedVersion"; -NSString *TProtocolErrorTypeKey = @"type"; -NSString *TProtocolErrorSourceLineKey = @"sourceLine"; -NSString *TProtocolErrorSourceFileKey = @"sourceFile"; -NSString *TProtocolErrorSourceMethodKey = @"sourceMethod"; -NSString *TProtocolErrorMessageNameKey = @"messageName"; diff --git a/lib/cocoa/src/protocol/TProtocolFactory.h b/lib/cocoa/src/protocol/TProtocolFactory.h deleted file mode 100644 index a022a7f9fe6..00000000000 --- a/lib/cocoa/src/protocol/TProtocolFactory.h +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -#import -#import "TProtocol.h" -#import "TTransport.h" - -NS_ASSUME_NONNULL_BEGIN - - -@protocol TProtocolFactory - -@property (readonly, nonatomic) NSString *protocolName; - --(id) newProtocolOnTransport:(id)transport; - -@end - - -NS_ASSUME_NONNULL_END diff --git a/lib/cocoa/src/protocol/TProtocolUtil.h b/lib/cocoa/src/protocol/TProtocolUtil.h deleted file mode 100644 index 82510cfca82..00000000000 --- a/lib/cocoa/src/protocol/TProtocolUtil.h +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -#import "TProtocol.h" -#import "TTransport.h" - -NS_ASSUME_NONNULL_BEGIN - - -@interface TProtocolUtil : NSObject - -+(BOOL) skipType:(int)type onProtocol:(id )protocol error:(NSError **)error; - -@end; - - -NS_ASSUME_NONNULL_END \ No newline at end of file diff --git a/lib/cocoa/src/protocol/TProtocolUtil.m b/lib/cocoa/src/protocol/TProtocolUtil.m deleted file mode 100644 index c0d65aceaf1..00000000000 --- a/lib/cocoa/src/protocol/TProtocolUtil.m +++ /dev/null @@ -1,171 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -#import "TProtocolUtil.h" - -@implementation TProtocolUtil - -+(BOOL) skipType:(int)type onProtocol:(id )protocol error:(NSError **)error -{ - switch (type) { - case TTypeBOOL: { - BOOL val; - if (![protocol readBool:&val error:error]) { - return NO; - } - } - break; - - case TTypeBYTE: { - UInt8 val; - if (![protocol readByte:&val error:error]) { - return NO; - } - } - break; - - case TTypeI16: { - SInt16 val; - if (![protocol readI16:&val error:error]) { - return NO; - } - } - break; - - case TTypeI32: { - SInt32 val; - if (![protocol readI32:&val error:error]) { - return NO; - } - } - break; - - case TTypeI64: { - SInt64 val; - if (![protocol readI64:&val error:error]) { - return NO; - } - } - break; - - case TTypeDOUBLE: { - double val; - if (![protocol readDouble:&val error:error]) { - return NO; - } - } - break; - - case TTypeSTRING: { - NSString *val; - if (![protocol readString:&val error:error]) { - return NO; - } - } - break; - - case TTypeSTRUCT: { - if (![protocol readStructBeginReturningName:NULL error:error]) { - return NO; - } - while (true) { - SInt32 fieldType; - if (![protocol readFieldBeginReturningName:nil type:&fieldType fieldID:nil error:error]) { - return NO; - } - if (fieldType == TTypeSTOP) { - break; - } - if (![self skipType:fieldType onProtocol:protocol error:error]) { - return NO; - } - if (![protocol readFieldEnd:error]) { - return NO; - } - } - if (![protocol readStructEnd:error]) { - return NO; - } - } - break; - - case TTypeMAP: { - SInt32 keyType; - SInt32 valueType; - SInt32 size; - if (![protocol readMapBeginReturningKeyType:&keyType valueType:&valueType size:&size error:error]) { - return NO; - } - int i; - for (i = 0; i < size; i++) { - if (![TProtocolUtil skipType:keyType onProtocol:protocol error:error]) { - return NO; - } - if (![TProtocolUtil skipType:valueType onProtocol:protocol error:error]) { - return NO; - } - } - if (![protocol readMapEnd:error]) { - return NO; - } - } - break; - - case TTypeSET: { - SInt32 elemType; - SInt32 size; - if (![protocol readSetBeginReturningElementType:&elemType size:&size error:error]) { - return NO; - } - int i; - for (i = 0; i < size; i++) { - if (![TProtocolUtil skipType:elemType onProtocol:protocol error:error]) { - return NO; - } - } - if (![protocol readSetEnd:error]) { - return NO; - } - } - break; - - case TTypeLIST: { - SInt32 elemType; - SInt32 size; - if (![protocol readListBeginReturningElementType:&elemType size:&size error:error]) { - return NO; - } - int i; - for (i = 0; i < size; i++) { - if (![TProtocolUtil skipType:elemType onProtocol:protocol error:error]) { - return NO; - } - } - if (![protocol readListEnd:error]) { - return NO; - } - } - break; - - } - - return YES; -} - -@end diff --git a/lib/cocoa/src/server/TSocketServer.h b/lib/cocoa/src/server/TSocketServer.h deleted file mode 100644 index 95b0d3c1916..00000000000 --- a/lib/cocoa/src/server/TSocketServer.h +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -#import -#import "TProtocolFactory.h" -#import "TProcessorFactory.h" - -#if !TARGET_OS_IPHONE -#import -#else -#import -#endif - -NS_ASSUME_NONNULL_BEGIN - - -extern NSString *const TSocketServerClientConnectionFinished; -extern NSString *const TSocketServerProcessorKey; -extern NSString *const TSockerServerTransportKey; - - -@interface TSocketServer : NSObject - --(instancetype) initWithPort:(int)port - protocolFactory:(id )protocolFactory - processorFactory:(id )processorFactory; - -- (instancetype) initWithPath: (NSString *) path - protocolFactory: (id ) protocolFactory - processorFactory: (id ) processorFactory; - -@end - - -NS_ASSUME_NONNULL_END diff --git a/lib/cocoa/src/server/TSocketServer.m b/lib/cocoa/src/server/TSocketServer.m deleted file mode 100644 index 09b603cf4ad..00000000000 --- a/lib/cocoa/src/server/TSocketServer.m +++ /dev/null @@ -1,239 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -#import -#import "TSocketServer.h" -#import "TNSFileHandleTransport.h" -#import "TProtocol.h" -#import "TTransportError.h" - -#import -#include -#include - - -NSString *const TSocketServerClientConnectionFinished = @"TSocketServerClientConnectionFinished"; -NSString *const TSocketServerProcessorKey = @"TSocketServerProcessor"; -NSString *const TSockerServerTransportKey = @"TSockerServerTransport"; - - -@interface TSocketServer () - -@property(strong, nonatomic) id inputProtocolFactory; -@property(strong, nonatomic) id outputProtocolFactory; -@property(strong, nonatomic) id processorFactory; -@property(strong, nonatomic) NSFileHandle *socketFileHandle; -@property(strong, nonatomic) dispatch_queue_t processingQueue; -@property(strong, nonatomic) NSString *domainSocketPath; - -@end - - -@implementation TSocketServer - --(instancetype) initWithSocket:(CFSocketRef)socket - protocolFactory:(id )protocolFactory - processorFactory:(id )processorFactory; -{ - self = [super init]; - - _inputProtocolFactory = protocolFactory; - _outputProtocolFactory = protocolFactory; - _processorFactory = processorFactory; - - dispatch_queue_attr_t processingQueueAttr = - dispatch_queue_attr_make_with_qos_class(DISPATCH_QUEUE_CONCURRENT, QOS_CLASS_BACKGROUND, 0); - - _processingQueue = dispatch_queue_create("TSocketServer.processing", processingQueueAttr); - - // create a socket. - int fd = CFSocketGetNative(socket); - - // wrap it in a file handle so we can get messages from it - _socketFileHandle = [[NSFileHandle alloc] initWithFileDescriptor:fd - closeOnDealloc:YES]; - - // throw away our socket - CFSocketInvalidate(socket); - CFRelease(socket); - - // register for notifications of accepted incoming connections - [[NSNotificationCenter defaultCenter] addObserver:self - selector:@selector(connectionAccepted:) - name:NSFileHandleConnectionAcceptedNotification - object:_socketFileHandle]; - - // tell socket to listen - [_socketFileHandle acceptConnectionInBackgroundAndNotify]; - - return self; -} - -- (id) initWithPort: (int) port - protocolFactory: (id ) protocolFactory - processorFactory: (id ) processorFactory -{ - CFSocketRef socket = [[self class] createSocketWithPort:port]; - if (socket == NULL) { - return nil; - } - - if (self = [self initWithSocket:socket protocolFactory:protocolFactory processorFactory:processorFactory]) { - NSLog(@"TSocketServer: Listening on TCP port %d", port); - } - return self; -} - - -+(CFSocketRef) createSocketWithPort:(int)port -{ - CFSocketRef socket = CFSocketCreate(kCFAllocatorDefault, PF_INET, SOCK_STREAM, IPPROTO_TCP, 0, NULL, NULL); - if (socket) { - CFSocketSetSocketFlags(socket, CFSocketGetSocketFlags(socket) & ~kCFSocketCloseOnInvalidate); - int fd = CFSocketGetNative(socket); - int yes = 1; - setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (void *)&yes, sizeof(yes)); - - struct sockaddr_in addr; - memset(&addr, 0, sizeof(addr)); - addr.sin_len = sizeof(addr); - addr.sin_family = AF_INET; - addr.sin_port = htons(port); - addr.sin_addr.s_addr = htonl(INADDR_ANY); - NSData *address = [NSData dataWithBytes:&addr length:sizeof(addr)]; - if (CFSocketSetAddress(socket, (__bridge CFDataRef)address) != kCFSocketSuccess) { - CFSocketInvalidate(socket); - CFRelease(socket); - NSLog(@"TSocketServer: Could not bind to address"); - return NULL; - } - - return socket; - } - else { - NSLog(@"TSocketServer: No server socket"); - return NULL; - } -} - -- (id) initWithPath: (NSString *) path - protocolFactory: (id ) protocolFactory - processorFactory: (id ) processorFactory -{ - _domainSocketPath = path; - CFSocketRef socket = [[self class] createSocketWithPath:path]; - if (socket == NULL) { - return nil; - } - - if (self = [self initWithSocket:socket protocolFactory:protocolFactory processorFactory:processorFactory]) { - NSLog(@"TSocketServer: Listening on path %@", path); - } - return self; -} - -+ (CFSocketRef) createSocketWithPath: (NSString *) path -{ - CFSocketRef socket = CFSocketCreate(kCFAllocatorDefault, PF_LOCAL, SOCK_STREAM, IPPROTO_IP, 0, NULL, NULL); - if (socket) { - CFSocketSetSocketFlags(socket, CFSocketGetSocketFlags(socket) & ~kCFSocketCloseOnInvalidate); - int fd = CFSocketGetNative(socket); - int yes = 1; - setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (void *)&yes, sizeof(yes)); - - size_t nullTerminatedPathLength = path.length + 1; - struct sockaddr_un addr; - if (nullTerminatedPathLength> sizeof(addr.sun_path)) { - NSLog(@"TSocketServer: Unable to create socket at path %@. Path is too long.", path); - return NULL; - } - - addr.sun_family = AF_LOCAL; - memcpy(addr.sun_path, path.UTF8String, nullTerminatedPathLength); - addr.sun_len = SUN_LEN(&addr); - - NSData *address = [NSData dataWithBytes:&addr length:sizeof(addr)]; - if (CFSocketSetAddress(socket, (__bridge CFDataRef)address) != kCFSocketSuccess) { - CFSocketInvalidate(socket); - CFRelease(socket); - NSLog(@"TSocketServer: Could not bind to address"); - return NULL; - } - - return socket; - } else { - NSLog(@"TSocketServer: No server socket"); - return NULL; - } -} - --(void) dealloc -{ - [[NSNotificationCenter defaultCenter] removeObserver:self]; - - if (_domainSocketPath != nil) { - unlink(_domainSocketPath.UTF8String); - } -} - - --(void) connectionAccepted:(NSNotification *)notification -{ - NSFileHandle *socket = [notification.userInfo objectForKey:NSFileHandleNotificationFileHandleItem]; - - // Now that we have a client connected, handle request on queue - dispatch_async(_processingQueue, ^{ - - [self handleClientConnection:socket]; - - }); - - // Continue accepting connections - [_socketFileHandle acceptConnectionInBackgroundAndNotify]; -} - - --(void) handleClientConnection:(NSFileHandle *)clientSocket -{ - @autoreleasepool { - - TNSFileHandleTransport *transport = [[TNSFileHandleTransport alloc] initWithFileHandle:clientSocket]; - id processor = [_processorFactory processorForTransport:transport]; - - id inProtocol = [_inputProtocolFactory newProtocolOnTransport:transport]; - id outProtocol = [_outputProtocolFactory newProtocolOnTransport:transport]; - - NSError *error; - if (![processor processOnInputProtocol:inProtocol outputProtocol:outProtocol error:&error]) { - // Handle error - NSLog(@"Error processing request: %@", error); - } - - dispatch_async(dispatch_get_main_queue(), ^{ - - [NSNotificationCenter.defaultCenter postNotificationName:TSocketServerClientConnectionFinished - object:self - userInfo:@{TSocketServerProcessorKey: processor, - TSockerServerTransportKey: transport}]; - }); - - } -} - -@end diff --git a/lib/cocoa/src/transport/TAsyncTransport.h b/lib/cocoa/src/transport/TAsyncTransport.h deleted file mode 100644 index bab4fbdcd2c..00000000000 --- a/lib/cocoa/src/transport/TAsyncTransport.h +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -#import "TTransport.h" - -NS_ASSUME_NONNULL_BEGIN - - -@protocol TAsyncTransport; - - -@protocol TAsyncTransportFactory - --(id) newTransport; - -@end - - -typedef void (^TAsyncCompletionBlock)(); -typedef void (^TAsyncFailureBlock)(NSError * __nonnull); - - -@protocol TAsyncTransport - --(void) flushWithCompletion:(TAsyncCompletionBlock)completed failure:(TAsyncFailureBlock)failure; - -@end - - -NS_ASSUME_NONNULL_END \ No newline at end of file diff --git a/lib/cocoa/src/transport/TFramedTransport.h b/lib/cocoa/src/transport/TFramedTransport.h deleted file mode 100644 index ea68ac44d5c..00000000000 --- a/lib/cocoa/src/transport/TFramedTransport.h +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -#import -#import "TTransport.h" - -NS_ASSUME_NONNULL_BEGIN - - -@interface TFramedTransport : NSObject - --(id) initWithTransport:(id )transport; - -@end - - -NS_ASSUME_NONNULL_END diff --git a/lib/cocoa/src/transport/TFramedTransport.m b/lib/cocoa/src/transport/TFramedTransport.m deleted file mode 100644 index 4db65c41c92..00000000000 --- a/lib/cocoa/src/transport/TFramedTransport.m +++ /dev/null @@ -1,180 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -#import "TFramedTransport.h" -#import "TTransportError.h" - -#define HEADER_SIZE 4 -#define INIT_FRAME_SIZE 1024 - - -@interface TFramedTransport () - -@property(strong, nonatomic) id transport; -@property(strong, nonatomic) NSMutableData *writeBuffer; -@property(strong, nonatomic) NSMutableData *readBuffer; -@property(assign, nonatomic) NSUInteger readOffset; - -@end - - -@implementation TFramedTransport - --(id) initWithTransport:(id )aTransport -{ - if ((self = [self init])) { - _transport = aTransport; - _readBuffer = nil; - _readOffset = 0; - _writeBuffer = [NSMutableData dataWithLength:HEADER_SIZE]; - } - return self; -} - --(BOOL) flush:(NSError **)error -{ - int len = (int)[_writeBuffer length]; - int data_len = len - HEADER_SIZE; - if (data_len < 0) { - if (error) { - *error = [NSError errorWithDomain:TTransportErrorDomain - code:TTransportErrorUnknown - userInfo:@{}]; - } - return NO; - } - - UInt8 i32rd[HEADER_SIZE]; - i32rd[0] = (UInt8)(0xff & (data_len >> 24)); - i32rd[1] = (UInt8)(0xff & (data_len >> 16)); - i32rd[2] = (UInt8)(0xff & (data_len >> 8)); - i32rd[3] = (UInt8)(0xff & (data_len)); - - // should we make a copy of the writeBuffer instead? Better for threaded - // operations! - [_writeBuffer replaceBytesInRange:NSMakeRange(0, HEADER_SIZE) - withBytes:i32rd length:HEADER_SIZE]; - - if (![_transport write:_writeBuffer.mutableBytes offset:0 length:len error:error]) { - return NO; - } - - if (![_transport flush:error]) { - return NO; - } - - _writeBuffer.length = HEADER_SIZE; - - return YES; -} - --(BOOL) write:(const UInt8 *)data offset:(UInt32)offset length:(UInt32)length error:(NSError *__autoreleasing *)error -{ - [_writeBuffer appendBytes:data+offset length:length]; - - return YES; -} - --(BOOL) readAll:(UInt8 *)outBuffer offset:(UInt32)outBufferOffset length:(UInt32)length error:(NSError *__autoreleasing *)error -{ - UInt32 got = [self readAvail:outBuffer offset:outBufferOffset maxLength:length error:error]; - if (got != length) { - - // Report underflow only if readAvail didn't report error already - if (error && !*error) { - *error = [NSError errorWithDomain:TTransportErrorDomain - code:TTransportErrorEndOfFile - userInfo:nil]; - } - - return NO; - } - - return YES; -} - --(UInt32) readAvail:(UInt8 *)outBuffer offset:(UInt32)outBufferOffset maxLength:(UInt32)length error:(NSError *__autoreleasing *)error -{ - UInt32 got = 0; - while (got < length) { - - NSUInteger avail = _readBuffer.length - _readOffset; - if (avail == 0) { - if (![self readFrame:error]) { - return 0; - } - avail = _readBuffer.length; - } - - NSRange range; - range.location = _readOffset; - range.length = MIN(length - got, avail); - - [_readBuffer getBytes:outBuffer+outBufferOffset+got range:range]; - _readOffset += range.length; - got += range.length; - } - - return got; -} - --(BOOL) readFrame:(NSError **)error -{ - UInt8 i32rd[HEADER_SIZE]; - if (![_transport readAll:i32rd offset:0 length:HEADER_SIZE error:error]) { - return NO; - } - - SInt32 size = - ((i32rd[0] & 0xff) << 24) | - ((i32rd[1] & 0xff) << 16) | - ((i32rd[2] & 0xff) << 8) | - ((i32rd[3] & 0xff)); - - if (_readBuffer == nil) { - - _readBuffer = [NSMutableData dataWithLength:size]; - - } - else { - - SInt32 len = (SInt32)_readBuffer.length; - if (len >= size) { - - _readBuffer.length = size; - - } - else { - - // increase length of data buffer - [_readBuffer increaseLengthBy:size-len]; - - } - - } - - // copy into internal memory buffer - if (![_transport readAll:_readBuffer.mutableBytes offset:0 length:size error:error]) { - return NO; - } - - return YES; -} - -@end diff --git a/lib/cocoa/src/transport/THTTPSessionTransport.h b/lib/cocoa/src/transport/THTTPSessionTransport.h deleted file mode 100644 index 003499b80a6..00000000000 --- a/lib/cocoa/src/transport/THTTPSessionTransport.h +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -#import -#import "TAsyncTransport.h" - -NS_ASSUME_NONNULL_BEGIN - - -typedef NSError *__nullable (^THTTPSessionTransportResponseValidateBlock) (NSHTTPURLResponse *response, NSData *responseData); - - -@interface THTTPSessionTransportFactory : NSObject - -@property (strong, nonatomic) THTTPSessionTransportResponseValidateBlock responseValidate; - -+(void) setupDefaultsForSessionConfiguration:(NSURLSessionConfiguration *)config - withProtocolName:(NSString *)protocolName; - --(id) initWithSession:(NSURLSession *)session - URL:(NSURL *)aURL; - -@end - - -@interface THTTPSessionTransport : NSObject - -@end - - -NS_ASSUME_NONNULL_END diff --git a/lib/cocoa/src/transport/THTTPSessionTransport.m b/lib/cocoa/src/transport/THTTPSessionTransport.m deleted file mode 100644 index c10b7fc3f6b..00000000000 --- a/lib/cocoa/src/transport/THTTPSessionTransport.m +++ /dev/null @@ -1,268 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -#import "THTTPSessionTransport.h" -#import "TTransportError.h" - - -@interface THTTPSessionTransportFactory () - -@property (strong, nonatomic) NSURLSession *session; -@property (strong, nonatomic) NSURL *url; - -@end - - -@interface THTTPSessionTransport () - -@property (strong, nonatomic) THTTPSessionTransportFactory *factory; -@property (strong, nonatomic) NSMutableData *requestData; -@property (strong, nonatomic) NSData *responseData; -@property (assign, nonatomic) NSUInteger responseDataOffset; - --(instancetype) initWithFactory:(THTTPSessionTransportFactory *)factory; - -@end - - -@implementation THTTPSessionTransportFactory - -+(void) setupDefaultsForSessionConfiguration:(NSURLSessionConfiguration *)config withProtocolName:(NSString *)protocolName -{ - NSString *thriftContentType = @"application/x-thrift"; - if (protocolName.length) { - thriftContentType = [thriftContentType stringByAppendingFormat:@"; p=%@", protocolName]; - } - - config.requestCachePolicy = NSURLRequestReloadIgnoringCacheData; - config.HTTPShouldUsePipelining = YES; - config.HTTPShouldSetCookies = NO; - config.URLCache = nil; - config.HTTPAdditionalHeaders = @{@"Content-Type":thriftContentType, - @"Accept":thriftContentType, - @"User-Agent":@"Thrift/Cocoa (Session)"}; -} - - --(id) initWithSession:(NSURLSession *)session URL:(NSURL *)url -{ - self = [super init]; - if (self) { - _session = session; - _url = url; - } - - return self; -} - --(id) newTransport -{ - return [[THTTPSessionTransport alloc] initWithFactory:self]; -} - --(NSURLSessionDataTask *) taskWithRequest:(NSURLRequest *)request - completionHandler:(void (^)(NSData *data, NSURLResponse *response, NSError *error))completionHandler - error:(NSError *__autoreleasing *)error -{ - NSURLSessionDataTask *newTask = [_session dataTaskWithRequest:request completionHandler:completionHandler]; - if (!newTask) { - if (error) { - *error = [NSError errorWithDomain:TTransportErrorDomain - code:TTransportErrorUnknown - userInfo:@{NSLocalizedDescriptionKey:@"Failed to create session data task"}]; - } - return nil; - } - - return newTask; -} - --(NSError *) validateResponse:(NSHTTPURLResponse *)response data:(NSData *)data -{ - if (_responseValidate) { - return _responseValidate(response, data); - } - return nil; -} - -@end - - - -@implementation THTTPSessionTransport - --(instancetype) initWithFactory:(THTTPSessionTransportFactory *)factory -{ - self = [super init]; - if (self) { - _factory = factory; - } - return self; -} - --(BOOL) readAll:(UInt8 *)outBuffer offset:(UInt32)outBufferOffset length:(UInt32)length error:(NSError *__autoreleasing *)error -{ - UInt32 got = [self readAvail:outBuffer offset:outBufferOffset maxLength:length error:error]; - if (got != length) { - - // Report underflow only if readAvail didn't report error already - if (error && !*error) { - *error = [NSError errorWithDomain:TTransportErrorDomain - code:TTransportErrorEndOfFile - userInfo:nil]; - } - - return NO; - } - - return YES; -} - --(UInt32) readAvail:(UInt8 *)outBuffer offset:(UInt32)outBufferOffset maxLength:(UInt32)maxLength error:(NSError *__autoreleasing *)error -{ - NSUInteger avail = _responseData.length - _responseDataOffset; - - NSRange range; - range.location = _responseDataOffset; - range.length = MIN(maxLength, avail); - - [_responseData getBytes:outBuffer+outBufferOffset range:range]; - _responseDataOffset += range.length; - - return (UInt32)range.length; -} - --(BOOL) write:(const UInt8 *)data offset:(UInt32)offset length:(UInt32)length error:(NSError *__autoreleasing *)error -{ - if (!_requestData) { - _requestData = [NSMutableData dataWithCapacity:256]; - } - - [_requestData appendBytes:data+offset length:length]; - - return YES; -} - --(void) flushWithCompletion:(TAsyncCompletionBlock)completed failure:(TAsyncFailureBlock)failure -{ - NSError *error; - - NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:_factory.url]; - request.HTTPMethod = @"POST"; - request.HTTPBody = _requestData; - - _requestData = nil; - - NSURLSessionDataTask *task = [_factory taskWithRequest:request completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) { - - // Check response type - if (!error && ![response isKindOfClass:NSHTTPURLResponse.class]) { - - error = [NSError errorWithDomain:TTransportErrorDomain - code:TTransportErrorUnknown - userInfo:@{TTransportErrorHttpErrorKey: @(THttpTransportErrorInvalidResponse)}]; - - } - - // Check status code - NSHTTPURLResponse *httpResponse = (id)response; - if (!error && httpResponse.statusCode != 200) { - - THttpTransportError code; - if (httpResponse.statusCode == 401) { - code = THttpTransportErrorAuthentication; - } - else { - code = THttpTransportErrorInvalidStatus; - } - - error = [NSError errorWithDomain:TTransportErrorDomain - code:TTransportErrorUnknown - userInfo:@{TTransportErrorHttpErrorKey: @(code), - @"statusCode":@(httpResponse.statusCode)}]; - } - - // Allow factory to check - if (!error) { - error = [_factory validateResponse:httpResponse data:data]; - } - - _responseDataOffset = 0; - - if (error) { - - _responseData = nil; - - failure(error); - - } - else { - - if (data == nil) { - data = [NSData data]; - } - - _responseData = data; - - completed(self); - } - - } error:&error]; - - if (!task) { - failure(error); - return; - } - - [task resume]; -} - --(BOOL) flush:(NSError *__autoreleasing *)error -{ - dispatch_semaphore_t completed = dispatch_semaphore_create(0); - - __block BOOL result; - __block NSError *internalError; - - [self flushWithCompletion:^(id < TAsyncTransport > transport) { - - result = YES; - - dispatch_semaphore_signal(completed); - - } failure:^(NSError *error) { - - internalError = error; - - result = NO; - - dispatch_semaphore_signal(completed); - - }]; - - dispatch_semaphore_wait(completed, DISPATCH_TIME_FOREVER); - - if (error) { - *error = internalError; - } - - return result; -} - -@end diff --git a/lib/cocoa/src/transport/THTTPTransport.h b/lib/cocoa/src/transport/THTTPTransport.h deleted file mode 100644 index 3c35daf9e75..00000000000 --- a/lib/cocoa/src/transport/THTTPTransport.h +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -#import -#import "TTransport.h" - -NS_ASSUME_NONNULL_BEGIN - - -@interface THTTPTransport : NSObject - --(id) initWithURL:(NSURL *)aURL; - --(id) initWithURL:(NSURL *)aURL - userAgent:(nullable NSString *)userAgent - timeout:(int)timeout; - --(void) setURL:(NSURL *)aURL; - -@end - - -NS_ASSUME_NONNULL_END diff --git a/lib/cocoa/src/transport/THTTPTransport.m b/lib/cocoa/src/transport/THTTPTransport.m deleted file mode 100644 index e4046c6ca23..00000000000 --- a/lib/cocoa/src/transport/THTTPTransport.m +++ /dev/null @@ -1,182 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -#import "THTTPTransport.h" -#import "TTransportError.h" - - -@interface THTTPTransport () - -@property (strong, nonatomic) NSURL *url; -@property (strong, nonatomic) NSMutableURLRequest *request; -@property (strong, nonatomic) NSMutableData *requestData; -@property (strong, nonatomic) NSData *responseData; -@property (assign, nonatomic) NSUInteger responseDataOffset; -@property (strong, nonatomic) NSString *userAgent; -@property (assign, nonatomic) NSTimeInterval timeout; - -@end - - -@implementation THTTPTransport - --(void) setupRequest -{ - // set up our request object that we'll use for each request - _request = [[NSMutableURLRequest alloc] initWithURL:_url]; - [_request setHTTPMethod:@"POST"]; - [_request setValue:@"application/x-thrift" forHTTPHeaderField:@"Content-Type"]; - [_request setValue:@"application/x-thrift" forHTTPHeaderField:@"Accept"]; - - NSString *userAgent = _userAgent; - if (!userAgent) { - userAgent = @"Thrift/Cocoa"; - } - [_request setValue:userAgent forHTTPHeaderField:@"User-Agent"]; - - [_request setCachePolicy:NSURLRequestReloadIgnoringCacheData]; - if (_timeout) { - [_request setTimeoutInterval:_timeout]; - } -} - - --(id) initWithURL:(NSURL *)aURL -{ - return [self initWithURL:aURL - userAgent:nil - timeout:0]; -} - - --(id) initWithURL:(NSURL *)aURL - userAgent:(NSString *)aUserAgent - timeout:(int)aTimeout -{ - self = [super init]; - if (!self) { - return nil; - } - - _timeout = aTimeout; - _userAgent = aUserAgent; - _url = aURL; - - [self setupRequest]; - - // create our request data buffer - _requestData = [[NSMutableData alloc] initWithCapacity:1024]; - - return self; -} - --(void) setURL:(NSURL *)aURL -{ - _url = aURL; - - [self setupRequest]; -} - --(BOOL) readAll:(UInt8 *)outBuffer offset:(UInt32)outBufferOffset length:(UInt32)length error:(NSError *__autoreleasing *)error -{ - UInt32 got = [self readAvail:outBuffer offset:outBufferOffset maxLength:length error:error]; - if (got != length) { - - // Report underflow only if readAvail didn't report error already - if (error && !*error) { - *error = [NSError errorWithDomain:TTransportErrorDomain - code:TTransportErrorEndOfFile - userInfo:nil]; - } - - return NO; - } - - return YES; -} - --(UInt32) readAvail:(UInt8 *)outBuffer offset:(UInt32)outBufferOffset maxLength:(UInt32)maxLength error:(NSError *__autoreleasing *)error -{ - NSUInteger avail = _responseData.length - _responseDataOffset; - - NSRange range; - range.location = _responseDataOffset; - range.length = MIN(maxLength, avail); - - [_responseData getBytes:outBuffer+outBufferOffset range:range]; - _responseDataOffset += range.length; - - return (UInt32)range.length; -} - --(BOOL) write:(const UInt8 *)data offset:(UInt32)offset length:(UInt32)length error:(NSError *__autoreleasing *)error -{ - [_requestData appendBytes:data+offset length:length]; - - return YES; -} - --(BOOL) flush:(NSError *__autoreleasing *)error -{ - [_request setHTTPBody:_requestData]; - - _responseDataOffset = 0; - - // make the HTTP request - NSURLResponse *response; - _responseData = [NSURLConnection sendSynchronousRequest:_request returningResponse:&response error:error]; - if (!_responseData) { - return NO; - } - - [_requestData setLength:0]; - - if (![response isKindOfClass:NSHTTPURLResponse.class]) { - if (error) { - *error = [NSError errorWithDomain:TTransportErrorDomain - code:TTransportErrorUnknown - userInfo:@{TTransportErrorHttpErrorKey: @(THttpTransportErrorInvalidResponse)}]; - } - return NO; - } - - NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)response; - if ([httpResponse statusCode] != 200) { - if (error) { - - THttpTransportError code; - if (httpResponse.statusCode == 401) { - code = THttpTransportErrorAuthentication; - } - else { - code = THttpTransportErrorInvalidStatus; - } - - *error = [NSError errorWithDomain:TTransportErrorDomain - code:TTransportErrorUnknown - userInfo:@{TTransportErrorHttpErrorKey: @(code), - @"statusCode":@(httpResponse.statusCode)}]; - } - return NO; - } - - return YES; -} - -@end diff --git a/lib/cocoa/src/transport/TMemoryBuffer.h b/lib/cocoa/src/transport/TMemoryBuffer.h deleted file mode 100644 index 6249d3209e4..00000000000 --- a/lib/cocoa/src/transport/TMemoryBuffer.h +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -#import -#import "TTransport.h" - -NS_ASSUME_NONNULL_BEGIN - - -@interface TMemoryBuffer : NSObject - --(NSData *) buffer; - --(id) initWithData:(NSData *)data; - --(id) initWithDataNoCopy:(NSMutableData *)data; - -@end - - -NS_ASSUME_NONNULL_END diff --git a/lib/cocoa/src/transport/TMemoryBuffer.m b/lib/cocoa/src/transport/TMemoryBuffer.m deleted file mode 100644 index ec19cc8082c..00000000000 --- a/lib/cocoa/src/transport/TMemoryBuffer.m +++ /dev/null @@ -1,121 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -#import "TMemoryBuffer.h" -#import "TTransportError.h" - - -#define GARBAGE_BUFFER_SIZE 4096 // 4KiB - - -@interface TMemoryBuffer () - -@property(strong, nonatomic) NSMutableData *buffer; -@property(assign, nonatomic) UInt32 bufferOffset; - -@end - - -@implementation TMemoryBuffer - --(id) init -{ - if ((self = [super init])) { - _buffer = [NSMutableData new]; - _bufferOffset = 0; - } - return self; -} - --(id) initWithData:(NSData *)data -{ - if (self = [super init]) { - _buffer = [data mutableCopy]; - _bufferOffset = 0; - } - return self; -} - --(id) initWithDataNoCopy:(NSMutableData *)data -{ - if (self = [super init]) { - _buffer = data; - _bufferOffset = 0; - } - return self; -} - --(BOOL) readAll:(UInt8 *)outBuffer offset:(UInt32)outBufferOffset length:(UInt32)length error:(NSError *__autoreleasing *)error -{ - UInt32 got = [self readAvail:outBuffer offset:outBufferOffset maxLength:length error:error]; - if (got != length) { - - // Report underflow only if readAvail didn't report error already - if (error && !*error) { - *error = [NSError errorWithDomain:TTransportErrorDomain - code:TTransportErrorEndOfFile - userInfo:nil]; - } - - return NO; - } - - return YES; -} - --(UInt32) readAvail:(UInt8 *)outBuffer offset:(UInt32)outBufferOffset maxLength:(UInt32)maxLength error:(NSError *__autoreleasing *)error -{ - UInt32 avail = (UInt32)_buffer.length - _bufferOffset; - if (avail == 0) { - return 0; - } - - NSRange range; - range.location = _bufferOffset; - range.length = MIN(maxLength, avail); - - [_buffer getBytes:outBuffer + outBufferOffset range:range]; - _bufferOffset += range.length; - - if (_bufferOffset >= GARBAGE_BUFFER_SIZE) { - [_buffer replaceBytesInRange:NSMakeRange(0, _bufferOffset) withBytes:NULL length:0]; - _bufferOffset = 0; - } - - return (UInt32)range.length; -} - --(BOOL) write:(const UInt8 *)inBuffer offset:(UInt32)inBufferOffset length:(UInt32)length error:(NSError *__autoreleasing *)error -{ - [_buffer appendBytes:inBuffer + inBufferOffset length:length]; - - return YES; -} - --(NSData *) buffer -{ - return _buffer; -} - --(BOOL) flush:(NSError *__autoreleasing *)error -{ - return YES; -} - -@end diff --git a/lib/cocoa/src/transport/TNSFileHandleTransport.h b/lib/cocoa/src/transport/TNSFileHandleTransport.h deleted file mode 100644 index db6edf3832d..00000000000 --- a/lib/cocoa/src/transport/TNSFileHandleTransport.h +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - - -#import -#import "TTransport.h" - -NS_ASSUME_NONNULL_BEGIN - - -@interface TNSFileHandleTransport : NSObject - --(id) initWithFileHandle:(NSFileHandle *)fileHandle; - --(id) initWithInputFileHandle:(NSFileHandle *)inputFileHandle - outputFileHandle:(NSFileHandle *)outputFileHandle; - - -@end - - -NS_ASSUME_NONNULL_END diff --git a/lib/cocoa/src/transport/TNSFileHandleTransport.m b/lib/cocoa/src/transport/TNSFileHandleTransport.m deleted file mode 100644 index c907f87da85..00000000000 --- a/lib/cocoa/src/transport/TNSFileHandleTransport.m +++ /dev/null @@ -1,118 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - - -#import "TNSFileHandleTransport.h" -#import "TTransportError.h" - - -@interface TNSFileHandleTransport () - -@property(strong, nonatomic) NSFileHandle *inputFileHandle; -@property(strong, nonatomic) NSFileHandle *outputFileHandle; - -@end - - -@implementation TNSFileHandleTransport - --(id) initWithFileHandle:(NSFileHandle *)fileHandle -{ - return [self initWithInputFileHandle:fileHandle - outputFileHandle:fileHandle]; -} - - --(id) initWithInputFileHandle:(NSFileHandle *)aInputFileHandle - outputFileHandle:(NSFileHandle *)aOutputFileHandle -{ - self = [super init]; - if (self) { - _inputFileHandle = aInputFileHandle; - _outputFileHandle = aOutputFileHandle; - } - return self; -} - - --(BOOL) readAll:(UInt8 *)buf offset:(UInt32)off length:(UInt32)len error:(NSError *__autoreleasing *)error -{ - UInt32 got = 0; - while (got < len) { - - NSData *d = [_inputFileHandle readDataOfLength:len-got]; - if (d.length == 0) { - if (error) { - *error = [NSError errorWithDomain:TTransportErrorDomain - code:TTransportErrorEndOfFile - userInfo:nil]; - } - return NO; - } - - [d getBytes:buf+got length:d.length]; - got += d.length; - } - return YES; -} - - --(UInt32) readAvail:(UInt8 *)buf offset:(UInt32)off maxLength:(UInt32)len error:(NSError *__autoreleasing *)error -{ - UInt32 got = 0; - while (got < len) { - - NSData *d = [_inputFileHandle readDataOfLength:len-got]; - if (d.length == 0) { - break; - } - - [d getBytes:buf+got length:d.length]; - got += d.length; - } - return got; -} - - --(BOOL) write:(const UInt8 *)data offset:(UInt32)offset length:(UInt32)length error:(NSError *__autoreleasing *)error -{ - void *pos = (void *)data + offset; - - @try { - [_outputFileHandle writeData:[NSData dataWithBytesNoCopy:pos length:length freeWhenDone:NO]]; - } - @catch (NSException *e) { - if (error) { - *error = [NSError errorWithDomain:TTransportErrorDomain - code:TTransportErrorNotOpen - userInfo:@{}]; - } - return NO; - } - - return YES; -} - - --(BOOL) flush:(NSError *__autoreleasing *)error -{ - return YES; -} - -@end diff --git a/lib/cocoa/src/transport/TNSStreamTransport.h b/lib/cocoa/src/transport/TNSStreamTransport.h deleted file mode 100644 index 54c48844471..00000000000 --- a/lib/cocoa/src/transport/TNSStreamTransport.h +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -#import -#import "TTransport.h" - -NS_ASSUME_NONNULL_BEGIN - - -@interface TNSStreamTransport : NSObject - -@property (strong, nonatomic) NSInputStream *input; -@property (strong, nonatomic) NSOutputStream *output; - --(id) initWithInputStream:(nullable NSInputStream *)input - outputStream:(nullable NSOutputStream *)output; - --(id) initWithInputStream:(NSInputStream *)input; - --(id) initWithOutputStream:(NSOutputStream *)output; - --(void) close; - -@end - - -NS_ASSUME_NONNULL_END diff --git a/lib/cocoa/src/transport/TNSStreamTransport.m b/lib/cocoa/src/transport/TNSStreamTransport.m deleted file mode 100644 index 18c41d3d9ca..00000000000 --- a/lib/cocoa/src/transport/TNSStreamTransport.m +++ /dev/null @@ -1,153 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -#import "TNSStreamTransport.h" -#import "TTransportError.h" - - -@interface TNSStreamTransport () -@end - - -@implementation TNSStreamTransport - --(id) initWithInputStream:(NSInputStream *)input - outputStream:(NSOutputStream *)output -{ - self = [super init]; - if (self) { - _input = input; - _output = output; - } - return self; -} - --(id) initWithInputStream:(NSInputStream *)input -{ - return [self initWithInputStream:input outputStream:nil]; -} - --(id) initWithOutputStream:(NSOutputStream *)output -{ - return [self initWithInputStream:nil outputStream:output]; -} - --(void) dealloc -{ - [self close]; -} - --(BOOL) readAll:(UInt8 *)buf offset:(UInt32)off length:(UInt32)len error:(NSError *__autoreleasing *)error -{ - UInt32 got = 0; - while (got < len) { - - UInt32 read = (UInt32)[_input read:buf+off+got maxLength:len-got]; - if (read <= 0) { - if (error) { - *error = [NSError errorWithDomain:TTransportErrorDomain - code:TTransportErrorNotOpen - userInfo:@{}]; - } - return NO; - } - - got += read; - } - - return YES; -} - - --(UInt32) readAvail:(UInt8 *)buf offset:(UInt32)off maxLength:(UInt32)len error:(NSError *__autoreleasing *)error -{ - UInt32 got = 0; - while (got < len) { - - UInt32 read = (UInt32)[_input read:buf+off+got maxLength:len-got]; - if (read <= 0) { - break; - } - - got += read; - } - - return got; -} - - --(BOOL) write:(const UInt8 *)data offset:(UInt32)offset length:(UInt32)length error:(NSError *__autoreleasing *)error -{ - UInt32 got = 0; - NSInteger total = 0; - while (got < length) { - - total = [_output write:data+offset+got maxLength:length-got]; - if (total == -1) { - if (error) { - *error = [NSError errorWithDomain:TTransportErrorDomain - code:TTransportErrorNotOpen - userInfo:@{}]; - } - return NO; - } - else if (total == 0) { - if (error) { - *error = [NSError errorWithDomain:TTransportErrorDomain - code:TTransportErrorEndOfFile - userInfo:@{}]; - } - return NO; - } - - got += total; - } - - return YES; -} - --(BOOL) flush:(NSError *__autoreleasing *)error -{ - return YES; -} - --(void) close -{ - NSInputStream *input = self.input; - if (input) { - // Close and reset inputstream - CFReadStreamSetProperty((__bridge CFReadStreamRef)(input), kCFStreamPropertyShouldCloseNativeSocket, kCFBooleanTrue); - [input setDelegate:nil]; - [input close]; - [input removeFromRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode]; - input = nil; - } - - NSOutputStream *output = self.output; - if (output) { - // Close and reset outputstream - CFWriteStreamSetProperty((__bridge CFWriteStreamRef)(output), kCFStreamPropertyShouldCloseNativeSocket, kCFBooleanTrue); - [output setDelegate:nil]; - [output close]; - [output removeFromRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode]; - output = nil; - } -} - -@end diff --git a/lib/cocoa/src/transport/TSSLSocketTransport.h b/lib/cocoa/src/transport/TSSLSocketTransport.h deleted file mode 100644 index b606c4a33ff..00000000000 --- a/lib/cocoa/src/transport/TSSLSocketTransport.h +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -#import -#import "TNSStreamTransport.h" - -NS_ASSUME_NONNULL_BEGIN - - -@interface TSSLSocketTransport : TNSStreamTransport - --(id) initWithHostname:(NSString *)hostname - port:(int)port - error:(NSError **)error; - --(BOOL) isOpen; - -@end - - -NS_ASSUME_NONNULL_END diff --git a/lib/cocoa/src/transport/TSSLSocketTransport.m b/lib/cocoa/src/transport/TSSLSocketTransport.m deleted file mode 100644 index 1b1214fe476..00000000000 --- a/lib/cocoa/src/transport/TSSLSocketTransport.m +++ /dev/null @@ -1,304 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -#import -#import -#import "TSSLSocketTransport.h" -#import "TSSLSocketTransportError.h" -#include -#include -#include - -#if !TARGET_OS_IPHONE -#import -#else -#import -#endif - -@interface TSSLSocketTransport () - -@property(strong, nonatomic) NSString *sslHostname; -@property(assign, nonatomic) int sd; - -@end - - -@implementation TSSLSocketTransport - --(id) initWithHostname:(NSString *)hostname - port:(int)port - error:(NSError **)error -{ - _sslHostname = hostname; - CFReadStreamRef readStream = NULL; - CFWriteStreamRef writeStream = NULL; - - - /* create a socket structure */ - struct sockaddr_in pin; - struct hostent *hp = NULL; - for (int i = 0; i < 10; i++) { - - - - if ((hp = gethostbyname([hostname UTF8String])) == NULL) { - NSLog(@"failed to resolve hostname %@", hostname); - herror("resolv"); - if (i == 9) { - if (error) { - *error = [NSError errorWithDomain:TSSLSocketTransportErrorDomain - code:TSSLSocketTransportErrorHostanameResolution - userInfo:nil]; - } - return nil; - } - [NSThread sleepForTimeInterval:0.2]; - } - else { - break; - } - } - - memset(&pin, 0, sizeof(pin)); - pin.sin_family = AF_INET; - memcpy(&pin.sin_addr, hp->h_addr, sizeof(struct in_addr)); - pin.sin_port = htons(port); - - /* create the socket */ - if ((_sd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) == -1) { - NSLog(@"failed to create socket for host %@:%d", hostname, port); - if (error) { - *error = [NSError errorWithDomain:TSSLSocketTransportErrorDomain - code:TSSLSocketTransportErrorSocketCreate - userInfo:nil]; - } - return nil; - } - - /* open a connection */ - if (connect(_sd, (struct sockaddr *)&pin, sizeof(pin)) == -1) { - NSLog(@"failed to create conenct to host %@:%d", hostname, port); - if (error) { - *error = [NSError errorWithDomain:TSSLSocketTransportErrorDomain - code:TSSLSocketTransportErrorConnect - userInfo:nil]; - } - return nil; - } - CFStreamCreatePairWithSocket(kCFAllocatorDefault, _sd, &readStream, &writeStream); - - CFReadStreamSetProperty(readStream, kCFStreamPropertyShouldCloseNativeSocket, kCFBooleanTrue); - CFWriteStreamSetProperty(writeStream, kCFStreamPropertyShouldCloseNativeSocket, kCFBooleanTrue); - - NSInputStream *inputStream; - NSOutputStream *outputStream; - - if (readStream && writeStream) { - - CFReadStreamSetProperty(readStream, - kCFStreamPropertySocketSecurityLevel, - kCFStreamSocketSecurityLevelTLSv1); - - NSDictionary *settings = @{(__bridge NSString *)kCFStreamSSLValidatesCertificateChain: @YES}; - - CFReadStreamSetProperty((CFReadStreamRef)readStream, - kCFStreamPropertySSLSettings, - (CFTypeRef)settings); - - CFWriteStreamSetProperty((CFWriteStreamRef)writeStream, - kCFStreamPropertySSLSettings, - (CFTypeRef)settings); - - inputStream = (__bridge NSInputStream *)readStream; - [inputStream setDelegate:self]; - [inputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode]; - [inputStream open]; - - outputStream = (__bridge NSOutputStream *)writeStream; - [outputStream setDelegate:self]; - [outputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode]; - [outputStream open]; - - CFRelease(readStream); - CFRelease(writeStream); - } - - self = [super initWithInputStream:inputStream outputStream:outputStream]; - - return self; -} - --(void) dealloc -{ - [self close]; -} - -#pragma mark - -#pragma mark NSStreamDelegate - --(void) stream:(NSStream *)aStream - handleEvent:(NSStreamEvent)eventCode -{ - switch (eventCode) { - case NSStreamEventNone: - break; - - case NSStreamEventHasBytesAvailable: - break; - - case NSStreamEventOpenCompleted: - break; - - case NSStreamEventHasSpaceAvailable: { - - BOOL proceed = NO; - SecTrustResultType trustResult = kSecTrustResultInvalid; - CFMutableArrayRef newPolicies = NULL; - - do { - - SecTrustRef trust = (__bridge SecTrustRef)[aStream propertyForKey:(NSString *)kCFStreamPropertySSLPeerTrust]; - - // Add new policy to current list of policies - SecPolicyRef policy = SecPolicyCreateSSL(NO, (__bridge CFStringRef)(_sslHostname)); - if (!policy) { - break; - } - - CFArrayRef policies; - if (SecTrustCopyPolicies(trust, &policies) != errSecSuccess) { - CFRelease(policy); - break; - } - - newPolicies = CFArrayCreateMutableCopy(NULL, 0, policies); - CFArrayAppendValue(newPolicies, policy); - - CFRelease(policies); - CFRelease(policy); - - // Update trust policies - if (SecTrustSetPolicies(trust, newPolicies) != errSecSuccess) { - break; - } - - // Evaluate the trust chain - if (SecTrustEvaluate(trust, &trustResult) != errSecSuccess) { - break; - } - - switch (trustResult) { - case kSecTrustResultProceed: - // NSLog(@"Trusted by USER"); - proceed = YES; - break; - - case kSecTrustResultUnspecified: - // NSLog(@"Trusted by OS"); - proceed = YES; - break; - - case kSecTrustResultRecoverableTrustFailure: - proceed = recoverFromTrustFailure(trust, trustResult); - break; - - case kSecTrustResultDeny: - // NSLog(@"Deny"); - break; - - case kSecTrustResultFatalTrustFailure: - // NSLog(@"FatalTrustFailure"); - break; - - case kSecTrustResultOtherError: - // NSLog(@"OtherError"); - break; - - case kSecTrustResultInvalid: - // NSLog(@"Invalid"); - break; - - default: - // NSLog(@"Default"); - break; - } - - } - while (NO); - - if (!proceed) { - NSLog(@"TSSLSocketTransport: Cannot trust certificate. Result: %u", trustResult); - [aStream close]; - } - - if (newPolicies) { - CFRelease(newPolicies); - } - - } - break; - - case NSStreamEventErrorOccurred: { - NSLog(@"TSSLSocketTransport: Error occurred opening stream: %@", [aStream streamError]); - break; - } - - case NSStreamEventEndEncountered: - break; - } -} - -BOOL recoverFromTrustFailure(SecTrustRef myTrust, SecTrustResultType lastTrustResult) -{ - CFAbsoluteTime trustTime = SecTrustGetVerifyTime(myTrust); - CFAbsoluteTime currentTime = CFAbsoluteTimeGetCurrent(); - - CFAbsoluteTime timeIncrement = 31536000; - CFAbsoluteTime newTime = currentTime - timeIncrement; - - if (trustTime - newTime) { - - CFDateRef newDate = CFDateCreate(NULL, newTime); - SecTrustSetVerifyDate(myTrust, newDate); - CFRelease(newDate); - - if (SecTrustEvaluate(myTrust, &lastTrustResult) != errSecSuccess) { - return NO; - } - - } - - if (lastTrustResult == kSecTrustResultProceed || lastTrustResult == kSecTrustResultUnspecified) { - return YES; - } - - NSLog(@"TSSLSocketTransport: Unable to recover certificate trust failure"); - return YES; -} - --(BOOL) isOpen -{ - if (_sd > 0) { - return TRUE; - } - else { - return FALSE; - } -} - -@end diff --git a/lib/cocoa/src/transport/TSSLSocketTransportError.h b/lib/cocoa/src/transport/TSSLSocketTransportError.h deleted file mode 100644 index e17f39e3276..00000000000 --- a/lib/cocoa/src/transport/TSSLSocketTransportError.h +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -#import "TTransportError.h" - - -extern NSString *TSSLSocketTransportErrorDomain; - - -typedef NS_ENUM (int, TSSLSocketTransportError) { - TSSLSocketTransportErrorHostanameResolution = -10000, - TSSLSocketTransportErrorSocketCreate = -10001, - TSSLSocketTransportErrorConnect = -10002, -}; diff --git a/lib/cocoa/src/transport/TSocketTransport.h b/lib/cocoa/src/transport/TSocketTransport.h deleted file mode 100644 index a7b91a2c976..00000000000 --- a/lib/cocoa/src/transport/TSocketTransport.h +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -#import -#import "TNSStreamTransport.h" - -NS_ASSUME_NONNULL_BEGIN - - -@interface TSocketTransport : TNSStreamTransport - --(id) initWithHostname:(NSString *)hostname - port:(int)port; - --(id) initWithPath:(NSString *)path; - -@end - - -NS_ASSUME_NONNULL_END diff --git a/lib/cocoa/src/transport/TSocketTransport.m b/lib/cocoa/src/transport/TSocketTransport.m deleted file mode 100644 index 9c58abb6d07..00000000000 --- a/lib/cocoa/src/transport/TSocketTransport.m +++ /dev/null @@ -1,131 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -#import "TSocketTransport.h" - -#if !TARGET_OS_IPHONE -#import -#else -#import -#endif - -#include -#include -#include - -@interface TSocketTransport () -@end - - -@implementation TSocketTransport - -- (id) initWithReadStream: (CFReadStreamRef) readStream writeStream: (CFWriteStreamRef) writeStream -{ - NSInputStream *inputStream = nil; - NSOutputStream *outputStream = nil; - - if (readStream && writeStream) { - - CFReadStreamSetProperty(readStream, kCFStreamPropertyShouldCloseNativeSocket, kCFBooleanTrue); - CFWriteStreamSetProperty(writeStream, kCFStreamPropertyShouldCloseNativeSocket, kCFBooleanTrue); - - inputStream = (__bridge NSInputStream *)readStream; - [inputStream setDelegate:self]; - [inputStream scheduleInRunLoop:NSRunLoop.currentRunLoop forMode:NSDefaultRunLoopMode]; - [inputStream open]; - - outputStream = (__bridge NSOutputStream *)writeStream; - [outputStream setDelegate:self]; - [outputStream scheduleInRunLoop:NSRunLoop.currentRunLoop forMode:NSDefaultRunLoopMode]; - [outputStream open]; - } - else { - - if (readStream) { - CFRelease(readStream); - } - - if (writeStream) { - CFRelease(writeStream); - } - - return nil; - } - - return [super initWithInputStream:inputStream outputStream:outputStream]; -} - -- (id) initWithHostname: (NSString *) hostname - port: (int) port -{ - CFReadStreamRef readStream = NULL; - CFWriteStreamRef writeStream = NULL; - CFStreamCreatePairWithSocketToHost(kCFAllocatorDefault, (__bridge CFStringRef)hostname, port, &readStream, &writeStream); - return [self initWithReadStream:readStream writeStream:writeStream]; -} - -- (id) initWithPath: (NSString *) path -{ - CFSocketNativeHandle sockfd = socket(AF_LOCAL, SOCK_STREAM, IPPROTO_IP); - int yes = 1; - if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes)) < 0) - { - NSLog(@"TSocketTransport: Unable to set REUSEADDR property of socket."); - return nil; - } - - NSData *serverAddress = [[self class] createAddressWithPath:path]; - - CFReadStreamRef readStream = NULL; - CFWriteStreamRef writeStream = NULL; - CFStreamCreatePairWithSocket(kCFAllocatorDefault, sockfd, &readStream, &writeStream); - if (!readStream || !writeStream) - { - NSLog(@"TSocketTransport: Unable to create read/write stream pair for socket."); - return nil; - } - - if (connect(sockfd, (struct sockaddr *)serverAddress.bytes, (socklen_t) serverAddress.length) < 0) - { - NSLog(@"TSocketTransport: Connect error: %s\n", strerror(errno)); - return nil; - } - - return [self initWithReadStream:readStream writeStream:writeStream]; -} - -+ (NSData *) createAddressWithPath: (NSString *)path -{ - struct sockaddr_un servaddr; - - size_t nullTerminatedPathLength = path.length + 1; - if (nullTerminatedPathLength> sizeof(servaddr.sun_path)) { - NSLog(@"TSocketTransport: Unable to create socket at path %@. Path is too long.", path); - return nil; - } - - bzero(&servaddr,sizeof(servaddr)); - servaddr.sun_family = AF_LOCAL; - memcpy(servaddr.sun_path, path.UTF8String, nullTerminatedPathLength); - servaddr.sun_len = SUN_LEN(&servaddr); - - return [NSData dataWithBytes:&servaddr length:sizeof(servaddr)]; -} - - -@end diff --git a/lib/cocoa/src/transport/TTransport.h b/lib/cocoa/src/transport/TTransport.h deleted file mode 100644 index 558cf60fb46..00000000000 --- a/lib/cocoa/src/transport/TTransport.h +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -#import - - -NS_ASSUME_NONNULL_BEGIN - - -@protocol TTransport - -/** - * Guarantees that all of len bytes are read - * - * @param buf Buffer to read into - * @param off Index in buffer to start storing bytes at - * @param len Maximum number of bytes to read - * @return YES if succeeded, NO if failed - * @throws TTransportError if there was an error reading data - */ --(BOOL) readAll:(UInt8 *)buf offset:(UInt32)off length:(UInt32)len error:(NSError *__autoreleasing *)error; - --(UInt32) readAvail:(UInt8 *)buf offset:(UInt32)off maxLength:(UInt32)maxLen error:(NSError *__autoreleasing *)error; - --(BOOL) write:(const UInt8 *)data offset:(UInt32)offset length:(UInt32)length error:(NSError *__autoreleasing *)error; - --(BOOL) flush:(NSError *__autoreleasing *)error; - -@end - - -NS_ASSUME_NONNULL_END diff --git a/lib/cocoa/src/transport/TTransportError.h b/lib/cocoa/src/transport/TTransportError.h deleted file mode 100644 index c8c9f067fd3..00000000000 --- a/lib/cocoa/src/transport/TTransportError.h +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -#import "TError.h" - - -extern NSString *TTransportErrorDomain; - - -typedef NS_ENUM (int, TTransportError) { - TTransportErrorUnknown = 0, - TTransportErrorNotOpen = 1, - TTransportErrorAlreadyOpen = 2, - TTransportErrorTimedOut = 3, - TTransportErrorEndOfFile = 4, -}; - - -extern NSString *TTransportErrorExtendedErrorKey; -extern NSString *TTransportErrorHttpErrorKey; - - -typedef NS_ENUM(int, THttpTransportError) { - THttpTransportErrorInvalidResponse = 1001, - THttpTransportErrorInvalidStatus = 1002, - THttpTransportErrorAuthentication = 1003, -}; diff --git a/lib/cocoa/src/transport/TTransportError.m b/lib/cocoa/src/transport/TTransportError.m deleted file mode 100644 index b1af076b271..00000000000 --- a/lib/cocoa/src/transport/TTransportError.m +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -#import "TTransportError.h" - - -NSString *TTransportErrorDomain = @"TTransportErrorDomain"; - - -NSString *TTransportErrorExtendedErrorKey = @"extendedError"; -NSString *TTransportErrorHttpErrorKey = @"httpError"; diff --git a/lib/cpp/CMakeLists.txt b/lib/cpp/CMakeLists.txt index 1ed0bfadfeb..b3a34d095c2 100755 --- a/lib/cpp/CMakeLists.txt +++ b/lib/cpp/CMakeLists.txt @@ -17,9 +17,16 @@ # under the License. # -include_directories(SYSTEM "${Boost_INCLUDE_DIRS}") +# 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 "") @@ -33,7 +40,6 @@ set( thriftcpp_SOURCES src/thrift/async/TConcurrentClientSyncInfo.cpp src/thrift/concurrency/ThreadManager.cpp src/thrift/concurrency/TimerManager.cpp - src/thrift/concurrency/Util.cpp src/thrift/processor/PeekProcessor.cpp src/thrift/protocol/TBase64Utils.cpp src/thrift/protocol/TDebugProtocol.cpp @@ -51,6 +57,9 @@ set( thriftcpp_SOURCES src/thrift/transport/TServerSocket.cpp src/thrift/transport/TTransportUtils.cpp src/thrift/transport/TBufferTransports.cpp + src/thrift/transport/TWebSocketServer.h + src/thrift/transport/TWebSocketServer.cpp + src/thrift/transport/SocketCommon.cpp src/thrift/server/TConnectedClient.cpp src/thrift/server/TServerFramework.cpp src/thrift/server/TSimpleServer.cpp @@ -91,8 +100,7 @@ else() ) endif() -# If OpenSSL is not found just ignore the OpenSSL stuff -find_package(OpenSSL) +# 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 @@ -102,50 +110,36 @@ if(OPENSSL_FOUND AND WITH_OPENSSL) list(APPEND SYSLIBS "${OPENSSL_LIBRARIES}") endif() -# WITH_*THREADS selects which threading library to use -if(WITH_BOOSTTHREADS) - set( thriftcpp_threads_SOURCES - src/thrift/concurrency/BoostThreadFactory.cpp - src/thrift/concurrency/BoostMonitor.cpp - src/thrift/concurrency/BoostMutex.cpp - ) - list(APPEND SYSLIBS "${Boost_LIBRARIES}") -elseif(UNIX AND NOT WITH_STDTHREADS) +if(UNIX) if(ANDROID) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -pthread") else() list(APPEND SYSLIBS pthread) endif() - set( thriftcpp_threads_SOURCES - src/thrift/concurrency/PosixThreadFactory.cpp - src/thrift/concurrency/Mutex.cpp - src/thrift/concurrency/Monitor.cpp - ) -else() - 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/StdThreadFactory.cpp - src/thrift/concurrency/StdMutex.cpp - src/thrift/concurrency/StdMonitor.cpp - ) 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 server +# If OpenSSL is not found or disabled just ignore the OpenSSL stuff +if(OPENSSL_FOUND AND WITH_OPENSSL) + list( APPEND thriftcppnb_SOURCES + src/thrift/transport/TNonblockingSSLServerSocket.cpp + ) +endif() + +# Thrift zlib transport set( thriftcppz_SOURCES src/thrift/transport/TZlibTransport.cpp src/thrift/protocol/THeaderProtocol.cpp @@ -154,12 +148,6 @@ set( thriftcppz_SOURCES src/thrift/transport/THeaderTransport.cpp ) -# Thrift Qt4 server -set( thriftcppqt_SOURCES - src/thrift/qt/TQIODeviceTransport.cpp - src/thrift/qt/TQTcpServer.cpp -) - # Contains the thrift specific ADD_LIBRARY_THRIFT and TARGET_LINK_LIBRARIES_THRIFT include(ThriftMacros) @@ -169,14 +157,16 @@ if(WIN32) 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 + find_package(Libevent REQUIRED) # Libevent comes with CMake support from 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}) - TARGET_LINK_LIBRARIES_THRIFT_AGAINST_THRIFT_LIBRARY(thriftnb thrift) + ADD_PKGCONFIG_THRIFT(thrift-nb) endif() if(WITH_ZLIB) @@ -186,27 +176,18 @@ if(WITH_ZLIB) ADD_LIBRARY_THRIFT(thriftz ${thriftcppz_SOURCES}) TARGET_LINK_LIBRARIES_THRIFT(thriftz ${SYSLIBS} ${ZLIB_LIBRARIES}) TARGET_LINK_LIBRARIES_THRIFT_AGAINST_THRIFT_LIBRARY(thriftz thrift) -endif() - -if(WITH_QT4) - set(CMAKE_AUTOMOC ON) - find_package(Qt4 REQUIRED COMPONENTS QtCore QtNetwork) - ADD_LIBRARY_THRIFT(thriftqt ${thriftcppqt_SOURCES}) - TARGET_LINK_LIBRARIES_THRIFT(thriftqt ${SYSLIBS} Qt4::QtCore Qt4::QtNetwork) - TARGET_LINK_LIBRARIES_THRIFT_AGAINST_THRIFT_LIBRARY(thriftqt thrift) + ADD_PKGCONFIG_THRIFT(thrift-z) endif() if(WITH_QT5) - # Qt5 has its own directory to avoid conflict with Qt4 caused by CMAKE_AUTOMOC add_subdirectory(src/thrift/qt) + ADD_PKGCONFIG_THRIFT(thrift-qt5) endif() if(MSVC) add_definitions("-DUNICODE -D_UNICODE") endif() -add_definitions("-D__STDC_LIMIT_MACROS") - # Install the headers install(DIRECTORY "src/thrift" DESTINATION "${INCLUDE_INSTALL_DIR}" FILES_MATCHING PATTERN "*.h" PATTERN "*.tcc") diff --git a/lib/cpp/Makefile.am b/lib/cpp/Makefile.am index 83ccd9b6f0f..eab2e217e80 100755 --- a/lib/cpp/Makefile.am +++ b/lib/cpp/Makefile.am @@ -17,10 +17,7 @@ # under the License. # -AUTOMAKE_OPTIONS = subdir-objects - -moc_%.cpp: %.h - $(QT_MOC) $(QT_CFLAGS) $< -o $@ +AUTOMAKE_OPTIONS = subdir-objects nostdinc moc__%.cpp: %.h $(QT5_MOC) $(QT5_CFLAGS) $< -o $@ @@ -28,12 +25,8 @@ moc__%.cpp: %.h SUBDIRS = . if WITH_TESTS -# This file is needed by compiler with plugin, while test/Makefile.am needs compiler -# So test directory is directly picked by top level Makefile.am for plugin build -if !WITH_PLUGIN SUBDIRS += test endif -endif pkgconfigdir = $(libdir)/pkgconfig @@ -52,10 +45,6 @@ if AMX_HAVE_ZLIB lib_LTLIBRARIES += libthriftz.la pkgconfig_DATA += thrift-z.pc endif -if AMX_HAVE_QT -lib_LTLIBRARIES += libthriftqt.la -pkgconfig_DATA += thrift-qt.pc -endif if AMX_HAVE_QT5 lib_LTLIBRARIES += libthriftqt5.la pkgconfig_DATA += thrift-qt5.pc @@ -74,7 +63,6 @@ libthrift_la_SOURCES = src/thrift/TApplicationException.cpp \ src/thrift/async/TConcurrentClientSyncInfo.cpp \ src/thrift/concurrency/ThreadManager.cpp \ src/thrift/concurrency/TimerManager.cpp \ - src/thrift/concurrency/Util.cpp \ src/thrift/processor/PeekProcessor.cpp \ src/thrift/protocol/TDebugProtocol.cpp \ src/thrift/protocol/TJSONProtocol.cpp \ @@ -99,6 +87,8 @@ libthrift_la_SOURCES = src/thrift/TApplicationException.cpp \ src/thrift/transport/TNonblockingSSLServerSocket.cpp \ src/thrift/transport/TTransportUtils.cpp \ src/thrift/transport/TBufferTransports.cpp \ + src/thrift/transport/TWebSocketServer.cpp \ + src/thrift/transport/SocketCommon.cpp \ src/thrift/server/TConnectedClient.cpp \ src/thrift/server/TServer.cpp \ src/thrift/server/TServerFramework.cpp \ @@ -106,15 +96,10 @@ libthrift_la_SOURCES = src/thrift/TApplicationException.cpp \ src/thrift/server/TThreadPoolServer.cpp \ src/thrift/server/TThreadedServer.cpp -if WITH_BOOSTTHREADS -libthrift_la_SOURCES += src/thrift/concurrency/BoostThreadFactory.cpp \ - src/thrift/concurrency/BoostMonitor.cpp \ - src/thrift/concurrency/BoostMutex.cpp -else libthrift_la_SOURCES += src/thrift/concurrency/Mutex.cpp \ - src/thrift/concurrency/Monitor.cpp \ - src/thrift/concurrency/PosixThreadFactory.cpp -endif + 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 \ @@ -125,39 +110,31 @@ libthriftz_la_SOURCES = src/thrift/transport/TZlibTransport.cpp \ src/thrift/protocol/THeaderProtocol.cpp -libthriftqt_la_MOC = src/thrift/qt/moc_TQTcpServer.cpp -nodist_libthriftqt_la_SOURCES = $(libthriftqt_la_MOC) -libthriftqt_la_SOURCES = src/thrift/qt/TQIODeviceTransport.cpp \ - src/thrift/qt/TQTcpServer.cpp -CLEANFILES = $(libthriftqt_la_MOC) - 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) +CLEANFILES = $(libthriftqt5_la_MOC) # Flags for the various libraries libthriftnb_la_CPPFLAGS = $(AM_CPPFLAGS) $(LIBEVENT_CPPFLAGS) libthriftz_la_CPPFLAGS = $(AM_CPPFLAGS) $(ZLIB_CPPFLAGS) -libthriftqt_la_CPPFLAGS = $(AM_CPPFLAGS) $(QT_CFLAGS) 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) -libthriftqt_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) -libthriftqt_la_LDFLAGS = -release $(VERSION) $(BOOST_LDFLAGS) $(QT_LIBS) +libthriftz_la_LDFLAGS = -release $(VERSION) $(BOOST_LDFLAGS) $(ZLIB_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 \ @@ -165,29 +142,26 @@ include_thrift_HEADERS = \ src/thrift/TApplicationException.h \ src/thrift/TLogging.h \ src/thrift/TToString.h \ - src/thrift/stdcxx.h \ - src/thrift/TBase.h + src/thrift/TBase.h \ + src/thrift/TConfiguration.h include_concurrencydir = $(include_thriftdir)/concurrency include_concurrency_HEADERS = \ - src/thrift/concurrency/BoostThreadFactory.h \ src/thrift/concurrency/Exception.h \ src/thrift/concurrency/Mutex.h \ src/thrift/concurrency/Monitor.h \ - src/thrift/concurrency/PlatformThreadFactory.h \ - src/thrift/concurrency/PosixThreadFactory.h \ - src/thrift/concurrency/StdMonitor.cpp \ - src/thrift/concurrency/StdMutex.cpp \ - src/thrift/concurrency/StdThreadFactory.cpp \ - src/thrift/concurrency/StdThreadFactory.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 \ - src/thrift/concurrency/Util.h + src/thrift/concurrency/FunctionRunner.h include_protocoldir = $(include_thriftdir)/protocol include_protocol_HEADERS = \ + src/thrift/protocol/TEnum.h \ + src/thrift/protocol/TList.h \ + src/thrift/protocol/TSet.h \ + src/thrift/protocol/TMap.h \ src/thrift/protocol/TBinaryProtocol.h \ src/thrift/protocol/TBinaryProtocol.tcc \ src/thrift/protocol/TCompactProtocol.h \ @@ -221,6 +195,7 @@ include_transport_HEADERS = \ src/thrift/transport/THttpClient.h \ src/thrift/transport/THttpServer.h \ src/thrift/transport/TSocket.h \ + src/thrift/transport/TSocketUtils.h \ src/thrift/transport/TPipe.h \ src/thrift/transport/TPipeServer.h \ src/thrift/transport/TSSLSocket.h \ @@ -231,7 +206,9 @@ include_transport_HEADERS = \ src/thrift/transport/TTransportUtils.h \ src/thrift/transport/TBufferTransports.h \ src/thrift/transport/TShortReadTransport.h \ - src/thrift/transport/TZlibTransport.h + src/thrift/transport/TZlibTransport.h \ + src/thrift/transport/TWebSocketServer.h \ + src/thrift/transport/SocketCommon.h include_serverdir = $(include_thriftdir)/server include_server_HEADERS = \ @@ -281,7 +258,6 @@ EXTRA_DIST = \ thrift-nb.pc.in \ thrift.pc.in \ thrift-z.pc.in \ - thrift-qt.pc.in \ thrift-qt5.pc.in \ src/thrift/qt/CMakeLists.txt \ $(WINDOWS_DIST) diff --git a/lib/cpp/README.md b/lib/cpp/README.md index e744a6a4621..8074484f197 100755 --- a/lib/cpp/README.md +++ b/lib/cpp/README.md @@ -33,8 +33,8 @@ In case you do not want to open another README.md file, do this thrift src: Thrift is divided into two libraries. -* libthrift - The core Thrift library contains all the core Thrift code. It requires - boost shared pointers, pthreads, and librt. +* 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. @@ -54,14 +54,12 @@ you are using libthriftnb you will also need libevent. ## Dependencies -If your C++ environment implements C++11 or later, thrift will automatically use -std::shared_ptr. Otherwise you will need the boost library to provide a shared_ptr -implementation for C++ environments pre-C++11. If you are linking against code -that expects to be using boost::shared_ptr, you can define the preprocessor -variable FORCE_BOOST_SMART_PTR for your build of thrift to make it use boost instead -of std for a number of memory related classes. See thrift/stdcxx.h for more. +C++11 is required at a minimum. C++03/C++98 are not supported after version 0.12.0. -libevent (for libthriftnb only) +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 @@ -85,42 +83,38 @@ The same linking guidelines described above for libthriftnb apply to windows as ## Linking Against Thrift You need to link your project that uses thrift against all the thrift -dependencies; in the case of libthrift, boost and for +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/confg.h" +the config header: "windows/config.h" ## Dependencies -The same dependencies for shared_ptr as described above apply to windows as well. - -boost thread -http://www.boost.org/doc/libs/release/doc/html/thread.html - libevent (for libthriftnb only) http://monkey.org/~provos/libevent/ -## Notes on boost thread (static vs shared): - -By default lib/cpp/windows/force_inc.h defines: +## Windows version compatibility - #define BOOST_ALL_NO_LIB 1 - #define BOOST_THREAD_NO_LIB 1 +The Thrift library targets Windows 7 or latter versions. The supports for windows XP and Vista are avaiable until 0.12.0. -This has for effect to have the host application linking against Thrift -to have to link with boost thread as a static library. +## Thrift and the VCPKG Package manager -If you wanted instead to link with boost thread as a shared library, -you'll need to uncomment those two lines, and recompile. +You can download and install thrift using the +[vcpkg](https://github.com/Microsoft/vcpkg) dependency manager: -## Windows version compatibility + git clone https://github.com/Microsoft/vcpkg.git + cd vcpkg + ./bootstrap-vcpkg.sh + ./vcpkg integrate install + ./vcpkg install thrift -The Thrift library targets Windows XP for broadest compatbility. A notable -difference is in the Windows-specific implementation of the socket poll -function. To target Vista, Win7 or other versions, comment out the line +The thrift port in vcpkg is kept up to date by Microsoft team members +and community contributors. The Apache Thrift project is *not* responsible +for the vcpkg port. Therefore, if the version is out of date, please +[create an issue or pull request](https://github.com/Microsoft/vcpkg) +on the vcpkg repository. - #define TARGET_WIN_XP. ## Named Pipes @@ -138,50 +132,16 @@ TSimpleServer, TThreadedServer, and TThreadPoolServer. ## Implementation -There're two main classes TSSLSocketFactory and TSSLSocket. Instances of +There are two main classes TSSLSocketFactory and TSSLSocket. Instances of TSSLSocket are always created from TSSLSocketFactory. -PosixSSLThreadFactory creates PosixSSLThread. The only difference from the -PthreadThread type is that it cleanups OpenSSL error queue upon exiting -the thread. Ideally, OpenSSL APIs should only be called from PosixSSLThread. - ## How to use SSL APIs -This is for demo. In real code, typically only one TSSLSocketFactory -instance is needed. - - shared_ptr getSSLSocketFactory() { - shared_ptr factory(new TSSLSocketFactory()); - // client: load trusted certificates - factory->loadTrustedCertificates("my-trusted-ca-certificates.pem"); - // client: optionally set your own access manager, otherwise, - // the default client access manager will be loaded. - - factory->loadCertificate("my-certificate-signed-by-ca.pem"); - factory->loadPrivateKey("my-private-key.pem"); - // server: optionally setup access manager - // shared_ptr accessManager(new MyAccessManager); - // factory->access(accessManager); - ... - } - - -client code sample - - shared_ptr factory = getSSLSocketFactory(); - shared_ptr socket = factory.createSocket(host, port); - shared_ptr transport(new TBufferedTransport(socket)); - ... +See the TestClient.cpp and TestServer.cpp files for examples. +### AccessManager (certificate validation) -server code sample - - shared_ptr factory = getSSLSocketFactory(); - shared_ptr socket(new TSSLServerSocket(port, factory)); - shared_ptr transportFactory(new TBufferedTransportFactory)); - ... - -## AccessManager +An example of certificate validation can be found in TestServer.cpp. AccessManager defines a callback interface. It has three callback methods: @@ -272,8 +232,48 @@ 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 classes which diff --git a/lib/cpp/libthrift.vcxproj b/lib/cpp/libthrift.vcxproj index d1097ecd333..2353cd39a51 100644 --- a/lib/cpp/libthrift.vcxproj +++ b/lib/cpp/libthrift.vcxproj @@ -39,8 +39,6 @@ - - @@ -81,10 +79,7 @@ - - - @@ -260,7 +255,7 @@ Disabled HAVE_CONFIG_H=1;WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions) $(IntDir)libthrift.pdb - MultiThreadedDebug + MultiThreadedDebugDll Windows @@ -273,6 +268,7 @@ Level3 Disabled HAVE_CONFIG_H=1;WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions) + MultiThreadedDebugDll Windows @@ -285,7 +281,7 @@ Level3 Disabled HAVE_CONFIG_H=1;WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions) - MultiThreadedDebug + MultiThreadedDebugDll Windows @@ -301,6 +297,7 @@ true HAVE_CONFIG_H=1;WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions) $(IntDir)libthrift.pdb + MultiThreadedDll Windows @@ -318,7 +315,7 @@ true HAVE_CONFIG_H=1;WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions) $(IntDir)libthrift.pdb - MultiThreaded + MultiThreadedDll Windows @@ -335,6 +332,7 @@ true true HAVE_CONFIG_H=1;WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions) + MultiThreadedDll Windows @@ -351,7 +349,7 @@ true true HAVE_CONFIG_H=1;WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions) - MultiThreaded + MultiThreadedDll Windows diff --git a/lib/cpp/libthrift.vcxproj.filters b/lib/cpp/libthrift.vcxproj.filters index 5f64f78cb4e..0e9a9fde1c2 100644 --- a/lib/cpp/libthrift.vcxproj.filters +++ b/lib/cpp/libthrift.vcxproj.filters @@ -96,12 +96,6 @@ concurrency - - concurrency - - - concurrency - windows @@ -227,15 +221,6 @@ windows - - concurrency - - - concurrency - - - concurrency - windows diff --git a/lib/cpp/libthriftnb.vcxproj b/lib/cpp/libthriftnb.vcxproj index 9a6ffe60d4b..dc6764e0367 100755 --- a/lib/cpp/libthriftnb.vcxproj +++ b/lib/cpp/libthriftnb.vcxproj @@ -172,6 +172,7 @@ Disabled HAVE_CONFIG_H=1;WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions) $(IntDir)libthriftnb.pdb + MultiThreadedDebugDll Windows @@ -186,7 +187,7 @@ Disabled HAVE_CONFIG_H=1;WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions) $(IntDir)libthriftnb.pdb - MultiThreadedDebug + MultiThreadedDebugDll Windows @@ -200,6 +201,7 @@ Level3 Disabled HAVE_CONFIG_H=1;WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions) + MultiThreadedDebugDll Windows @@ -213,7 +215,7 @@ Level3 Disabled HAVE_CONFIG_H=1;WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions) - MultiThreadedDebug + MultiThreadedDebugDll Windows @@ -230,6 +232,7 @@ true HAVE_CONFIG_H=1;WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions) $(IntDir)libthriftnb.pdb + MultiThreadedDll Windows @@ -248,7 +251,7 @@ true HAVE_CONFIG_H=1;WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions) $(IntDir)libthriftnb.pdb - MultiThreaded + MultiThreadedDll Windows @@ -266,6 +269,7 @@ true true HAVE_CONFIG_H=1;WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions) + MultiThreadedDll Windows @@ -283,7 +287,7 @@ true true HAVE_CONFIG_H=1;WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions) - MultiThreaded + MultiThreadedDll Windows diff --git a/lib/cpp/src/thrift/TApplicationException.h b/lib/cpp/src/thrift/TApplicationException.h index 81d94249d67..cd1b3e7c0d1 100644 --- a/lib/cpp/src/thrift/TApplicationException.h +++ b/lib/cpp/src/thrift/TApplicationException.h @@ -57,7 +57,7 @@ class TApplicationException : public TException { TApplicationException(TApplicationExceptionType type, const std::string& message) : TException(message), type_(type) {} - virtual ~TApplicationException() throw() {} + ~TApplicationException() noexcept override = default; /** * Returns an error code that provides information about the type of error @@ -67,7 +67,7 @@ class TApplicationException : public TException { */ TApplicationExceptionType getType() const { return type_; } - virtual const char* what() const throw() { + const char* what() const noexcept override { if (message_.empty()) { switch (type_) { case UNKNOWN: diff --git a/lib/cpp/src/thrift/TBase.h b/lib/cpp/src/thrift/TBase.h index a274bcd73bb..e2e78e72545 100644 --- a/lib/cpp/src/thrift/TBase.h +++ b/lib/cpp/src/thrift/TBase.h @@ -28,7 +28,7 @@ namespace thrift { class TBase { public: - virtual ~TBase(){}; + virtual ~TBase() = default; virtual uint32_t read(protocol::TProtocol* iprot) = 0; virtual uint32_t write(protocol::TProtocol* oprot) const = 0; }; diff --git a/lib/cpp/src/thrift/TConfiguration.h b/lib/cpp/src/thrift/TConfiguration.h new file mode 100644 index 00000000000..5bff440a082 --- /dev/null +++ b/lib/cpp/src/thrift/TConfiguration.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_TCONFIGURATION_H +#define THRIFT_TCONFIGURATION_H + +namespace apache { +namespace thrift { + +class TConfiguration +{ +public: + TConfiguration(int maxMessageSize = DEFAULT_MAX_MESSAGE_SIZE, + int maxFrameSize = DEFAULT_MAX_FRAME_SIZE, int recursionLimit = DEFAULT_RECURSION_DEPTH) + : maxMessageSize_(maxMessageSize), maxFrameSize_(maxFrameSize), recursionLimit_(recursionLimit) {} + + const static int DEFAULT_MAX_MESSAGE_SIZE = 100 * 1024 * 1024; + const static int DEFAULT_MAX_FRAME_SIZE = 16384000; // this value is used consistently across all Thrift libraries + const static int DEFAULT_RECURSION_DEPTH = 64; + + inline int getMaxMessageSize() { return maxMessageSize_; } + inline void setMaxMessageSize(int maxMessageSize) { maxMessageSize_ = maxMessageSize; } + inline int getMaxFrameSize() { return maxFrameSize_; } + inline void setMaxFrameSize(int maxFrameSize) { maxFrameSize_ = maxFrameSize; } + inline int getRecursionLimit() { return recursionLimit_; } + inline void setRecursionLimit(int recursionLimit) { recursionLimit_ = recursionLimit; } + +private: + int maxMessageSize_ = DEFAULT_MAX_MESSAGE_SIZE; + int maxFrameSize_ = DEFAULT_MAX_FRAME_SIZE; + int recursionLimit_ = DEFAULT_RECURSION_DEPTH; + + // TODO(someone_smart): add connection and i/o timeouts +}; +} +} // apache::thrift + +#endif /* THRIFT_TCONFIGURATION_H */ + diff --git a/lib/cpp/src/thrift/TDispatchProcessor.h b/lib/cpp/src/thrift/TDispatchProcessor.h index dadc87b5c05..ae522b2d846 100644 --- a/lib/cpp/src/thrift/TDispatchProcessor.h +++ b/lib/cpp/src/thrift/TDispatchProcessor.h @@ -33,15 +33,15 @@ namespace thrift { template class TDispatchProcessorT : public TProcessor { public: - virtual bool process(stdcxx::shared_ptr in, - stdcxx::shared_ptr out, - void* connectionContext) { + bool process(std::shared_ptr in, + std::shared_ptr out, + void* connectionContext) override { protocol::TProtocol* inRaw = in.get(); protocol::TProtocol* outRaw = out.get(); // Try to dynamic cast to the template protocol type - Protocol_* specificIn = dynamic_cast(inRaw); - Protocol_* specificOut = dynamic_cast(outRaw); + auto* specificIn = dynamic_cast(inRaw); + auto* specificOut = dynamic_cast(outRaw); if (specificIn && specificOut) { return processFast(specificIn, specificOut, connectionContext); } @@ -105,9 +105,9 @@ class TDispatchProcessorT : public TProcessor { */ class TDispatchProcessor : public TProcessor { public: - virtual bool process(stdcxx::shared_ptr in, - stdcxx::shared_ptr out, - void* connectionContext) { + bool process(std::shared_ptr in, + std::shared_ptr out, + void* connectionContext) override { std::string fname; protocol::TMessageType mtype; int32_t seqid; diff --git a/lib/cpp/src/thrift/TOutput.cpp b/lib/cpp/src/thrift/TOutput.cpp index ae3a9e28220..a30879acd6b 100644 --- a/lib/cpp/src/thrift/TOutput.cpp +++ b/lib/cpp/src/thrift/TOutput.cpp @@ -24,10 +24,16 @@ #include #include +#ifdef _WIN32 +#include +#endif + namespace apache { namespace thrift { -TOutput GlobalOutput; +THRIFT_EXPORT TOutput GlobalOutput; + +TOutput::TOutput() : f_(&errorTimeWrapper) {} void TOutput::printf(const char* message, ...) { #ifndef THRIFT_SQUELCH_CONSOLE_OUTPUT @@ -60,7 +66,7 @@ void TOutput::printf(const char* message, ...) { #endif char* heap_buf = (char*)malloc((need + 1) * sizeof(char)); - if (heap_buf == NULL) { + if (heap_buf == nullptr) { #ifdef _MSC_VER va_start(ap, message); vsnprintf_s(stack_buf, STACK_BUF_SIZE, _TRUNCATE, message, ap); @@ -99,14 +105,12 @@ void TOutput::perror(const char* message, int errno_copy) { } 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 HAVE_STRERROR_R #ifdef STRERROR_R_CHAR_P - char* b_error = strerror_r(errno_copy, b_errbuf, sizeof(b_errbuf)); -#else + char* b_error = ::strerror_r(errno_copy, b_errbuf, sizeof(b_errbuf)); +#else // STRERROR_R_CHAR_P char* b_error = b_errbuf; int rv = strerror_r(errno_copy, b_errbuf, sizeof(b_errbuf)); if (rv == -1) { @@ -114,13 +118,27 @@ std::string TOutput::strerror_s(int errno_copy) { return "XSI-compliant strerror_r() failed with errno = " + to_string(errno_copy); } -#endif +#endif // STRERROR_R_CHAR_P +#else // HAVE_STRERROR_R +#ifdef _WIN32 + const size_t size = FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, + nullptr, errno_copy, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + b_errbuf, sizeof(b_errbuf), nullptr); + + if (size > 2 && b_errbuf[size-2] == '\r' && b_errbuf[size-1] == '\n') { + b_errbuf[size-2] = '\0'; + b_errbuf[size-1] = '\0'; + } +#else // _WIN32 + ::strerror_s(b_errbuf, sizeof(b_errbuf), errno_copy); +#endif // _WIN32 + char* b_error = b_errbuf; +#endif // HAVE_STRERROR_R + // 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/lib/cpp/src/thrift/TOutput.h b/lib/cpp/src/thrift/TOutput.h index 1375f737d54..26c9a563aed 100644 --- a/lib/cpp/src/thrift/TOutput.h +++ b/lib/cpp/src/thrift/TOutput.h @@ -20,12 +20,14 @@ #ifndef _THRIFT_OUTPUT_H_ #define _THRIFT_OUTPUT_H_ 1 +#include + namespace apache { namespace thrift { class TOutput { public: - TOutput() : f_(&errorTimeWrapper) {} + TOutput(); inline void setOutputFunction(void (*function)(const char*)) { f_ = function; } @@ -51,7 +53,7 @@ class TOutput { void (*f_)(const char*); }; -extern TOutput GlobalOutput; +THRIFT_EXPORT extern TOutput GlobalOutput; } } // namespace apache::thrift diff --git a/lib/cpp/src/thrift/TProcessor.h b/lib/cpp/src/thrift/TProcessor.h index 27294d3ec0c..65bf3d4a7a4 100644 --- a/lib/cpp/src/thrift/TProcessor.h +++ b/lib/cpp/src/thrift/TProcessor.h @@ -22,7 +22,6 @@ #include #include -#include namespace apache { namespace thrift { @@ -36,7 +35,7 @@ namespace thrift { */ class TProcessorEventHandler { public: - virtual ~TProcessorEventHandler() {} + virtual ~TProcessorEventHandler() = default; /** * Called before calling other callback methods. @@ -47,7 +46,7 @@ class TProcessorEventHandler { virtual void* getContext(const char* fn_name, void* serverContext) { (void)fn_name; (void)serverContext; - return NULL; + return nullptr; } /** @@ -109,7 +108,7 @@ class TProcessorEventHandler { } protected: - TProcessorEventHandler() {} + TProcessorEventHandler() = default; }; /** @@ -120,10 +119,10 @@ class TProcessorContextFreer { TProcessorContextFreer(TProcessorEventHandler* handler, void* context, const char* method) : handler_(handler), context_(context), method_(method) {} ~TProcessorContextFreer() { - if (handler_ != NULL) + if (handler_ != nullptr) handler_->freeContext(context_, method_); } - void unregister() { handler_ = NULL; } + void unregister() { handler_ = nullptr; } private: apache::thrift::TProcessorEventHandler* handler_; @@ -140,30 +139,30 @@ class TProcessorContextFreer { */ class TProcessor { public: - virtual ~TProcessor() {} + virtual ~TProcessor() = default; - virtual bool process(stdcxx::shared_ptr in, - stdcxx::shared_ptr out, + virtual bool process(std::shared_ptr in, + std::shared_ptr out, void* connectionContext) = 0; - bool process(stdcxx::shared_ptr io, void* connectionContext) { + bool process(std::shared_ptr io, void* connectionContext) { return process(io, io, connectionContext); } - stdcxx::shared_ptr getEventHandler() const { return eventHandler_; } + std::shared_ptr getEventHandler() const { return eventHandler_; } - void setEventHandler(stdcxx::shared_ptr eventHandler) { + void setEventHandler(std::shared_ptr eventHandler) { eventHandler_ = eventHandler; } protected: - TProcessor() {} + TProcessor() = default; - stdcxx::shared_ptr eventHandler_; + std::shared_ptr eventHandler_; }; /** - * This is a helper class to allow stdcxx::shared_ptr to be used with handler + * 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 @@ -177,7 +176,7 @@ class TProcessor { template class ReleaseHandler { public: - ReleaseHandler(const stdcxx::shared_ptr& handlerFactory) + ReleaseHandler(const std::shared_ptr& handlerFactory) : handlerFactory_(handlerFactory) {} void operator()(typename HandlerFactory_::Handler* handler) { @@ -187,23 +186,23 @@ class ReleaseHandler { } private: - stdcxx::shared_ptr handlerFactory_; + std::shared_ptr handlerFactory_; }; struct TConnectionInfo { // The input and output protocols - stdcxx::shared_ptr input; - stdcxx::shared_ptr output; + std::shared_ptr input; + std::shared_ptr 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. - stdcxx::shared_ptr transport; + std::shared_ptr transport; }; class TProcessorFactory { public: - virtual ~TProcessorFactory() {} + virtual ~TProcessorFactory() = default; /** * Get the TProcessor to use for a particular connection. @@ -212,17 +211,17 @@ class TProcessorFactory { * 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 stdcxx::shared_ptr getProcessor(const TConnectionInfo& connInfo) = 0; + virtual std::shared_ptr getProcessor(const TConnectionInfo& connInfo) = 0; }; class TSingletonProcessorFactory : public TProcessorFactory { public: - TSingletonProcessorFactory(stdcxx::shared_ptr processor) : processor_(processor) {} + TSingletonProcessorFactory(std::shared_ptr processor) : processor_(processor) {} - stdcxx::shared_ptr getProcessor(const TConnectionInfo&) { return processor_; } + std::shared_ptr getProcessor(const TConnectionInfo&) override { return processor_; } private: - stdcxx::shared_ptr processor_; + std::shared_ptr processor_; }; } } // apache::thrift diff --git a/lib/cpp/src/thrift/Thrift.h b/lib/cpp/src/thrift/Thrift.h index e8e70eba044..d5066ee7107 100644 --- a/lib/cpp/src/thrift/Thrift.h +++ b/lib/cpp/src/thrift/Thrift.h @@ -42,9 +42,6 @@ #include #include -#include -#include - #include #include @@ -82,9 +79,9 @@ class TException : public std::exception { TException(const std::string& message) : message_(message) {} - virtual ~TException() throw() {} + virtual ~TException() noexcept override = default; - virtual const char* what() const throw() { + const char* what() const noexcept override { if (message_.empty()) { return "Default TException."; } else { @@ -101,14 +98,14 @@ class TDelayedException { template static TDelayedException* delayException(const E& e); virtual void throw_it() = 0; - virtual ~TDelayedException(){}; + virtual ~TDelayedException() = default; }; template class TExceptionWrapper : public TDelayedException { public: TExceptionWrapper(const E& e) : e_(e) {} - virtual void throw_it() { + void throw_it() override { E temp(e_); delete this; throw temp; diff --git a/lib/cpp/src/thrift/VirtualProfiling.cpp b/lib/cpp/src/thrift/VirtualProfiling.cpp index 6ce346b82ef..4d752cf1ebf 100644 --- a/lib/cpp/src/thrift/VirtualProfiling.cpp +++ b/lib/cpp/src/thrift/VirtualProfiling.cpp @@ -112,7 +112,7 @@ class Backtrace { void* getFrame(int index) const { int adjusted_index = index + skip_; if (adjusted_index < 0 || adjusted_index >= numCallers_) { - return NULL; + return nullptr; } return callers_[adjusted_index]; } @@ -151,7 +151,7 @@ class Key { }; Key(const Backtrace* bt, const std::type_info& type_info) - : backtrace_(bt), typeName1_(type_info.name()), typeName2_(NULL) {} + : backtrace_(bt), typeName1_(type_info.name()), typeName2_(nullptr) {} 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()) {} @@ -189,7 +189,7 @@ class Key { */ void cleanup() { delete backtrace_; - backtrace_ = NULL; + backtrace_ = nullptr; } int cmp(const Key& k) const { diff --git a/lib/cpp/src/thrift/async/TAsyncBufferProcessor.h b/lib/cpp/src/thrift/async/TAsyncBufferProcessor.h index 0d56c78d0b3..e3c3597c2db 100644 --- a/lib/cpp/src/thrift/async/TAsyncBufferProcessor.h +++ b/lib/cpp/src/thrift/async/TAsyncBufferProcessor.h @@ -20,7 +20,7 @@ #ifndef _THRIFT_TASYNC_BUFFER_PROCESSOR_H_ #define _THRIFT_TASYNC_BUFFER_PROCESSOR_H_ 1 -#include +#include #include namespace apache { @@ -34,10 +34,10 @@ class TAsyncBufferProcessor { // forcefully close the connection (if applicable). // "in" and "out" should be TMemoryBuffer or similar, // not a wrapper around a socket. - virtual void process(stdcxx::function _return, - stdcxx::shared_ptr ibuf, - stdcxx::shared_ptr obuf) = 0; - virtual ~TAsyncBufferProcessor() {} + virtual void process(std::function _return, + std::shared_ptr ibuf, + std::shared_ptr obuf) = 0; + virtual ~TAsyncBufferProcessor() = default; }; } } diff --git a/lib/cpp/src/thrift/async/TAsyncChannel.cpp b/lib/cpp/src/thrift/async/TAsyncChannel.cpp index c87659fcaf5..01b91131f44 100644 --- a/lib/cpp/src/thrift/async/TAsyncChannel.cpp +++ b/lib/cpp/src/thrift/async/TAsyncChannel.cpp @@ -18,7 +18,6 @@ */ #include -#include namespace apache { namespace thrift { @@ -27,8 +26,8 @@ namespace async { void TAsyncChannel::sendAndRecvMessage(const VoidCallback& cob, TMemoryBuffer* sendBuf, TMemoryBuffer* recvBuf) { - apache::thrift::stdcxx::function send_done - = apache::thrift::stdcxx::bind(&TAsyncChannel::recvMessage, this, cob, recvBuf); + std::function send_done + = std::bind(&TAsyncChannel::recvMessage, this, cob, recvBuf); sendMessage(send_done, sendBuf); } diff --git a/lib/cpp/src/thrift/async/TAsyncChannel.h b/lib/cpp/src/thrift/async/TAsyncChannel.h index f8d2b036aeb..22cf383883a 100644 --- a/lib/cpp/src/thrift/async/TAsyncChannel.h +++ b/lib/cpp/src/thrift/async/TAsyncChannel.h @@ -20,7 +20,8 @@ #ifndef _THRIFT_ASYNC_TASYNCCHANNEL_H_ #define _THRIFT_ASYNC_TASYNCCHANNEL_H_ 1 -#include +#include +#include #include namespace apache { @@ -38,9 +39,9 @@ using apache::thrift::transport::TMemoryBuffer; class TAsyncChannel { public: - typedef apache::thrift::stdcxx::function VoidCallback; + typedef std::function VoidCallback; - virtual ~TAsyncChannel() {} + virtual ~TAsyncChannel() = default; // is the channel in a good state? virtual bool good() const = 0; diff --git a/lib/cpp/src/thrift/async/TAsyncDispatchProcessor.h b/lib/cpp/src/thrift/async/TAsyncDispatchProcessor.h index a1450f0154c..2a694ac53bb 100644 --- a/lib/cpp/src/thrift/async/TAsyncDispatchProcessor.h +++ b/lib/cpp/src/thrift/async/TAsyncDispatchProcessor.h @@ -34,15 +34,15 @@ namespace async { template class TAsyncDispatchProcessorT : public TAsyncProcessor { public: - virtual void process(apache::thrift::stdcxx::function _return, - stdcxx::shared_ptr in, - stdcxx::shared_ptr out) { + void process(std::function _return, + std::shared_ptr in, + std::shared_ptr out) override { protocol::TProtocol* inRaw = in.get(); protocol::TProtocol* outRaw = out.get(); // Try to dynamic cast to the template protocol type - Protocol_* specificIn = dynamic_cast(inRaw); - Protocol_* specificOut = dynamic_cast(outRaw); + auto* specificIn = dynamic_cast(inRaw); + auto* specificOut = dynamic_cast(outRaw); if (specificIn && specificOut) { return processFast(_return, specificIn, specificOut); } @@ -70,7 +70,7 @@ class TAsyncDispatchProcessorT : public TAsyncProcessor { return this->dispatchCall(_return, inRaw, outRaw, fname, seqid); } - void processFast(apache::thrift::stdcxx::function _return, + void processFast(std::function _return, Protocol_* in, Protocol_* out) { std::string fname; @@ -87,13 +87,13 @@ class TAsyncDispatchProcessorT : public TAsyncProcessor { return this->dispatchCallTemplated(_return, in, out, fname, seqid); } - virtual void dispatchCall(apache::thrift::stdcxx::function _return, + virtual void dispatchCall(std::function _return, apache::thrift::protocol::TProtocol* in, apache::thrift::protocol::TProtocol* out, const std::string& fname, int32_t seqid) = 0; - virtual void dispatchCallTemplated(apache::thrift::stdcxx::function _return, + virtual void dispatchCallTemplated(std::function _return, Protocol_* in, Protocol_* out, const std::string& fname, @@ -106,9 +106,9 @@ class TAsyncDispatchProcessorT : public TAsyncProcessor { */ class TAsyncDispatchProcessor : public TAsyncProcessor { public: - virtual void process(apache::thrift::stdcxx::function _return, - stdcxx::shared_ptr in, - stdcxx::shared_ptr out) { + void process(std::function _return, + std::shared_ptr in, + std::shared_ptr out) override { protocol::TProtocol* inRaw = in.get(); protocol::TProtocol* outRaw = out.get(); @@ -131,7 +131,7 @@ class TAsyncDispatchProcessor : public TAsyncProcessor { return dispatchCall(_return, inRaw, outRaw, fname, seqid); } - virtual void dispatchCall(apache::thrift::stdcxx::function _return, + virtual void dispatchCall(std::function _return, apache::thrift::protocol::TProtocol* in, apache::thrift::protocol::TProtocol* out, const std::string& fname, diff --git a/lib/cpp/src/thrift/async/TAsyncProcessor.h b/lib/cpp/src/thrift/async/TAsyncProcessor.h index afc4ffaa43f..01923394587 100644 --- a/lib/cpp/src/thrift/async/TAsyncProcessor.h +++ b/lib/cpp/src/thrift/async/TAsyncProcessor.h @@ -21,7 +21,7 @@ #define _THRIFT_TASYNCPROCESSOR_H_ 1 #include -#include +#include #include namespace apache { @@ -35,32 +35,32 @@ namespace async { class TAsyncProcessor { public: - virtual ~TAsyncProcessor() {} + virtual ~TAsyncProcessor() = default; - virtual void process(stdcxx::function _return, - stdcxx::shared_ptr in, - stdcxx::shared_ptr out) = 0; + virtual void process(std::function _return, + std::shared_ptr in, + std::shared_ptr out) = 0; - void process(stdcxx::function _return, - stdcxx::shared_ptr io) { + void process(std::function _return, + std::shared_ptr io) { return process(_return, io, io); } - stdcxx::shared_ptr getEventHandler() const { return eventHandler_; } + std::shared_ptr getEventHandler() const { return eventHandler_; } - void setEventHandler(stdcxx::shared_ptr eventHandler) { + void setEventHandler(std::shared_ptr eventHandler) { eventHandler_ = eventHandler; } protected: - TAsyncProcessor() {} + TAsyncProcessor() = default; - stdcxx::shared_ptr eventHandler_; + std::shared_ptr eventHandler_; }; class TAsyncProcessorFactory { public: - virtual ~TAsyncProcessorFactory() {} + virtual ~TAsyncProcessorFactory() = default; /** * Get the TAsyncProcessor to use for a particular connection. @@ -69,7 +69,7 @@ class TAsyncProcessorFactory { * 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 stdcxx::shared_ptr getProcessor(const TConnectionInfo& connInfo) = 0; + virtual std::shared_ptr getProcessor(const TConnectionInfo& connInfo) = 0; }; } } diff --git a/lib/cpp/src/thrift/async/TAsyncProtocolProcessor.cpp b/lib/cpp/src/thrift/async/TAsyncProtocolProcessor.cpp index b9ffb044802..cb5201bf65b 100644 --- a/lib/cpp/src/thrift/async/TAsyncProtocolProcessor.cpp +++ b/lib/cpp/src/thrift/async/TAsyncProtocolProcessor.cpp @@ -26,23 +26,23 @@ namespace apache { namespace thrift { namespace async { -void TAsyncProtocolProcessor::process(apache::thrift::stdcxx::function _return, - stdcxx::shared_ptr ibuf, - stdcxx::shared_ptr obuf) { - stdcxx::shared_ptr iprot(pfact_->getProtocol(ibuf)); - stdcxx::shared_ptr oprot(pfact_->getProtocol(obuf)); +void TAsyncProtocolProcessor::process(std::function _return, + std::shared_ptr ibuf, + std::shared_ptr obuf) { + std::shared_ptr iprot(pfact_->getProtocol(ibuf)); + std::shared_ptr oprot(pfact_->getProtocol(obuf)); return underlying_ - ->process(apache::thrift::stdcxx::bind(&TAsyncProtocolProcessor::finish, + ->process(std::bind(&TAsyncProtocolProcessor::finish, _return, oprot, - apache::thrift::stdcxx::placeholders::_1), + std::placeholders::_1), iprot, oprot); } /* static */ void TAsyncProtocolProcessor::finish( - apache::thrift::stdcxx::function _return, - stdcxx::shared_ptr oprot, + std::function _return, + std::shared_ptr oprot, bool healthy) { (void)oprot; // This is a stub function to hold a reference to oprot. diff --git a/lib/cpp/src/thrift/async/TAsyncProtocolProcessor.h b/lib/cpp/src/thrift/async/TAsyncProtocolProcessor.h index ce3883c2f23..ace72b6dcb5 100644 --- a/lib/cpp/src/thrift/async/TAsyncProtocolProcessor.h +++ b/lib/cpp/src/thrift/async/TAsyncProtocolProcessor.h @@ -30,23 +30,23 @@ namespace async { class TAsyncProtocolProcessor : public TAsyncBufferProcessor { public: - TAsyncProtocolProcessor(stdcxx::shared_ptr underlying, - stdcxx::shared_ptr pfact) + TAsyncProtocolProcessor(std::shared_ptr underlying, + std::shared_ptr pfact) : underlying_(underlying), pfact_(pfact) {} - virtual void process(apache::thrift::stdcxx::function _return, - stdcxx::shared_ptr ibuf, - stdcxx::shared_ptr obuf); + void process(std::function _return, + std::shared_ptr ibuf, + std::shared_ptr obuf) override; - virtual ~TAsyncProtocolProcessor() {} + ~TAsyncProtocolProcessor() override = default; private: - static void finish(apache::thrift::stdcxx::function _return, - stdcxx::shared_ptr oprot, + static void finish(std::function _return, + std::shared_ptr oprot, bool healthy); - stdcxx::shared_ptr underlying_; - stdcxx::shared_ptr pfact_; + std::shared_ptr underlying_; + std::shared_ptr pfact_; }; } } diff --git a/lib/cpp/src/thrift/async/TConcurrentClientSyncInfo.cpp b/lib/cpp/src/thrift/async/TConcurrentClientSyncInfo.cpp index c7e27c07873..0dac52458d6 100644 --- a/lib/cpp/src/thrift/async/TConcurrentClientSyncInfo.cpp +++ b/lib/cpp/src/thrift/async/TConcurrentClientSyncInfo.cpp @@ -17,10 +17,11 @@ * under the License. */ -#include +#include +#include #include +#include #include -#include namespace apache { namespace thrift { namespace async { @@ -75,7 +76,7 @@ void TConcurrentClientSyncInfo::updatePending( MonitorPtr monitor; { Guard seqidGuard(seqidMutex_); - MonitorMap::iterator i = seqidToMonitorMap_.find(rseqid); + auto i = seqidToMonitorMap_.find(rseqid); if(i == seqidToMonitorMap_.end()) throwBadSeqId_(); monitor = i->second; @@ -140,15 +141,15 @@ void TConcurrentClientSyncInfo::markBad_(const Guard &) { wakeupSomeone_ = true; stop_ = true; - for(MonitorMap::iterator i = seqidToMonitorMap_.begin(); i != seqidToMonitorMap_.end(); ++i) - i->second->notify(); + for(auto & i : seqidToMonitorMap_) + i.second->notify(); } TConcurrentClientSyncInfo::MonitorPtr TConcurrentClientSyncInfo::newMonitor_(const Guard &) { if(freeMonitors_.empty()) - return MonitorPtr(new Monitor(&readMutex_)); + return std::make_shared(&readMutex_); MonitorPtr retval; //swapping to avoid an atomic operation retval.swap(freeMonitors_.back()); diff --git a/lib/cpp/src/thrift/async/TConcurrentClientSyncInfo.h b/lib/cpp/src/thrift/async/TConcurrentClientSyncInfo.h index 9ec77b962ca..0bc5eb565f5 100644 --- a/lib/cpp/src/thrift/async/TConcurrentClientSyncInfo.h +++ b/lib/cpp/src/thrift/async/TConcurrentClientSyncInfo.h @@ -22,7 +22,7 @@ #include #include #include -#include +#include #include #include #include @@ -60,7 +60,7 @@ class TConcurrentRecvSentry { class TConcurrentClientSyncInfo { private: // typedefs - typedef stdcxx::shared_ptr< ::apache::thrift::concurrency::Monitor> MonitorPtr; + typedef std::shared_ptr< ::apache::thrift::concurrency::Monitor> MonitorPtr; typedef std::map MonitorMap; public: diff --git a/lib/cpp/src/thrift/async/TEvhttpClientChannel.cpp b/lib/cpp/src/thrift/async/TEvhttpClientChannel.cpp index 6af8104b649..7656596859a 100644 --- a/lib/cpp/src/thrift/async/TEvhttpClientChannel.cpp +++ b/lib/cpp/src/thrift/async/TEvhttpClientChannel.cpp @@ -41,15 +41,15 @@ TEvhttpClientChannel::TEvhttpClientChannel(const std::string& host, struct event_base* eb, struct evdns_base* dnsbase) - : host_(host), path_(path), conn_(NULL) { + : host_(host), path_(path), conn_(nullptr) { conn_ = evhttp_connection_base_new(eb, dnsbase, address, port); - if (conn_ == NULL) { + if (conn_ == nullptr) { throw TException("evhttp_connection_new failed"); } } TEvhttpClientChannel::~TEvhttpClientChannel() { - if (conn_ != NULL) { + if (conn_ != nullptr) { evhttp_connection_free(conn_); } } @@ -58,7 +58,7 @@ 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 == NULL) { + if (req == nullptr) { throw TException("evhttp_request_new failed"); } @@ -110,7 +110,7 @@ void TEvhttpClientChannel::finish(struct evhttp_request* req) { assert(!completionQueue_.empty()); Completion completion = completionQueue_.front(); completionQueue_.pop(); - if (req == NULL) { + if (req == nullptr) { try { completion.first(); } catch (const TTransportException& e) { @@ -142,7 +142,7 @@ void TEvhttpClientChannel::finish(struct evhttp_request* req) { } /* static */ void TEvhttpClientChannel::response(struct evhttp_request* req, void* arg) { - TEvhttpClientChannel* self = (TEvhttpClientChannel*)arg; + auto* self = (TEvhttpClientChannel*)arg; try { self->finish(req); } catch (std::exception& e) { diff --git a/lib/cpp/src/thrift/async/TEvhttpClientChannel.h b/lib/cpp/src/thrift/async/TEvhttpClientChannel.h index 3515ca22f12..f74272665b8 100644 --- a/lib/cpp/src/thrift/async/TEvhttpClientChannel.h +++ b/lib/cpp/src/thrift/async/TEvhttpClientChannel.h @@ -23,7 +23,7 @@ #include #include #include -#include +#include #include struct event_base; @@ -52,24 +52,24 @@ class TEvhttpClientChannel : public TAsyncChannel { const char* address, int port, struct event_base* eb, - struct evdns_base *dnsbase = 0); - ~TEvhttpClientChannel(); + struct evdns_base *dnsbase = nullptr); + ~TEvhttpClientChannel() override; - virtual void sendAndRecvMessage(const VoidCallback& cob, + void sendAndRecvMessage(const VoidCallback& cob, apache::thrift::transport::TMemoryBuffer* sendBuf, - apache::thrift::transport::TMemoryBuffer* recvBuf); + apache::thrift::transport::TMemoryBuffer* recvBuf) override; - virtual void sendMessage(const VoidCallback& cob, - apache::thrift::transport::TMemoryBuffer* message); - virtual void recvMessage(const VoidCallback& cob, - apache::thrift::transport::TMemoryBuffer* message); + 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 - virtual bool good() const { return true; } - virtual bool error() const { return false; } - virtual bool timedOut() const { return false; } + 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); diff --git a/lib/cpp/src/thrift/async/TEvhttpServer.cpp b/lib/cpp/src/thrift/async/TEvhttpServer.cpp index d87e507d82c..7d2cf21c0fc 100644 --- a/lib/cpp/src/thrift/async/TEvhttpServer.cpp +++ b/lib/cpp/src/thrift/async/TEvhttpServer.cpp @@ -20,7 +20,7 @@ #include #include #include -#include +#include #include #include #include @@ -31,8 +31,7 @@ #endif using apache::thrift::transport::TMemoryBuffer; -using apache::thrift::stdcxx::scoped_ptr; -using apache::thrift::stdcxx::shared_ptr; +using std::shared_ptr; namespace apache { namespace thrift { @@ -40,31 +39,31 @@ namespace async { struct TEvhttpServer::RequestContext { struct evhttp_request* req; - stdcxx::shared_ptr ibuf; - stdcxx::shared_ptr obuf; + std::shared_ptr ibuf; + std::shared_ptr obuf; RequestContext(struct evhttp_request* req); }; -TEvhttpServer::TEvhttpServer(stdcxx::shared_ptr processor) - : processor_(processor), eb_(NULL), eh_(NULL) { +TEvhttpServer::TEvhttpServer(std::shared_ptr processor) + : processor_(processor), eb_(nullptr), eh_(nullptr) { } -TEvhttpServer::TEvhttpServer(stdcxx::shared_ptr processor, int port) - : processor_(processor), eb_(NULL), eh_(NULL) { +TEvhttpServer::TEvhttpServer(std::shared_ptr processor, int port) + : processor_(processor), eb_(nullptr), eh_(nullptr) { // Create event_base and evhttp. eb_ = event_base_new(); - if (eb_ == NULL) { + if (eb_ == nullptr) { throw TException("event_base_new failed"); } eh_ = evhttp_new(eb_); - if (eh_ == NULL) { + if (eh_ == nullptr) { event_base_free(eb_); throw TException("evhttp_new failed"); } // Bind to port. - int ret = evhttp_bind_socket(eh_, NULL, port); + int ret = evhttp_bind_socket(eh_, nullptr, port); if (ret < 0) { evhttp_free(eh_); event_base_free(eb_); @@ -78,16 +77,16 @@ TEvhttpServer::TEvhttpServer(stdcxx::shared_ptr processor } TEvhttpServer::~TEvhttpServer() { - if (eh_ != NULL) { + if (eh_ != nullptr) { evhttp_free(eh_); } - if (eb_ != NULL) { + if (eb_ != nullptr) { event_base_free(eb_); } } int TEvhttpServer::serve() { - if (eb_ == NULL) { + if (eb_ == nullptr) { throw TException("Unexpected call to TEvhttpServer::serve"); } return event_base_dispatch(eb_); @@ -104,23 +103,23 @@ void TEvhttpServer::request(struct evhttp_request* req, void* self) { try { static_cast(self)->process(req); } catch (std::exception& e) { - evhttp_send_reply(req, HTTP_INTERNAL, e.what(), 0); + evhttp_send_reply(req, HTTP_INTERNAL, e.what(), nullptr); } } void TEvhttpServer::process(struct evhttp_request* req) { - RequestContext* ctx = new RequestContext(req); - return processor_->process(apache::thrift::stdcxx::bind(&TEvhttpServer::complete, + auto* ctx = new RequestContext(req); + return processor_->process(std::bind(&TEvhttpServer::complete, this, ctx, - apache::thrift::stdcxx::placeholders::_1), + std::placeholders::_1), ctx->ibuf, ctx->obuf); } void TEvhttpServer::complete(RequestContext* ctx, bool success) { (void)success; - scoped_ptr ptr(ctx); + std::unique_ptr ptr(ctx); int code = success ? 200 : 400; const char* reason = success ? "OK" : "Bad Request"; @@ -132,7 +131,7 @@ void TEvhttpServer::complete(RequestContext* ctx, bool success) { } struct evbuffer* buf = evbuffer_new(); - if (buf == NULL) { + if (buf == nullptr) { // TODO: Log an error. std::cerr << "evbuffer_new failed " << __FILE__ << ":" << __LINE__ << std::endl; } else { @@ -148,7 +147,7 @@ void TEvhttpServer::complete(RequestContext* ctx, bool success) { } evhttp_send_reply(ctx->req, code, reason, buf); - if (buf != NULL) { + if (buf != nullptr) { evbuffer_free(buf); } } diff --git a/lib/cpp/src/thrift/async/TEvhttpServer.h b/lib/cpp/src/thrift/async/TEvhttpServer.h index afc679ccfc6..c5bf3b6eef4 100644 --- a/lib/cpp/src/thrift/async/TEvhttpServer.h +++ b/lib/cpp/src/thrift/async/TEvhttpServer.h @@ -20,7 +20,7 @@ #ifndef _THRIFT_TEVHTTP_SERVER_H_ #define _THRIFT_TEVHTTP_SERVER_H_ 1 -#include +#include struct event_base; struct evhttp; @@ -41,14 +41,14 @@ class TEvhttpServer { * address of the server as the extra arg. * Do not call "serve" on this server. */ - TEvhttpServer(stdcxx::shared_ptr processor); + TEvhttpServer(std::shared_ptr 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(stdcxx::shared_ptr processor, int port); + TEvhttpServer(std::shared_ptr processor, int port); ~TEvhttpServer(); @@ -63,7 +63,7 @@ class TEvhttpServer { void process(struct evhttp_request* req); void complete(RequestContext* ctx, bool success); - stdcxx::shared_ptr processor_; + std::shared_ptr processor_; struct event_base* eb_; struct evhttp* eh_; }; diff --git a/lib/cpp/src/thrift/concurrency/BoostMonitor.cpp b/lib/cpp/src/thrift/concurrency/BoostMonitor.cpp deleted file mode 100644 index ebfa0b9dd9a..00000000000 --- a/lib/cpp/src/thrift/concurrency/BoostMonitor.cpp +++ /dev/null @@ -1,214 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -#include - -#include -#include -#include -#include -#include - -#include -#include -#include - -namespace apache { -namespace thrift { -namespace concurrency { - -/** - * Monitor implementation using the boost thread library - * - * @version $Id:$ - */ -class Monitor::Impl : public boost::condition_variable_any { - -public: - Impl() : ownedMutex_(new Mutex()), mutex_(NULL) { init(ownedMutex_.get()); } - - Impl(Mutex* mutex) : mutex_(NULL) { init(mutex); } - - Impl(Monitor* monitor) : mutex_(NULL) { 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(int64_t timeout_ms) { - int result = waitForTimeRelative(timeout_ms); - 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_ms == 0. - * - * Returns 0 if condition occurs, THRIFT_ETIMEDOUT on timeout, or an error code. - */ - int waitForTimeRelative(int64_t timeout_ms) { - if (timeout_ms == 0LL) { - return waitForever(); - } - - assert(mutex_); - boost::timed_mutex* mutexImpl - = reinterpret_cast(mutex_->getUnderlyingImpl()); - assert(mutexImpl); - - boost::timed_mutex::scoped_lock lock(*mutexImpl, boost::adopt_lock); - int res - = timed_wait(lock, boost::get_system_time() + boost::posix_time::milliseconds(timeout_ms)) - ? 0 - : THRIFT_ETIMEDOUT; - lock.release(); - return res; - } - - /** - * Waits until the absolute time specified using struct THRIFT_TIMESPEC. - * Returns 0 if condition occurs, THRIFT_ETIMEDOUT on timeout, or an error code. - */ - int waitForTime(const THRIFT_TIMESPEC* abstime) { - struct timeval temp; - temp.tv_sec = static_cast(abstime->tv_sec); - temp.tv_usec = static_cast(abstime->tv_nsec) / 1000; - return waitForTime(&temp); - } - - /** - * Waits until the absolute time specified using struct timeval. - * Returns 0 if condition occurs, THRIFT_ETIMEDOUT on timeout, or an error code. - */ - int waitForTime(const struct timeval* abstime) { - assert(mutex_); - boost::timed_mutex* mutexImpl = static_cast(mutex_->getUnderlyingImpl()); - assert(mutexImpl); - - struct timeval currenttime; - Util::toTimeval(currenttime, Util::currentTime()); - - long tv_sec = static_cast(abstime->tv_sec - currenttime.tv_sec); - long tv_usec = static_cast(abstime->tv_usec - currenttime.tv_usec); - if (tv_sec < 0) - tv_sec = 0; - if (tv_usec < 0) - tv_usec = 0; - - boost::timed_mutex::scoped_lock lock(*mutexImpl, boost::adopt_lock); - int res = timed_wait(lock, - boost::get_system_time() + boost::posix_time::seconds(tv_sec) - + boost::posix_time::microseconds(tv_usec)) - ? 0 - : THRIFT_ETIMEDOUT; - lock.release(); - return res; - } - - /** - * Waits forever until the condition occurs. - * Returns 0 if condition occurs, or an error code otherwise. - */ - int waitForever() { - assert(mutex_); - boost::timed_mutex* mutexImpl - = reinterpret_cast(mutex_->getUnderlyingImpl()); - assert(mutexImpl); - - boost::timed_mutex::scoped_lock lock(*mutexImpl, boost::adopt_lock); - ((boost::condition_variable_any*)this)->wait(lock); - lock.release(); - return 0; - } - - void notify() { notify_one(); } - - void notifyAll() { notify_all(); } - -private: - void init(Mutex* mutex) { mutex_ = mutex; } - - stdcxx::scoped_ptr ownedMutex_; - 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(impl_)->mutex(); -} - -void Monitor::lock() const { - const_cast(impl_)->lock(); -} - -void Monitor::unlock() const { - const_cast(impl_)->unlock(); -} - -void Monitor::wait(int64_t timeout) const { - const_cast(impl_)->wait(timeout); -} - -int Monitor::waitForTime(const THRIFT_TIMESPEC* abstime) const { - return const_cast(impl_)->waitForTime(abstime); -} - -int Monitor::waitForTime(const timeval* abstime) const { - return const_cast(impl_)->waitForTime(abstime); -} - -int Monitor::waitForTimeRelative(int64_t timeout_ms) const { - return const_cast(impl_)->waitForTimeRelative(timeout_ms); -} - -int Monitor::waitForever() const { - return const_cast(impl_)->waitForever(); -} - -void Monitor::notify() const { - const_cast(impl_)->notify(); -} - -void Monitor::notifyAll() const { - const_cast(impl_)->notifyAll(); -} -} -} -} // apache::thrift::concurrency diff --git a/lib/cpp/src/thrift/concurrency/BoostMutex.cpp b/lib/cpp/src/thrift/concurrency/BoostMutex.cpp deleted file mode 100644 index 4e556df1702..00000000000 --- a/lib/cpp/src/thrift/concurrency/BoostMutex.cpp +++ /dev/null @@ -1,73 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -#include - -#include -#include -#include - -#include -#include -#include -#include - -namespace apache { -namespace thrift { -namespace concurrency { - -/** - * Implementation of Mutex class using boost::timed_mutex - * - * Methods throw boost::lock_error on error. - * - * @version $Id:$ - */ -class Mutex::impl : public boost::timed_mutex {}; - -Mutex::Mutex(Initializer init) : impl_(new Mutex::impl()) { - THRIFT_UNUSED_VARIABLE(init); -} - -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_->timed_lock(boost::get_system_time() + boost::posix_time::milliseconds(ms)); -} - -void Mutex::unlock() const { - impl_->unlock(); -} - -void Mutex::DEFAULT_INITIALIZER(void* arg) { - THRIFT_UNUSED_VARIABLE(arg); -} -} -} -} // apache::thrift::concurrency diff --git a/lib/cpp/src/thrift/concurrency/BoostThreadFactory.cpp b/lib/cpp/src/thrift/concurrency/BoostThreadFactory.cpp deleted file mode 100644 index d7d8d54e902..00000000000 --- a/lib/cpp/src/thrift/concurrency/BoostThreadFactory.cpp +++ /dev/null @@ -1,162 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -#include - -#if USE_BOOST_THREAD - -#include -#include -#include -#include - -#include - -namespace apache { -namespace thrift { - -using stdcxx::bind; -using stdcxx::scoped_ptr; -using stdcxx::shared_ptr; -using stdcxx::weak_ptr; - -namespace concurrency { - -/** - * The boost thread class. - * - * @version $Id:$ - */ -class BoostThread : public Thread { -public: - enum STATE { uninitialized, starting, started, stopping, stopped }; - - static void* threadMain(void* arg); - -private: - scoped_ptr thread_; - Monitor monitor_; - STATE state_; - weak_ptr self_; - bool detached_; - -public: - BoostThread(bool detached, shared_ptr runnable) - : state_(uninitialized), detached_(detached) { - this->Thread::runnable(runnable); - } - - ~BoostThread() { - 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(); - } - } - - void start() { - // Create reference - shared_ptr* selfRef = new shared_ptr(); - *selfRef = self_.lock(); - - setState(starting); - - Synchronized sync(monitor_); - - thread_.reset(new boost::thread(bind(threadMain, (void*)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(); - } - - void join() { - if (!detached_ && getState() != uninitialized) { - thread_->join(); - } - } - - Thread::id_t getId() { return thread_.get() ? thread_->get_id() : boost::thread::id(); } - - shared_ptr runnable() const { return Thread::runnable(); } - - void runnable(shared_ptr value) { Thread::runnable(value); } - - void weakRef(shared_ptr self) { - assert(self.get() == this); - self_ = weak_ptr(self); - } -}; - -void* BoostThread::threadMain(void* arg) { - shared_ptr thread = *(shared_ptr*)arg; - delete reinterpret_cast*>(arg); - - thread->setState(started); - thread->runnable()->run(); - - if (thread->getState() != stopping && thread->getState() != stopped) { - thread->setState(stopping); - } - return (void*)0; -} - -BoostThreadFactory::BoostThreadFactory(bool detached) - : ThreadFactory(detached) { -} - -shared_ptr BoostThreadFactory::newThread(shared_ptr runnable) const { - shared_ptr result = shared_ptr(new BoostThread(isDetached(), runnable)); - result->weakRef(result); - runnable->thread(result); - return result; -} - -Thread::id_t BoostThreadFactory::getCurrentThreadId() const { - return boost::this_thread::get_id(); -} -} -} -} // apache::thrift::concurrency - -#endif // USE_BOOST_THREAD diff --git a/lib/cpp/src/thrift/concurrency/BoostThreadFactory.h b/lib/cpp/src/thrift/concurrency/BoostThreadFactory.h deleted file mode 100644 index bf11a708bf8..00000000000 --- a/lib/cpp/src/thrift/concurrency/BoostThreadFactory.h +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -#ifndef _THRIFT_CONCURRENCY_BOOSTTHREADFACTORY_H_ -#define _THRIFT_CONCURRENCY_BOOSTTHREADFACTORY_H_ 1 - -#include -#include -#include - -namespace apache { -namespace thrift { -namespace concurrency { - -/** - * A thread factory to create posix threads - * - * @version $Id:$ - */ -class BoostThreadFactory : public ThreadFactory { - -public: - /** - * Boost thread factory. All threads created by a factory are reference-counted - * via stdcxx::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. - * - * Threads are created with the specified boost policy, priority, stack-size. A detachable thread - * is not joinable. - * - * By default threads are not joinable. - */ - - BoostThreadFactory(bool detached = true); - - // From ThreadFactory; - stdcxx::shared_ptr newThread(stdcxx::shared_ptr runnable) const; - - // From ThreadFactory; - Thread::id_t getCurrentThreadId() const; -}; - -} -} -} // apache::thrift::concurrency - -#endif // #ifndef _THRIFT_CONCURRENCY_BOOSTTHREADFACTORY_H_ diff --git a/lib/cpp/src/thrift/concurrency/Exception.h b/lib/cpp/src/thrift/concurrency/Exception.h index 6438fda101e..947fc9f04a0 100644 --- a/lib/cpp/src/thrift/concurrency/Exception.h +++ b/lib/cpp/src/thrift/concurrency/Exception.h @@ -35,7 +35,7 @@ class InvalidArgumentException : public apache::thrift::TException {}; class IllegalStateException : public apache::thrift::TException { public: - IllegalStateException() {} + IllegalStateException() = default; IllegalStateException(const std::string& message) : TException(message) {} }; @@ -53,7 +53,7 @@ class TooManyPendingTasksException : public apache::thrift::TException { class SystemResourceException : public apache::thrift::TException { public: - SystemResourceException() {} + SystemResourceException() = default; SystemResourceException(const std::string& message) : TException(message) {} }; diff --git a/lib/cpp/src/thrift/concurrency/FunctionRunner.h b/lib/cpp/src/thrift/concurrency/FunctionRunner.h index eabf019b759..46883441666 100644 --- a/lib/cpp/src/thrift/concurrency/FunctionRunner.h +++ b/lib/cpp/src/thrift/concurrency/FunctionRunner.h @@ -21,7 +21,7 @@ #define _THRIFT_CONCURRENCY_FUNCTION_RUNNER_H 1 #include -#include +#include namespace apache { namespace thrift { @@ -44,7 +44,7 @@ namespace concurrency { * A* a = new A(); * // To create a thread that executes a.foo() every 100 milliseconds: * factory->newThread(FunctionRunner::create( - * apache::thrift::stdcxx::bind(&A::foo, a), 100))->start(); + * std::bind(&A::foo, a), 100))->start(); * */ @@ -53,20 +53,20 @@ class FunctionRunner : public Runnable { // 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 stdcxx::function VoidFunc; + typedef std::function VoidFunc; - typedef stdcxx::function BoolFunc; + typedef std::function BoolFunc; /** * Syntactic sugar to make it easier to create new FunctionRunner * objects wrapped in shared_ptr. */ - static stdcxx::shared_ptr create(const VoidFunc& cob) { - return stdcxx::shared_ptr(new FunctionRunner(cob)); + static std::shared_ptr create(const VoidFunc& cob) { + return std::shared_ptr(new FunctionRunner(cob)); } - static stdcxx::shared_ptr create(PthreadFuncPtr func, void* arg) { - return stdcxx::shared_ptr(new FunctionRunner(func, arg)); + static std::shared_ptr create(PthreadFuncPtr func, void* arg) { + return std::shared_ptr(new FunctionRunner(func, arg)); } private: @@ -81,7 +81,7 @@ class FunctionRunner : public Runnable { * execute the given callback. Note that the 'void*' return value is ignored. */ FunctionRunner(PthreadFuncPtr func, void* arg) - : func_(stdcxx::bind(pthread_func_wrapper, func, arg)), intervalMs_(-1) {} + : func_(std::bind(pthread_func_wrapper, func, arg)), intervalMs_(-1) {} /** * Given a generic callback, this FunctionRunner will execute it. @@ -96,7 +96,7 @@ class FunctionRunner : public Runnable { */ FunctionRunner(const BoolFunc& cob, int intervalMs) : repFunc_(cob), intervalMs_(intervalMs) {} - void run() { + void run() override { if (repFunc_) { while (repFunc_()) { THRIFT_SLEEP_USEC(intervalMs_ * 1000); diff --git a/lib/cpp/src/thrift/concurrency/Monitor.cpp b/lib/cpp/src/thrift/concurrency/Monitor.cpp index af4fcd09c50..dc92efd6cd5 100644 --- a/lib/cpp/src/thrift/concurrency/Monitor.cpp +++ b/lib/cpp/src/thrift/concurrency/Monitor.cpp @@ -21,45 +21,37 @@ #include #include -#include #include -#include - #include -#include - -#include +#include +#include +#include +#include namespace apache { namespace thrift { - -using stdcxx::scoped_ptr; -using stdcxx::shared_ptr; - namespace concurrency { /** - * Monitor implementation using the POSIX pthread library + * Monitor implementation using the std thread library * * @version $Id:$ */ class Monitor::Impl { public: - Impl() : ownedMutex_(new Mutex()), mutex_(NULL), condInitialized_(false) { - init(ownedMutex_.get()); - } - - Impl(Mutex* mutex) : mutex_(NULL), condInitialized_(false) { init(mutex); } + Impl() : ownedMutex_(new Mutex()), conditionVariable_(), mutex_(nullptr) { init(ownedMutex_.get()); } - Impl(Monitor* monitor) : mutex_(NULL), condInitialized_(false) { init(&(monitor->mutex())); } + Impl(Mutex* mutex) : ownedMutex_(), conditionVariable_(), mutex_(nullptr) { init(mutex); } - ~Impl() { cleanup(); } + Impl(Monitor* monitor) : ownedMutex_(), conditionVariable_(), mutex_(nullptr) { + init(&(monitor->mutex())); + } Mutex& mutex() { return *mutex_; } - void lock() { mutex().lock(); } - void unlock() { mutex().unlock(); } + void lock() { mutex_->lock(); } + void unlock() { mutex_->unlock(); } /** * Exception-throwing version of waitForTimeRelative(), called simply @@ -68,106 +60,78 @@ class Monitor::Impl { * If the condition occurs, this function returns cleanly; on timeout or * error an exception is thrown. */ - void wait(int64_t timeout_ms) const { - int result = waitForTimeRelative(timeout_ms); + void wait(const std::chrono::milliseconds &timeout) { + int result = waitForTimeRelative(timeout); if (result == THRIFT_ETIMEDOUT) { - // pthread_cond_timedwait has been observed to return early on - // various platforms, so comment out this assert. - // assert(Util::currentTime() >= (now + timeout)); throw TimedOutException(); } else if (result != 0) { - throw TException("pthread_cond_wait() or pthread_cond_timedwait() failed"); + throw TException("Monitor::wait() failed"); } } /** * Waits until the specified timeout in milliseconds for the condition to - * occur, or waits forever if timeout_ms == 0. + * occur, or waits forever if timeout is zero. * * Returns 0 if condition occurs, THRIFT_ETIMEDOUT on timeout, or an error code. */ - int waitForTimeRelative(int64_t timeout_ms) const { - if (timeout_ms == 0LL) { + int waitForTimeRelative(const std::chrono::milliseconds &timeout) { + if (timeout.count() == 0) { return waitForever(); } - struct THRIFT_TIMESPEC abstime; - Util::toTimespec(abstime, Util::currentTime() + timeout_ms); - return waitForTime(&abstime); + assert(mutex_); + auto* mutexImpl = static_cast(mutex_->getUnderlyingImpl()); + assert(mutexImpl); + + std::unique_lock 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 using struct THRIFT_TIMESPEC. + * Waits until the absolute time specified by abstime. * Returns 0 if condition occurs, THRIFT_ETIMEDOUT on timeout, or an error code. */ - int waitForTime(const THRIFT_TIMESPEC* abstime) const { + int waitForTime(const std::chrono::time_point& abstime) { assert(mutex_); - pthread_mutex_t* mutexImpl = reinterpret_cast(mutex_->getUnderlyingImpl()); + auto* mutexImpl = static_cast(mutex_->getUnderlyingImpl()); assert(mutexImpl); - // XXX Need to assert that caller owns mutex - return pthread_cond_timedwait(&pthread_cond_, mutexImpl, abstime); + std::unique_lock lock(*mutexImpl, std::adopt_lock); + bool timedout = (conditionVariable_.wait_until(lock, abstime) + == std::cv_status::timeout); + lock.release(); + return (timedout ? THRIFT_ETIMEDOUT : 0); } - int waitForTime(const struct timeval* abstime) const { - struct THRIFT_TIMESPEC temp; - temp.tv_sec = abstime->tv_sec; - temp.tv_nsec = abstime->tv_usec * 1000; - return waitForTime(&temp); - } /** * Waits forever until the condition occurs. * Returns 0 if condition occurs, or an error code otherwise. */ - int waitForever() const { + int waitForever() { assert(mutex_); - pthread_mutex_t* mutexImpl = reinterpret_cast(mutex_->getUnderlyingImpl()); + auto* mutexImpl = static_cast(mutex_->getUnderlyingImpl()); assert(mutexImpl); - return pthread_cond_wait(&pthread_cond_, mutexImpl); - } - void notify() { - // XXX Need to assert that caller owns mutex - int iret = pthread_cond_signal(&pthread_cond_); - THRIFT_UNUSED_VARIABLE(iret); - assert(iret == 0); + std::unique_lock lock(*mutexImpl, std::adopt_lock); + conditionVariable_.wait(lock); + lock.release(); + return 0; } - void notifyAll() { - // XXX Need to assert that caller owns mutex - int iret = pthread_cond_broadcast(&pthread_cond_); - THRIFT_UNUSED_VARIABLE(iret); - assert(iret == 0); - } + void notify() { conditionVariable_.notify_one(); } -private: - void init(Mutex* mutex) { - mutex_ = mutex; - - if (pthread_cond_init(&pthread_cond_, NULL) == 0) { - condInitialized_ = true; - } - - if (!condInitialized_) { - cleanup(); - throw SystemResourceException(); - } - } + void notifyAll() { conditionVariable_.notify_all(); } - void cleanup() { - if (condInitialized_) { - condInitialized_ = false; - int iret = pthread_cond_destroy(&pthread_cond_); - THRIFT_UNUSED_VARIABLE(iret); - assert(iret == 0); - } - } +private: + void init(Mutex* mutex) { mutex_ = mutex; } - scoped_ptr ownedMutex_; + const std::unique_ptr ownedMutex_; + std::condition_variable_any conditionVariable_; Mutex* mutex_; - - mutable pthread_cond_t pthread_cond_; - mutable bool condInitialized_; }; Monitor::Monitor() : impl_(new Monitor::Impl()) { @@ -182,43 +146,39 @@ Monitor::~Monitor() { } Mutex& Monitor::mutex() const { - return impl_->mutex(); + return const_cast(impl_)->mutex(); } void Monitor::lock() const { - impl_->lock(); + const_cast(impl_)->lock(); } void Monitor::unlock() const { - impl_->unlock(); -} - -void Monitor::wait(int64_t timeout) const { - impl_->wait(timeout); + const_cast(impl_)->unlock(); } -int Monitor::waitForTime(const THRIFT_TIMESPEC* abstime) const { - return impl_->waitForTime(abstime); +void Monitor::wait(const std::chrono::milliseconds &timeout) const { + const_cast(impl_)->wait(timeout); } -int Monitor::waitForTime(const timeval* abstime) const { - return impl_->waitForTime(abstime); +int Monitor::waitForTime(const std::chrono::time_point& abstime) const { + return const_cast(impl_)->waitForTime(abstime); } -int Monitor::waitForTimeRelative(int64_t timeout_ms) const { - return impl_->waitForTimeRelative(timeout_ms); +int Monitor::waitForTimeRelative(const std::chrono::milliseconds &timeout) const { + return const_cast(impl_)->waitForTimeRelative(timeout); } int Monitor::waitForever() const { - return impl_->waitForever(); + return const_cast(impl_)->waitForever(); } void Monitor::notify() const { - impl_->notify(); + const_cast(impl_)->notify(); } void Monitor::notifyAll() const { - impl_->notifyAll(); + const_cast(impl_)->notifyAll(); } } } diff --git a/lib/cpp/src/thrift/concurrency/Monitor.h b/lib/cpp/src/thrift/concurrency/Monitor.h index 2399a987986..b3939cb019b 100644 --- a/lib/cpp/src/thrift/concurrency/Monitor.h +++ b/lib/cpp/src/thrift/concurrency/Monitor.h @@ -20,15 +20,10 @@ #ifndef _THRIFT_CONCURRENCY_MONITOR_H_ #define _THRIFT_CONCURRENCY_MONITOR_H_ 1 -#ifdef HAVE_SYS_TIME_H -#include -#endif - +#include #include #include -#include - namespace apache { namespace thrift { namespace concurrency { @@ -73,23 +68,19 @@ class Monitor : boost::noncopyable { /** * Waits a maximum of the specified timeout in milliseconds for the condition - * to occur, or waits forever if timeout_ms == 0. + * to occur, or waits forever if timeout is zero. * * Returns 0 if condition occurs, THRIFT_ETIMEDOUT on timeout, or an error code. */ - int waitForTimeRelative(int64_t timeout_ms) const; + int waitForTimeRelative(const std::chrono::milliseconds &timeout) const; - /** - * Waits until the absolute time specified using struct THRIFT_TIMESPEC. - * Returns 0 if condition occurs, THRIFT_ETIMEDOUT on timeout, or an error code. - */ - int waitForTime(const THRIFT_TIMESPEC* abstime) const; + int waitForTimeRelative(uint64_t timeout_ms) const { return waitForTimeRelative(std::chrono::milliseconds(timeout_ms)); } /** - * Waits until the absolute time specified using struct timeval. + * Waits until the absolute time specified by abstime. * Returns 0 if condition occurs, THRIFT_ETIMEDOUT on timeout, or an error code. */ - int waitForTime(const struct timeval* abstime) const; + int waitForTime(const std::chrono::time_point& abstime) const; /** * Waits forever until the condition occurs. @@ -99,12 +90,14 @@ class Monitor : boost::noncopyable { /** * Exception-throwing version of waitForTimeRelative(), called simply - * wait(int64) for historical reasons. Timeout is in milliseconds. + * wait(std::chrono::milliseconds) for historical reasons. Timeout is in milliseconds. * - * If the condition occurs, this function returns cleanly; on timeout or + * If the condition occurs, this function returns cleanly; on timeout or * error an exception is thrown. */ - void wait(int64_t timeout_ms = 0LL) const; + 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; diff --git a/lib/cpp/src/thrift/concurrency/Mutex.cpp b/lib/cpp/src/thrift/concurrency/Mutex.cpp index a5264617d5a..75802835d0b 100644 --- a/lib/cpp/src/thrift/concurrency/Mutex.cpp +++ b/lib/cpp/src/thrift/concurrency/Mutex.cpp @@ -17,202 +17,29 @@ * under the License. */ -// needed to test for pthread implementation capabilities: -#define __USE_GNU - -#include - -#include -#include #include -#include - -#include -#include -#include -#include -#include -#include +#include +#include namespace apache { namespace thrift { namespace concurrency { -// Enable this to turn on mutex contention profiling support -// #define THRIFT_PTHREAD_MUTEX_CONTENTION_PROFILING - -#ifdef THRIFT_PTHREAD_MUTEX_CONTENTION_PROFILING - -static int32_t mutexProfilingCounter = 0; -static int32_t mutexProfilingSampleRate = 0; -static MutexWaitCallback mutexProfilingCallback = 0; - -void enableMutexProfiling(int32_t profilingSampleRate, MutexWaitCallback callback) { - mutexProfilingSampleRate = profilingSampleRate; - mutexProfilingCallback = callback; -} - -#define PROFILE_MUTEX_START_LOCK() int64_t _lock_startTime = maybeGetProfilingStartTime(); - -#define PROFILE_MUTEX_NOT_LOCKED() \ - do { \ - if (_lock_startTime > 0) { \ - int64_t endTime = Util::currentTimeUsec(); \ - (*mutexProfilingCallback)(this, endTime - _lock_startTime); \ - } \ - } while (0) - -#define PROFILE_MUTEX_LOCKED() \ - do { \ - profileTime_ = _lock_startTime; \ - if (profileTime_ > 0) { \ - profileTime_ = Util::currentTimeUsec() - profileTime_; \ - } \ - } while (0) - -#define PROFILE_MUTEX_START_UNLOCK() \ - int64_t _temp_profileTime = profileTime_; \ - profileTime_ = 0; - -#define PROFILE_MUTEX_UNLOCKED() \ - do { \ - if (_temp_profileTime > 0) { \ - (*mutexProfilingCallback)(this, _temp_profileTime); \ - } \ - } while (0) - -static inline int64_t maybeGetProfilingStartTime() { - if (mutexProfilingSampleRate && mutexProfilingCallback) { - // This block is unsynchronized, but should produce a reasonable sampling - // rate on most architectures. The main race conditions are the gap - // between the decrement and the test, the non-atomicity of decrement, and - // potential caching of different values at different CPUs. - // - // - if two decrements race, the likeliest result is that the counter - // decrements slowly (perhaps much more slowly) than intended. - // - // - many threads could potentially decrement before resetting the counter - // to its large value, causing each additional incoming thread to - // profile every call. This situation is unlikely to persist for long - // as the critical gap is quite short, but profiling could be bursty. - sig_atomic_t localValue = --mutexProfilingCounter; - if (localValue <= 0) { - mutexProfilingCounter = mutexProfilingSampleRate; - return Util::currentTimeUsec(); - } - } - - return 0; -} - -#else -#define PROFILE_MUTEX_START_LOCK() -#define PROFILE_MUTEX_NOT_LOCKED() -#define PROFILE_MUTEX_LOCKED() -#define PROFILE_MUTEX_START_UNLOCK() -#define PROFILE_MUTEX_UNLOCKED() -#endif // THRIFT_PTHREAD_MUTEX_CONTENTION_PROFILING - -#define EINTR_LOOP(_CALL) int ret; do { ret = _CALL; } while (ret == EINTR) -#define ABORT_ONFAIL(_CALL) { EINTR_LOOP(_CALL); if (ret) { abort(); } } -#define THROW_SRE(_CALLSTR, RET) { throw SystemResourceException(boost::str(boost::format("%1% returned %2% (%3%)") % _CALLSTR % RET % ::strerror(RET))); } -#define THROW_SRE_ONFAIL(_CALL) { EINTR_LOOP(_CALL); if (ret) { THROW_SRE(#_CALL, ret); } } -#define THROW_SRE_TRYFAIL(_CALL) { EINTR_LOOP(_CALL); if (ret == 0) { return true; } else if (ret == EBUSY) { return false; } THROW_SRE(#_CALL, ret); } - /** - * Implementation of Mutex class using POSIX mutex + * Implementation of Mutex class using C++11 std::timed_mutex * - * Throws apache::thrift::concurrency::SystemResourceException on error. + * Methods throw std::system_error on error. * * @version $Id:$ */ -class Mutex::impl { -public: - impl(Initializer init) : initialized_(false) { -#ifdef THRIFT_PTHREAD_MUTEX_CONTENTION_PROFILING - profileTime_ = 0; -#endif - init(&pthread_mutex_); - initialized_ = true; - } +class Mutex::impl : public std::timed_mutex {}; - ~impl() { - if (initialized_) { - initialized_ = false; - ABORT_ONFAIL(pthread_mutex_destroy(&pthread_mutex_)); - } - } - - void lock() const { - PROFILE_MUTEX_START_LOCK(); - THROW_SRE_ONFAIL(pthread_mutex_lock(&pthread_mutex_)); - PROFILE_MUTEX_LOCKED(); - } - - bool trylock() const { - THROW_SRE_TRYFAIL(pthread_mutex_trylock(&pthread_mutex_)); - } - - bool timedlock(int64_t milliseconds) const { -#if defined(_POSIX_TIMEOUTS) && _POSIX_TIMEOUTS >= 200112L - PROFILE_MUTEX_START_LOCK(); - - struct THRIFT_TIMESPEC ts; - Util::toTimespec(ts, milliseconds + Util::currentTime()); - EINTR_LOOP(pthread_mutex_timedlock(&pthread_mutex_, &ts)); - if (ret == 0) { - PROFILE_MUTEX_LOCKED(); - return true; - } else if (ret == ETIMEDOUT) { - PROFILE_MUTEX_NOT_LOCKED(); - return false; - } - - THROW_SRE("pthread_mutex_timedlock(&pthread_mutex_, &ts)", ret); -#else - /* Otherwise follow solution used by Mono for Android */ - struct THRIFT_TIMESPEC sleepytime, now, to; - - /* This is just to avoid a completely busy wait */ - sleepytime.tv_sec = 0; - sleepytime.tv_nsec = 10000000L; /* 10ms */ - - Util::toTimespec(to, milliseconds + Util::currentTime()); - - while ((trylock()) == false) { - Util::toTimespec(now, Util::currentTime()); - if (now.tv_sec >= to.tv_sec && now.tv_nsec >= to.tv_nsec) { - return false; - } - nanosleep(&sleepytime, NULL); - } - - return true; -#endif - } - - void unlock() const { - PROFILE_MUTEX_START_UNLOCK(); - THROW_SRE_ONFAIL(pthread_mutex_unlock(&pthread_mutex_)); - PROFILE_MUTEX_UNLOCKED(); - } - - void* getUnderlyingImpl() const { return (void*)&pthread_mutex_; } - -private: - mutable pthread_mutex_t pthread_mutex_; - mutable bool initialized_; -#ifdef THRIFT_PTHREAD_MUTEX_CONTENTION_PROFILING - mutable int64_t profileTime_; -#endif -}; - -Mutex::Mutex(Initializer init) : impl_(new Mutex::impl(init)) { +Mutex::Mutex() : impl_(new Mutex::impl()) { } void* Mutex::getUnderlyingImpl() const { - return impl_->getUnderlyingImpl(); + return impl_.get(); } void Mutex::lock() const { @@ -220,161 +47,17 @@ void Mutex::lock() const { } bool Mutex::trylock() const { - return impl_->trylock(); + return impl_->try_lock(); } bool Mutex::timedlock(int64_t ms) const { - return impl_->timedlock(ms); + return impl_->try_lock_for(std::chrono::milliseconds(ms)); } void Mutex::unlock() const { impl_->unlock(); } -void Mutex::DEFAULT_INITIALIZER(void* arg) { - pthread_mutex_t* pthread_mutex = (pthread_mutex_t*)arg; - THROW_SRE_ONFAIL(pthread_mutex_init(pthread_mutex, NULL)); -} - -#if defined(PTHREAD_ADAPTIVE_MUTEX_INITIALIZER_NP) || defined(PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP) || defined(PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP) -static void init_with_kind(pthread_mutex_t* mutex, int kind) { - pthread_mutexattr_t mutexattr; - THROW_SRE_ONFAIL(pthread_mutexattr_init(&mutexattr)); - THROW_SRE_ONFAIL(pthread_mutexattr_settype(&mutexattr, kind)); - THROW_SRE_ONFAIL(pthread_mutex_init(mutex, &mutexattr)); - THROW_SRE_ONFAIL(pthread_mutexattr_destroy(&mutexattr)); -} -#endif - -#ifdef PTHREAD_ADAPTIVE_MUTEX_INITIALIZER_NP -void Mutex::ADAPTIVE_INITIALIZER(void* arg) { - // From mysql source: mysys/my_thr_init.c - // Set mutex type to "fast" a.k.a "adaptive" - // - // In this case the thread may steal the mutex from some other thread - // that is waiting for the same mutex. This will save us some - // context switches but may cause a thread to 'starve forever' while - // waiting for the mutex (not likely if the code within the mutex is - // short). - init_with_kind((pthread_mutex_t*)arg, PTHREAD_MUTEX_ADAPTIVE_NP); -} -#endif - -#ifdef PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP -void Mutex::ERRORCHECK_INITIALIZER(void* arg) { - init_with_kind((pthread_mutex_t*)arg, PTHREAD_MUTEX_ERRORCHECK); -} -#endif - -#ifdef PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP -void Mutex::RECURSIVE_INITIALIZER(void* arg) { - init_with_kind((pthread_mutex_t*)arg, PTHREAD_MUTEX_RECURSIVE_NP); -} -#endif - -/** - * Implementation of ReadWriteMutex class using POSIX rw lock - * - * @version $Id:$ - */ -class ReadWriteMutex::impl { -public: - impl() : initialized_(false) { -#ifdef THRIFT_PTHREAD_MUTEX_CONTENTION_PROFILING - profileTime_ = 0; -#endif - THROW_SRE_ONFAIL(pthread_rwlock_init(&rw_lock_, NULL)); - initialized_ = true; - } - - ~impl() { - if (initialized_) { - initialized_ = false; - ABORT_ONFAIL(pthread_rwlock_destroy(&rw_lock_)); - } - } - - void acquireRead() const { - PROFILE_MUTEX_START_LOCK(); - THROW_SRE_ONFAIL(pthread_rwlock_rdlock(&rw_lock_)); - PROFILE_MUTEX_NOT_LOCKED(); // not exclusive, so use not-locked path - } - - void acquireWrite() const { - PROFILE_MUTEX_START_LOCK(); - THROW_SRE_ONFAIL(pthread_rwlock_wrlock(&rw_lock_)); - PROFILE_MUTEX_LOCKED(); - } - - bool attemptRead() const { THROW_SRE_TRYFAIL(pthread_rwlock_tryrdlock(&rw_lock_)); } - - bool attemptWrite() const { THROW_SRE_TRYFAIL(pthread_rwlock_trywrlock(&rw_lock_)); } - - void release() const { - PROFILE_MUTEX_START_UNLOCK(); - THROW_SRE_ONFAIL(pthread_rwlock_unlock(&rw_lock_)); - PROFILE_MUTEX_UNLOCKED(); - } - -private: - mutable pthread_rwlock_t rw_lock_; - mutable bool initialized_; -#ifdef THRIFT_PTHREAD_MUTEX_CONTENTION_PROFILING - mutable int64_t profileTime_; -#endif -}; - -ReadWriteMutex::ReadWriteMutex() : impl_(new ReadWriteMutex::impl()) { -} - -void ReadWriteMutex::acquireRead() const { - impl_->acquireRead(); -} - -void ReadWriteMutex::acquireWrite() const { - impl_->acquireWrite(); -} - -bool ReadWriteMutex::attemptRead() const { - return impl_->attemptRead(); -} - -bool ReadWriteMutex::attemptWrite() const { - return impl_->attemptWrite(); -} - -void ReadWriteMutex::release() const { - impl_->release(); -} - -NoStarveReadWriteMutex::NoStarveReadWriteMutex() : writerWaiting_(false) { -} - -void NoStarveReadWriteMutex::acquireRead() const { - if (writerWaiting_) { - // writer is waiting, block on the writer's mutex until he's done with it - mutex_.lock(); - mutex_.unlock(); - } - - ReadWriteMutex::acquireRead(); -} - -void NoStarveReadWriteMutex::acquireWrite() const { - // if we can acquire the rwlock the easy way, we're done - if (attemptWrite()) { - return; - } - - // failed to get the rwlock, do it the hard way: - // locking the mutex and setting writerWaiting will cause all new readers to - // block on the mutex rather than on the rwlock. - mutex_.lock(); - writerWaiting_ = true; - ReadWriteMutex::acquireWrite(); - writerWaiting_ = false; - mutex_.unlock(); -} } } } // apache::thrift::concurrency diff --git a/lib/cpp/src/thrift/concurrency/Mutex.h b/lib/cpp/src/thrift/concurrency/Mutex.h index 09b938e5b1b..27e386ed48b 100644 --- a/lib/cpp/src/thrift/concurrency/Mutex.h +++ b/lib/cpp/src/thrift/concurrency/Mutex.h @@ -20,39 +20,13 @@ #ifndef _THRIFT_CONCURRENCY_MUTEX_H_ #define _THRIFT_CONCURRENCY_MUTEX_H_ 1 -#include +#include #include -#include namespace apache { namespace thrift { namespace concurrency { -#ifndef THRIFT_NO_CONTENTION_PROFILING - -/** - * Determines if the Thrift Mutex and ReadWriteMutex classes will attempt to - * profile their blocking acquire methods. If this value is set to non-zero, - * Thrift will attempt to invoke the callback once every profilingSampleRate - * times. However, as the sampling is not synchronized the rate is not - * guranateed, and could be subject to big bursts and swings. Please ensure - * your sampling callback is as performant as your application requires. - * - * The callback will get called with the wait time taken to lock the mutex in - * usec and a (void*) that uniquely identifies the Mutex (or ReadWriteMutex) - * being locked. - * - * The enableMutexProfiling() function is unsynchronized; calling this function - * while profiling is already enabled may result in race conditions. On - * architectures where a pointer assignment is atomic, this is safe but there - * is no guarantee threads will agree on a single callback within any - * particular time period. - */ -typedef void (*MutexWaitCallback)(const void* id, int64_t waitTimeMicros); -void enableMutexProfiling(int32_t profilingSampleRate, MutexWaitCallback callback); - -#endif - /** * NOTE: All mutex implementations throw an exception on failure. See each * specific implementation to understand the exception type(s) used. @@ -65,10 +39,8 @@ void enableMutexProfiling(int32_t profilingSampleRate, MutexWaitCallback callbac */ class Mutex { public: - typedef void (*Initializer)(void*); - - Mutex(Initializer init = DEFAULT_INITIALIZER); - virtual ~Mutex() {} + Mutex(); + virtual ~Mutex() = default; virtual void lock() const; virtual bool trylock() const; @@ -77,57 +49,11 @@ class Mutex { void* getUnderlyingImpl() const; - // If you attempt to use one of these and it fails to link, it means - // your version of pthreads does not support it - try another one. - static void ADAPTIVE_INITIALIZER(void*); - static void DEFAULT_INITIALIZER(void*); - static void ERRORCHECK_INITIALIZER(void*); - static void RECURSIVE_INITIALIZER(void*); - private: class impl; - stdcxx::shared_ptr impl_; + std::shared_ptr impl_; }; -class ReadWriteMutex { -public: - ReadWriteMutex(); - virtual ~ReadWriteMutex() {} - - // these get the lock and block until it is done successfully - virtual void acquireRead() const; - virtual void acquireWrite() const; - - // these attempt to get the lock, returning false immediately if they fail - virtual bool attemptRead() const; - virtual bool attemptWrite() const; - - // this releases both read and write locks - virtual void release() const; - -private: - class impl; - stdcxx::shared_ptr impl_; -}; - -/** - * A ReadWriteMutex that guarantees writers will not be starved by readers: - * When a writer attempts to acquire the mutex, all new readers will be - * blocked from acquiring the mutex until the writer has acquired and - * released it. In some operating systems, this may already be guaranteed - * by a regular ReadWriteMutex. - */ -class NoStarveReadWriteMutex : public ReadWriteMutex { -public: - NoStarveReadWriteMutex(); - - virtual void acquireRead() const; - virtual void acquireWrite() const; - -private: - Mutex mutex_; - mutable volatile bool writerWaiting_; -}; class Guard : boost::noncopyable { public: @@ -136,11 +62,11 @@ class Guard : boost::noncopyable { value.lock(); } else if (timeout < 0) { if (!value.trylock()) { - mutex_ = NULL; + mutex_ = nullptr; } } else { if (!value.timedlock(timeout)) { - mutex_ = NULL; + mutex_ = nullptr; } } } @@ -150,38 +76,12 @@ class Guard : boost::noncopyable { } } - operator bool() const { return (mutex_ != NULL); } + operator bool() const { return (mutex_ != nullptr); } private: const Mutex* mutex_; }; -// Can be used as second argument to RWGuard to make code more readable -// as to whether we're doing acquireRead() or acquireWrite(). -enum RWGuardType { RW_READ = 0, RW_WRITE = 1 }; - -class RWGuard : boost::noncopyable { -public: - RWGuard(const ReadWriteMutex& value, bool write = false) : rw_mutex_(value) { - if (write) { - rw_mutex_.acquireWrite(); - } else { - rw_mutex_.acquireRead(); - } - } - - RWGuard(const ReadWriteMutex& value, RWGuardType type) : rw_mutex_(value) { - if (type == RW_WRITE) { - rw_mutex_.acquireWrite(); - } else { - rw_mutex_.acquireRead(); - } - } - ~RWGuard() { rw_mutex_.release(); } - -private: - const ReadWriteMutex& rw_mutex_; -}; } } } // apache::thrift::concurrency diff --git a/lib/cpp/src/thrift/concurrency/PosixThreadFactory.cpp b/lib/cpp/src/thrift/concurrency/PosixThreadFactory.cpp deleted file mode 100644 index 2e35446b5c8..00000000000 --- a/lib/cpp/src/thrift/concurrency/PosixThreadFactory.cpp +++ /dev/null @@ -1,335 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -#include - -#include -#include -#include - -#if GOOGLE_PERFTOOLS_REGISTER_THREAD -#include -#endif - -#include -#include - -#include - -#include - -namespace apache { -namespace thrift { -namespace concurrency { - -/** - * The POSIX thread class. - * - * @version $Id:$ - */ -class PthreadThread : public Thread { -public: - enum STATE { uninitialized, starting, started, stopping, stopped }; - - static const int MB = 1024 * 1024; - - static void* threadMain(void* arg); - -private: - pthread_t pthread_; - Monitor monitor_; // guard to protect state_ and also notification - STATE state_; // to protect proper thread start behavior - int policy_; - int priority_; - int stackSize_; - stdcxx::weak_ptr self_; - bool detached_; - -public: - PthreadThread(int policy, - int priority, - int stackSize, - bool detached, - stdcxx::shared_ptr runnable) - : - -#ifndef _WIN32 - pthread_(0), -#endif // _WIN32 - state_(uninitialized), - policy_(policy), - priority_(priority), - stackSize_(stackSize), - detached_(detached) { - - this->Thread::runnable(runnable); - } - - ~PthreadThread() { - /* Nothing references this thread, if is is not detached, do a join - now, otherwise the thread-id and, possibly, other resources will - be leaked. */ - if (!detached_) { - 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(); - } - } - - void start() { - if (getState() != uninitialized) { - return; - } - - pthread_attr_t thread_attr; - if (pthread_attr_init(&thread_attr) != 0) { - throw SystemResourceException("pthread_attr_init failed"); - } - - if (pthread_attr_setdetachstate(&thread_attr, - detached_ ? PTHREAD_CREATE_DETACHED : PTHREAD_CREATE_JOINABLE) - != 0) { - throw SystemResourceException("pthread_attr_setdetachstate failed"); - } - - // Set thread stack size - if (pthread_attr_setstacksize(&thread_attr, MB * stackSize_) != 0) { - throw SystemResourceException("pthread_attr_setstacksize failed"); - } - -// Set thread policy -#ifdef _WIN32 - // WIN32 Pthread implementation doesn't seem to support sheduling policies other then - // PosixThreadFactory::OTHER - runtime error - policy_ = PosixThreadFactory::OTHER; -#endif - -#if _POSIX_THREAD_PRIORITY_SCHEDULING > 0 - if (pthread_attr_setschedpolicy(&thread_attr, policy_) != 0) { - throw SystemResourceException("pthread_attr_setschedpolicy failed"); - } -#endif - - struct sched_param sched_param; - sched_param.sched_priority = priority_; - - // Set thread priority - if (pthread_attr_setschedparam(&thread_attr, &sched_param) != 0) { - throw SystemResourceException("pthread_attr_setschedparam failed"); - } - - // Create reference - stdcxx::shared_ptr* selfRef = new stdcxx::shared_ptr(); - *selfRef = self_.lock(); - - setState(starting); - - Synchronized sync(monitor_); - - if (pthread_create(&pthread_, &thread_attr, threadMain, (void*)selfRef) != 0) { - throw SystemResourceException("pthread_create failed"); - } - - // The caller may not choose to guarantee the scope of the Runnable - // being used in the thread, so we must actually wait until the thread - // starts before we return. If we do not wait, it would be possible - // for the caller to start destructing the Runnable and the Thread, - // and we would end up in a race. This was identified with valgrind. - monitor_.wait(); - } - - void join() { - if (!detached_ && getState() != uninitialized) { - void* ignore; - /* XXX - If join fails it is most likely due to the fact - that the last reference was the thread itself and cannot - join. This results in leaked threads and will eventually - cause the process to run out of thread resources. - We're beyond the point of throwing an exception. Not clear how - best to handle this. */ - int res = pthread_join(pthread_, &ignore); - detached_ = (res == 0); - if (res != 0) { - GlobalOutput.printf("PthreadThread::join(): fail with code %d", res); - } - } - } - - Thread::id_t getId() { - -#ifndef _WIN32 - return (Thread::id_t)pthread_; -#else - return (Thread::id_t)pthread_.p; -#endif // _WIN32 - } - - stdcxx::shared_ptr runnable() const { return Thread::runnable(); } - - void runnable(stdcxx::shared_ptr value) { Thread::runnable(value); } - - void weakRef(stdcxx::shared_ptr self) { - assert(self.get() == this); - self_ = stdcxx::weak_ptr(self); - } -}; - -void* PthreadThread::threadMain(void* arg) { - stdcxx::shared_ptr thread = *(stdcxx::shared_ptr*)arg; - delete reinterpret_cast*>(arg); - -#if GOOGLE_PERFTOOLS_REGISTER_THREAD - ProfilerRegisterThread(); -#endif - - thread->setState(started); - - thread->runnable()->run(); - - STATE _s = thread->getState(); - if (_s != stopping && _s != stopped) { - thread->setState(stopping); - } - - return (void*)0; -} - -/** - * Converts generic posix thread schedule policy enums into pthread - * API values. - */ -static int toPthreadPolicy(PosixThreadFactory::POLICY policy) { - switch (policy) { - case PosixThreadFactory::OTHER: - return SCHED_OTHER; - case PosixThreadFactory::FIFO: - return SCHED_FIFO; - case PosixThreadFactory::ROUND_ROBIN: - return SCHED_RR; - } - return SCHED_OTHER; -} - -/** - * Converts relative thread priorities to absolute value based on posix - * thread scheduler policy - * - * The idea is simply to divide up the priority range for the given policy - * into the correpsonding relative priority level (lowest..highest) and - * then pro-rate accordingly. - */ -static int toPthreadPriority(PosixThreadFactory::POLICY policy, PosixThreadFactory::PRIORITY priority) { - int pthread_policy = toPthreadPolicy(policy); - int min_priority = 0; - int max_priority = 0; -#ifdef HAVE_SCHED_GET_PRIORITY_MIN - min_priority = sched_get_priority_min(pthread_policy); -#endif -#ifdef HAVE_SCHED_GET_PRIORITY_MAX - max_priority = sched_get_priority_max(pthread_policy); -#endif - int quanta = (PosixThreadFactory::HIGHEST - PosixThreadFactory::LOWEST) + 1; - float stepsperquanta = (float)(max_priority - min_priority) / quanta; - - if (priority <= PosixThreadFactory::HIGHEST) { - return (int)(min_priority + stepsperquanta * priority); - } else { - // should never get here for priority increments. - assert(false); - return (int)(min_priority + stepsperquanta * PosixThreadFactory::NORMAL); - } -} - -PosixThreadFactory::PosixThreadFactory(POLICY policy, - PRIORITY priority, - int stackSize, - bool detached) - : ThreadFactory(detached), - policy_(policy), - priority_(priority), - stackSize_(stackSize) { -} - -PosixThreadFactory::PosixThreadFactory(bool detached) - : ThreadFactory(detached), - policy_(ROUND_ROBIN), - priority_(NORMAL), - stackSize_(1) { -} - -stdcxx::shared_ptr PosixThreadFactory::newThread(stdcxx::shared_ptr runnable) const { - stdcxx::shared_ptr result - = stdcxx::shared_ptr(new PthreadThread(toPthreadPolicy(policy_), - toPthreadPriority(policy_, priority_), - stackSize_, - isDetached(), - runnable)); - result->weakRef(result); - runnable->thread(result); - return result; -} - -int PosixThreadFactory::getStackSize() const { - return stackSize_; -} - -void PosixThreadFactory::setStackSize(int value) { - stackSize_ = value; -} - -PosixThreadFactory::PRIORITY PosixThreadFactory::getPriority() const { - return priority_; -} - -void PosixThreadFactory::setPriority(PRIORITY value) { - priority_ = value; -} - -Thread::id_t PosixThreadFactory::getCurrentThreadId() const { -#ifndef _WIN32 - return (Thread::id_t)pthread_self(); -#else - return (Thread::id_t)pthread_self().p; -#endif // _WIN32 -} - -} -} -} // apache::thrift::concurrency diff --git a/lib/cpp/src/thrift/concurrency/PosixThreadFactory.h b/lib/cpp/src/thrift/concurrency/PosixThreadFactory.h deleted file mode 100644 index 5e04d01d33f..00000000000 --- a/lib/cpp/src/thrift/concurrency/PosixThreadFactory.h +++ /dev/null @@ -1,129 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -#ifndef _THRIFT_CONCURRENCY_POSIXTHREADFACTORY_H_ -#define _THRIFT_CONCURRENCY_POSIXTHREADFACTORY_H_ 1 - -#include - -#include - -namespace apache { -namespace thrift { -namespace concurrency { - -/** - * A thread factory to create posix threads - * - * @version $Id:$ - */ -class PosixThreadFactory : public ThreadFactory { - -public: - /** - * POSIX Thread scheduler policies - */ - enum POLICY { OTHER, FIFO, ROUND_ROBIN }; - - /** - * POSIX Thread scheduler relative priorities, - * - * Absolute priority is determined by scheduler policy and OS. This - * enumeration specifies relative priorities such that one can specify a - * priority within a giving scheduler policy without knowing the absolute - * value of the priority. - */ - enum PRIORITY { - LOWEST = 0, - LOWER = 1, - LOW = 2, - NORMAL = 3, - HIGH = 4, - HIGHER = 5, - HIGHEST = 6, - INCREMENT = 7, - DECREMENT = 8 - }; - - /** - * Posix thread (pthread) factory. All threads created by a factory are reference-counted - * via stdcxx::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. - * - * Threads are created with the specified policy, priority, stack-size and detachable-mode - * detached means the thread is free-running and will release all system resources the - * when it completes. A detachable thread is not joinable. The join method - * of a detachable thread will return immediately with no error. - * - * By default threads are not joinable. - */ - PosixThreadFactory(POLICY policy = ROUND_ROBIN, - PRIORITY priority = NORMAL, - int stackSize = 1, - bool detached = true); - - /** - * Provide a constructor compatible with the other factories - * The default policy is POLICY::ROUND_ROBIN. - * The default priority is PRIORITY::NORMAL. - * The default stackSize is 1. - */ - PosixThreadFactory(bool detached); - - // From ThreadFactory; - stdcxx::shared_ptr newThread(stdcxx::shared_ptr runnable) const; - - // From ThreadFactory; - Thread::id_t getCurrentThreadId() const; - - /** - * Gets stack size for newly created threads - * - * @return int size in megabytes - */ - virtual int getStackSize() const; - - /** - * Sets stack size for newly created threads - * - * @param value size in megabytes - */ - virtual void setStackSize(int value); - - /** - * Gets priority relative to current policy - */ - virtual PRIORITY getPriority() const; - - /** - * Sets priority relative to current policy - */ - virtual void setPriority(PRIORITY priority); - -private: - POLICY policy_; - PRIORITY priority_; - int stackSize_; -}; -} -} -} // apache::thrift::concurrency - -#endif // #ifndef _THRIFT_CONCURRENCY_POSIXTHREADFACTORY_H_ diff --git a/lib/cpp/src/thrift/concurrency/StdMonitor.cpp b/lib/cpp/src/thrift/concurrency/StdMonitor.cpp deleted file mode 100644 index 7b3b209a74d..00000000000 --- a/lib/cpp/src/thrift/concurrency/StdMonitor.cpp +++ /dev/null @@ -1,213 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -#include - -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -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_(NULL) { init(ownedMutex_.get()); } - - Impl(Mutex* mutex) : ownedMutex_(), conditionVariable_(), mutex_(NULL) { init(mutex); } - - Impl(Monitor* monitor) : ownedMutex_(), conditionVariable_(), mutex_(NULL) { - 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(int64_t timeout_ms) { - int result = waitForTimeRelative(timeout_ms); - 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_ms == 0. - * - * Returns 0 if condition occurs, THRIFT_ETIMEDOUT on timeout, or an error code. - */ - int waitForTimeRelative(int64_t timeout_ms) { - if (timeout_ms == 0LL) { - return waitForever(); - } - - assert(mutex_); - std::timed_mutex* mutexImpl = static_cast(mutex_->getUnderlyingImpl()); - assert(mutexImpl); - - std::unique_lock lock(*mutexImpl, std::adopt_lock); - bool timedout = (conditionVariable_.wait_for(lock, std::chrono::milliseconds(timeout_ms)) - == std::cv_status::timeout); - lock.release(); - return (timedout ? THRIFT_ETIMEDOUT : 0); - } - - /** - * Waits until the absolute time specified using struct THRIFT_TIMESPEC. - * Returns 0 if condition occurs, THRIFT_ETIMEDOUT on timeout, or an error code. - */ - int waitForTime(const THRIFT_TIMESPEC* abstime) { - struct timeval temp; - temp.tv_sec = static_cast(abstime->tv_sec); - temp.tv_usec = static_cast(abstime->tv_nsec) / 1000; - return waitForTime(&temp); - } - - /** - * Waits until the absolute time specified using struct timeval. - * Returns 0 if condition occurs, THRIFT_ETIMEDOUT on timeout, or an error code. - */ - int waitForTime(const struct timeval* abstime) { - assert(mutex_); - std::timed_mutex* mutexImpl = static_cast(mutex_->getUnderlyingImpl()); - assert(mutexImpl); - - struct timeval currenttime; - Util::toTimeval(currenttime, Util::currentTime()); - - long tv_sec = static_cast(abstime->tv_sec - currenttime.tv_sec); - long tv_usec = static_cast(abstime->tv_usec - currenttime.tv_usec); - if (tv_sec < 0) - tv_sec = 0; - if (tv_usec < 0) - tv_usec = 0; - - std::unique_lock lock(*mutexImpl, std::adopt_lock); - bool timedout = (conditionVariable_.wait_for(lock, - std::chrono::seconds(tv_sec) - + std::chrono::microseconds(tv_usec)) - == 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_); - std::timed_mutex* mutexImpl = static_cast(mutex_->getUnderlyingImpl()); - assert(mutexImpl); - - std::unique_lock 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 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(impl_)->mutex(); -} - -void Monitor::lock() const { - const_cast(impl_)->lock(); -} - -void Monitor::unlock() const { - const_cast(impl_)->unlock(); -} - -void Monitor::wait(int64_t timeout) const { - const_cast(impl_)->wait(timeout); -} - -int Monitor::waitForTime(const THRIFT_TIMESPEC* abstime) const { - return const_cast(impl_)->waitForTime(abstime); -} - -int Monitor::waitForTime(const timeval* abstime) const { - return const_cast(impl_)->waitForTime(abstime); -} - -int Monitor::waitForTimeRelative(int64_t timeout_ms) const { - return const_cast(impl_)->waitForTimeRelative(timeout_ms); -} - -int Monitor::waitForever() const { - return const_cast(impl_)->waitForever(); -} - -void Monitor::notify() const { - const_cast(impl_)->notify(); -} - -void Monitor::notifyAll() const { - const_cast(impl_)->notifyAll(); -} -} -} -} // apache::thrift::concurrency diff --git a/lib/cpp/src/thrift/concurrency/StdMutex.cpp b/lib/cpp/src/thrift/concurrency/StdMutex.cpp deleted file mode 100644 index e0f79fa37f0..00000000000 --- a/lib/cpp/src/thrift/concurrency/StdMutex.cpp +++ /dev/null @@ -1,71 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -#include - -#include -#include - -#include -#include -#include - -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(Initializer init) : impl_(new Mutex::impl()) { - ((void)init); -} - -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(); -} - -void Mutex::DEFAULT_INITIALIZER(void* arg) { - ((void)arg); -} -} -} -} // apache::thrift::concurrency diff --git a/lib/cpp/src/thrift/concurrency/StdThreadFactory.cpp b/lib/cpp/src/thrift/concurrency/StdThreadFactory.cpp deleted file mode 100644 index da0c5e373bc..00000000000 --- a/lib/cpp/src/thrift/concurrency/StdThreadFactory.cpp +++ /dev/null @@ -1,153 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -#include - -#if USE_STD_THREAD - -#include -#include -#include -#include - -#include -#include - -namespace apache { -namespace thrift { -namespace concurrency { - -/** - * The C++11 thread class. - * - * Note that we use boost shared_ptr rather than std shared_ptrs here - * because the Thread/Runnable classes use those and we don't want to - * mix them. - * - * @version $Id:$ - */ -class StdThread : public Thread, public stdcxx::enable_shared_from_this { -public: - enum STATE { uninitialized, starting, started, stopping, stopped }; - - static void threadMain(stdcxx::shared_ptr thread); - -private: - std::unique_ptr thread_; - Monitor monitor_; - STATE state_; - bool detached_; - -public: - StdThread(bool detached, stdcxx::shared_ptr runnable) - : state_(uninitialized), detached_(detached) { - this->Thread::runnable(runnable); - } - - ~StdThread() { - 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(); - } - } - - void start() { - if (getState() != uninitialized) { - return; - } - - stdcxx::shared_ptr selfRef = shared_from_this(); - setState(starting); - - Synchronized sync(monitor_); - thread_ = std::unique_ptr(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(); - } - - void join() { - if (!detached_ && state_ != uninitialized) { - thread_->join(); - } - } - - Thread::id_t getId() { return thread_.get() ? thread_->get_id() : std::thread::id(); } - - stdcxx::shared_ptr runnable() const { return Thread::runnable(); } - - void runnable(stdcxx::shared_ptr value) { Thread::runnable(value); } -}; - -void StdThread::threadMain(stdcxx::shared_ptr thread) { -#if GOOGLE_PERFTOOLS_REGISTER_THREAD - ProfilerRegisterThread(); -#endif - - thread->setState(started); - thread->runnable()->run(); - - if (thread->getState() != stopping && thread->getState() != stopped) { - thread->setState(stopping); - } -} - -StdThreadFactory::StdThreadFactory(bool detached) : ThreadFactory(detached) { -} - -stdcxx::shared_ptr StdThreadFactory::newThread(stdcxx::shared_ptr runnable) const { - stdcxx::shared_ptr result = stdcxx::shared_ptr(new StdThread(isDetached(), runnable)); - runnable->thread(result); - return result; -} - -Thread::id_t StdThreadFactory::getCurrentThreadId() const { - return std::this_thread::get_id(); -} -} -} -} // apache::thrift::concurrency - -#endif // USE_STD_THREAD diff --git a/compiler/cpp/src/thrift/plugin/plugin_output.h b/lib/cpp/src/thrift/concurrency/Thread.cpp similarity index 69% rename from compiler/cpp/src/thrift/plugin/plugin_output.h rename to lib/cpp/src/thrift/concurrency/Thread.cpp index eab2d1bfcde..a2bb1270f92 100644 --- a/compiler/cpp/src/thrift/plugin/plugin_output.h +++ b/lib/cpp/src/thrift/concurrency/Thread.cpp @@ -17,22 +17,21 @@ * under the License. */ -#ifndef T_PLUGIN_PLUGIN_OUTPUT_H -#define T_PLUGIN_PLUGIN_OUTPUT_H +#include -#include +namespace apache { +namespace thrift { +namespace concurrency { -class t_program; +void Thread::threadMain(std::shared_ptr thread) { + thread->setState(started); + thread->runnable()->run(); -namespace plugin_output { - -enum PluginDelegateResult { - PLUGIN_NOT_FOUND, - PLUGIN_FAILURE, - PLUGIN_SUCCEESS, -}; - -PluginDelegateResult delegateToPlugin(t_program* program, const std::string& options); + if (thread->getState() != stopping && thread->getState() != stopped) { + thread->setState(stopping); + } } -#endif +} +} +} // apache::thrift::concurrency diff --git a/lib/cpp/src/thrift/concurrency/Thread.h b/lib/cpp/src/thrift/concurrency/Thread.h index 788623bdad7..344d2ca31a3 100644 --- a/lib/cpp/src/thrift/concurrency/Thread.h +++ b/lib/cpp/src/thrift/concurrency/Thread.h @@ -20,20 +20,10 @@ #ifndef _THRIFT_CONCURRENCY_THREAD_H_ #define _THRIFT_CONCURRENCY_THREAD_H_ 1 -#include -#include - -#include - -#if USE_BOOST_THREAD -#include -#elif USE_STD_THREAD +#include #include -#else -#ifdef HAVE_PTHREAD_H -#include -#endif -#endif + +#include namespace apache { namespace thrift { @@ -49,23 +39,23 @@ class Thread; class Runnable { public: - virtual ~Runnable(){}; + 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 stdcxx::shared_ptr thread() { return thread_.lock(); } + virtual std::shared_ptr 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(stdcxx::shared_ptr value) { thread_ = value; } + virtual void thread(std::shared_ptr value) { thread_ = value; } private: - stdcxx::weak_ptr thread_; + std::weak_ptr thread_; }; /** @@ -73,103 +63,117 @@ class Runnable { * 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 + * ThreadFactory implementations to deal with * * @see apache::thrift::concurrency::ThreadFactory) */ -class Thread { +class Thread : public std::enable_shared_from_this { public: -#if USE_BOOST_THREAD - typedef boost::thread::id id_t; - - static inline bool is_current(id_t t) { return t == boost::this_thread::get_id(); } - static inline id_t get_current() { return boost::this_thread::get_id(); } -#elif USE_STD_THREAD typedef std::thread::id id_t; + typedef void (*thread_funct_t)(std::shared_ptr ); + + enum STATE { uninitialized, starting, started, stopping, stopped }; + + static void threadMain(std::shared_ptr 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(); } -#else - typedef pthread_t id_t; - static inline bool is_current(id_t t) { return pthread_equal(pthread_self(), t); } - static inline id_t get_current() { return pthread_self(); } -#endif - - virtual ~Thread(){}; + Thread(bool detached, std::shared_ptr runnable) + : state_(uninitialized), detached_(detached) { + this->_runnable = runnable; + } + + virtual ~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. */ - virtual void start() = 0; + virtual void start() { + if (getState() != uninitialized) { + return; + } + + std::shared_ptr selfRef = shared_from_this(); + setState(starting); + + Synchronized sync(monitor_); + thread_ = std::unique_ptr(new std::thread(getThreadFunc(), 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. */ - virtual void join() = 0; + virtual void join() { + if (!detached_ && state_ != uninitialized) { + thread_->join(); + } + } /** * Gets the thread's platform-specific ID */ - virtual id_t getId() = 0; + Thread::id_t getId() const { return thread_.get() ? thread_->get_id() : std::thread::id(); } /** * Gets the runnable object this thread is hosting */ - virtual stdcxx::shared_ptr runnable() const { return _runnable; } - -protected: - virtual void runnable(stdcxx::shared_ptr value) { _runnable = value; } - -private: - stdcxx::shared_ptr _runnable; -}; + std::shared_ptr runnable() const { return _runnable; } -/** - * Factory to create platform-specific thread object and bind them to Runnable - * object for execution - */ -class ThreadFactory { protected: - ThreadFactory(bool detached) : detached_(detached) { } -public: - virtual ~ThreadFactory() { } - - /** - * 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. - */ - virtual stdcxx::shared_ptr newThread(stdcxx::shared_ptr runnable) const = 0; - - /** - * Gets the current thread id or unknown_thread_id if the current thread is not a thrift thread - */ - virtual Thread::id_t getCurrentThreadId() const = 0; - - /** - * For code readability define the unknown/undefined thread id - */ - static const Thread::id_t unknown_thread_id; + virtual thread_funct_t getThreadFunc() const { + return threadMain; + } private: + std::shared_ptr _runnable; + std::unique_ptr thread_; + Monitor monitor_; + STATE state_; bool detached_; }; + } } } // apache::thrift::concurrency diff --git a/lib/cpp/src/thrift/concurrency/Util.cpp b/lib/cpp/src/thrift/concurrency/ThreadFactory.cpp similarity index 71% rename from lib/cpp/src/thrift/concurrency/Util.cpp rename to lib/cpp/src/thrift/concurrency/ThreadFactory.cpp index dd6d19f971a..becb3b2447f 100644 --- a/lib/cpp/src/thrift/concurrency/Util.cpp +++ b/lib/cpp/src/thrift/concurrency/ThreadFactory.cpp @@ -19,26 +19,22 @@ #include -#include -#include - -#if defined(HAVE_SYS_TIME_H) -#include -#endif +#include +#include namespace apache { namespace thrift { namespace concurrency { -int64_t Util::currentTimeTicks(int64_t ticksPerSec) { - int64_t result; - struct timeval now; - int ret = THRIFT_GETTIMEOFDAY(&now, NULL); - assert(ret == 0); - THRIFT_UNUSED_VARIABLE(ret); // squelching "unused variable" warning - toTicks(result, now, ticksPerSec); +std::shared_ptr ThreadFactory::newThread(std::shared_ptr runnable) const { + std::shared_ptr result = std::make_shared(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/lib/cpp/src/thrift/concurrency/StdThreadFactory.h b/lib/cpp/src/thrift/concurrency/ThreadFactory.h similarity index 53% rename from lib/cpp/src/thrift/concurrency/StdThreadFactory.h rename to lib/cpp/src/thrift/concurrency/ThreadFactory.h index 8e116b64aa0..9db832e321b 100644 --- a/lib/cpp/src/thrift/concurrency/StdThreadFactory.h +++ b/lib/cpp/src/thrift/concurrency/ThreadFactory.h @@ -17,45 +17,60 @@ * under the License. */ -#ifndef _THRIFT_CONCURRENCY_STDTHREADFACTORY_H_ -#define _THRIFT_CONCURRENCY_STDTHREADFACTORY_H_ 1 +#ifndef _THRIFT_CONCURRENCY_THREADFACTORY_H_ +#define _THRIFT_CONCURRENCY_THREADFACTORY_H_ 1 #include -#include - +#include namespace apache { namespace thrift { namespace concurrency { /** - * A thread factory to create std::threads. - * - * @version $Id:$ + * Factory to create thread object and bind them to Runnable + * object for execution */ -class StdThreadFactory : public ThreadFactory { - +class ThreadFactory { public: /** - * Std thread factory. All threads created by a factory are reference-counted - * via stdcxx::shared_ptr. The factory guarantees that threads and the Runnable tasks + * 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) { } + + virtual ~ThreadFactory() = default; + + /** + * Gets current detached mode + */ + bool isDetached() const { return detached_; } - StdThreadFactory(bool detached = true); + /** + * Sets the detached disposition of newly created threads. + */ + void setDetached(bool detached) { detached_ = detached; } - // From ThreadFactory; - stdcxx::shared_ptr newThread(stdcxx::shared_ptr runnable) const; + /** + * Create a new thread. + */ + virtual std::shared_ptr newThread(std::shared_ptr runnable) const; - // From ThreadFactory; + /** + * 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_STDTHREADFACTORY_H_ +#endif // #ifndef _THRIFT_CONCURRENCY_THREADFACTORY_H_ diff --git a/lib/cpp/src/thrift/concurrency/ThreadManager.cpp b/lib/cpp/src/thrift/concurrency/ThreadManager.cpp index 2e27b7f42df..e917f4a478b 100644 --- a/lib/cpp/src/thrift/concurrency/ThreadManager.cpp +++ b/lib/cpp/src/thrift/concurrency/ThreadManager.cpp @@ -22,9 +22,8 @@ #include #include #include -#include -#include +#include #include #include @@ -34,8 +33,9 @@ namespace apache { namespace thrift { namespace concurrency { -using stdcxx::shared_ptr; -using stdcxx::dynamic_pointer_cast; +using std::shared_ptr; +using std::unique_ptr; +using std::dynamic_pointer_cast; /** * ThreadManager class @@ -64,19 +64,19 @@ class ThreadManager::Impl : public ThreadManager { maxMonitor_(&mutex_), workerMonitor_(&mutex_) {} - ~Impl() { stop(); } + ~Impl() override { stop(); } - void start(); - void stop(); + void start() override; + void stop() override; - ThreadManager::STATE state() const { return state_; } + ThreadManager::STATE state() const override { return state_; } - shared_ptr threadFactory() const { + shared_ptr threadFactory() const override { Guard g(mutex_); return threadFactory_; } - void threadFactory(shared_ptr value) { + void threadFactory(shared_ptr value) override { Guard g(mutex_); if (threadFactory_ && threadFactory_->isDetached() != value->isDetached()) { throw InvalidArgumentException(); @@ -84,33 +84,33 @@ class ThreadManager::Impl : public ThreadManager { threadFactory_ = value; } - void addWorker(size_t value); + void addWorker(size_t value) override; - void removeWorker(size_t value); + void removeWorker(size_t value) override; - size_t idleWorkerCount() const { return idleCount_; } + size_t idleWorkerCount() const override { return idleCount_; } - size_t workerCount() const { + size_t workerCount() const override { Guard g(mutex_); return workerCount_; } - size_t pendingTaskCount() const { + size_t pendingTaskCount() const override { Guard g(mutex_); return tasks_.size(); } - size_t totalTaskCount() const { + size_t totalTaskCount() const override { Guard g(mutex_); return tasks_.size() + workerCount_ - idleCount_; } - size_t pendingTaskCountMax() const { + size_t pendingTaskCountMax() const override { Guard g(mutex_); return pendingTaskCountMax_; } - size_t expiredTaskCount() { + size_t expiredTaskCount() const override { Guard g(mutex_); return expiredCount_; } @@ -120,17 +120,17 @@ class ThreadManager::Impl : public ThreadManager { pendingTaskCountMax_ = value; } - void add(shared_ptr value, int64_t timeout, int64_t expiration); + void add(shared_ptr value, int64_t timeout, int64_t expiration) override; - void remove(shared_ptr task); + void remove(shared_ptr task) override; - shared_ptr removeNextPending(); + shared_ptr removeNextPending() override; - void removeExpiredTasks() { + void removeExpiredTasks() override { removeExpired(false); } - void setExpireCallback(ExpireCallback expireCallback); + void setExpireCallback(ExpireCallback expireCallback) override; private: /** @@ -180,14 +180,17 @@ class ThreadManager::Task : public Runnable { public: enum STATE { WAITING, EXECUTING, TIMEDOUT, COMPLETE }; - Task(shared_ptr runnable, int64_t expiration = 0LL) + Task(shared_ptr runnable, uint64_t expiration = 0ULL) : runnable_(runnable), - state_(WAITING), - expireTime_(expiration != 0LL ? Util::currentTime() + expiration : 0LL) {} + state_(WAITING) { + if (expiration != 0ULL) { + expireTime_.reset(new std::chrono::steady_clock::time_point(std::chrono::steady_clock::now() + std::chrono::milliseconds(expiration))); + } + } - ~Task() {} + ~Task() override = default; - void run() { + void run() override { if (state_ == EXECUTING) { runnable_->run(); state_ = COMPLETE; @@ -196,13 +199,13 @@ class ThreadManager::Task : public Runnable { shared_ptr getRunnable() { return runnable_; } - int64_t getExpireTime() const { return expireTime_; } + const unique_ptr & getExpireTime() const { return expireTime_; } private: shared_ptr runnable_; friend class ThreadManager::Worker; STATE state_; - int64_t expireTime_; + unique_ptr expireTime_; }; class ThreadManager::Worker : public Runnable { @@ -211,7 +214,7 @@ class ThreadManager::Worker : public Runnable { public: Worker(ThreadManager::Impl* manager) : manager_(manager), state_(UNINITIALIZED) {} - ~Worker() {} + ~Worker() override = default; private: bool isActive() const { @@ -226,7 +229,7 @@ class ThreadManager::Worker : public Runnable { * As long as worker thread is running, pull tasks off the task queue and * execute. */ - void run() { + void run() override { Guard g(manager_->mutex_); /** @@ -280,7 +283,7 @@ class ThreadManager::Worker : public Runnable { // 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() < Util::currentTime()) ? + (task->getExpireTime() && *(task->getExpireTime()) < std::chrono::steady_clock::now()) ? ThreadManager::Task::TIMEDOUT : ThreadManager::Task::EXECUTING; } @@ -316,7 +319,9 @@ class ThreadManager::Worker : public Runnable { } else if (manager_->expireCallback_) { // The only other state the task could have been in is TIMEDOUT (see above) + manager_->mutex_.unlock(); manager_->expireCallback_(task->getRunnable()); + manager_->mutex_.lock(); manager_->expiredCount_++; } } @@ -341,7 +346,7 @@ void ThreadManager::Impl::addWorker(size_t value) { std::set > newThreads; for (size_t ix = 0; ix < value; ix++) { shared_ptr worker - = shared_ptr(new ThreadManager::Worker(this)); + = std::make_shared(this); newThreads.insert(threadFactory_->newThread(worker)); } @@ -349,13 +354,12 @@ void ThreadManager::Impl::addWorker(size_t value) { workerMaxCount_ += value; workers_.insert(newThreads.begin(), newThreads.end()); - for (std::set >::iterator ix = newThreads.begin(); ix != newThreads.end(); - ++ix) { + for (const auto & newThread : newThreads) { shared_ptr worker - = dynamic_pointer_cast((*ix)->runnable()); + = dynamic_pointer_cast(newThread->runnable()); worker->state_ = ThreadManager::Worker::STARTING; - (*ix)->start(); - idMap_.insert(std::pair >((*ix)->getId(), *ix)); + newThread->start(); + idMap_.insert(std::pair >(newThread->getId(), newThread)); } while (workerCount_ != workerMaxCount_) { @@ -427,17 +431,15 @@ void ThreadManager::Impl::removeWorkersUnderLock(size_t value) { workerMonitor_.wait(); } - for (std::set >::iterator ix = deadWorkers_.begin(); - ix != deadWorkers_.end(); - ++ix) { + for (const auto & deadWorker : deadWorkers_) { // when used with a joinable thread factory, we join the threads as we remove them if (!threadFactory_->isDetached()) { - (*ix)->join(); + deadWorker->join(); } - idMap_.erase((*ix)->getId()); - workers_.erase(*ix); + idMap_.erase(deadWorker->getId()); + workers_.erase(deadWorker); } deadWorkers_.clear(); @@ -477,7 +479,7 @@ void ThreadManager::Impl::add(shared_ptr value, int64_t timeout, int64 } } - tasks_.push_back(shared_ptr(new ThreadManager::Task(value, expiration))); + tasks_.push_back(std::make_shared(value, expiration)); // If idle thread is available notify it, otherwise all worker threads are // running and will get around to this task in time. @@ -494,7 +496,7 @@ void ThreadManager::Impl::remove(shared_ptr task) { "started"); } - for (TaskQueue::iterator it = tasks_.begin(); it != tasks_.end(); ++it) + for (auto it = tasks_.begin(); it != tasks_.end(); ++it) { if ((*it)->getRunnable() == task) { @@ -504,7 +506,7 @@ void ThreadManager::Impl::remove(shared_ptr task) { } } -stdcxx::shared_ptr ThreadManager::Impl::removeNextPending() { +std::shared_ptr ThreadManager::Impl::removeNextPending() { Guard g(mutex_); if (state_ != ThreadManager::STARTED) { throw IllegalStateException( @@ -513,7 +515,7 @@ stdcxx::shared_ptr ThreadManager::Impl::removeNextPending() { } if (tasks_.empty()) { - return stdcxx::shared_ptr(); + return std::shared_ptr(); } shared_ptr task = tasks_.front(); @@ -524,15 +526,14 @@ stdcxx::shared_ptr ThreadManager::Impl::removeNextPending() { void ThreadManager::Impl::removeExpired(bool justOne) { // this is always called under a lock - int64_t now = 0LL; + if (tasks_.empty()) { + return; + } + auto now = std::chrono::steady_clock::now(); - for (TaskQueue::iterator it = tasks_.begin(); it != tasks_.end(); ) + for (auto it = tasks_.begin(); it != tasks_.end(); ) { - if (now == 0LL) { - now = Util::currentTime(); - } - - if ((*it)->getExpireTime() > 0LL && (*it)->getExpireTime() < now) { + if ((*it)->getExpireTime() && *((*it)->getExpireTime()) < now) { if (expireCallback_) { expireCallback_((*it)->getRunnable()); } @@ -560,7 +561,7 @@ class SimpleThreadManager : public ThreadManager::Impl { SimpleThreadManager(size_t workerCount = 4, size_t pendingTaskCountMax = 0) : workerCount_(workerCount), pendingTaskCountMax_(pendingTaskCountMax) {} - void start() { + void start() override { ThreadManager::Impl::pendingTaskCountMax(pendingTaskCountMax_); ThreadManager::Impl::start(); addWorker(workerCount_); diff --git a/lib/cpp/src/thrift/concurrency/ThreadManager.h b/lib/cpp/src/thrift/concurrency/ThreadManager.h index b3b75421d9a..7b202ca6ad1 100644 --- a/lib/cpp/src/thrift/concurrency/ThreadManager.h +++ b/lib/cpp/src/thrift/concurrency/ThreadManager.h @@ -20,9 +20,9 @@ #ifndef _THRIFT_CONCURRENCY_THREADMANAGER_H_ #define _THRIFT_CONCURRENCY_THREADMANAGER_H_ 1 -#include -#include -#include +#include +#include +#include namespace apache { namespace thrift { @@ -55,12 +55,12 @@ class ThreadManager; class ThreadManager { protected: - ThreadManager() {} + ThreadManager() = default; public: - typedef apache::thrift::stdcxx::function)> ExpireCallback; + typedef std::function)> ExpireCallback; - virtual ~ThreadManager() {} + virtual ~ThreadManager() = default; /** * Starts the thread manager. Verifies all attributes have been properly @@ -87,14 +87,14 @@ class ThreadManager { /** * \returns the current thread factory */ - virtual stdcxx::shared_ptr threadFactory() const = 0; + virtual std::shared_ptr 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(stdcxx::shared_ptr value) = 0; + virtual void threadFactory(std::shared_ptr value) = 0; /** * Adds worker thread(s). @@ -140,7 +140,7 @@ class ThreadManager { * Gets the number of tasks which have been expired without being run * since start() was called. */ - virtual size_t expiredTaskCount() = 0; + virtual size_t expiredTaskCount() const = 0; /** * Adds a task to be executed at some time in the future by a worker thread. @@ -161,21 +161,21 @@ class ThreadManager { * * @throws TooManyPendingTasksException Pending task count exceeds max pending task count */ - virtual void add(stdcxx::shared_ptr task, + virtual void add(std::shared_ptr task, int64_t timeout = 0LL, int64_t expiration = 0LL) = 0; /** * Removes a pending task */ - virtual void remove(stdcxx::shared_ptr task) = 0; + virtual void remove(std::shared_ptr task) = 0; /** * Remove the next pending task which would be run. * * @return the task removed. */ - virtual stdcxx::shared_ptr removeNextPending() = 0; + virtual std::shared_ptr removeNextPending() = 0; /** * Remove tasks from front of task queue that have expired. @@ -190,14 +190,14 @@ class ThreadManager { */ virtual void setExpireCallback(ExpireCallback expireCallback) = 0; - static stdcxx::shared_ptr newThreadManager(); + static std::shared_ptr 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 stdcxx::shared_ptr newSimpleThreadManager(size_t count = 4, + static std::shared_ptr newSimpleThreadManager(size_t count = 4, size_t pendingTaskCountMax = 0); class Task; diff --git a/lib/cpp/src/thrift/concurrency/TimerManager.cpp b/lib/cpp/src/thrift/concurrency/TimerManager.cpp index 20171467e73..703c19ed1db 100644 --- a/lib/cpp/src/thrift/concurrency/TimerManager.cpp +++ b/lib/cpp/src/thrift/concurrency/TimerManager.cpp @@ -19,18 +19,18 @@ #include #include -#include #include #include +#include #include namespace apache { namespace thrift { namespace concurrency { -using stdcxx::shared_ptr; -using stdcxx::weak_ptr; +using std::shared_ptr; +using std::weak_ptr; /** * TimerManager class @@ -44,9 +44,9 @@ class TimerManager::Task : public Runnable { Task(shared_ptr runnable) : runnable_(runnable), state_(WAITING) {} - ~Task() {} + ~Task() override = default; - void run() { + void run() override { if (state_ == EXECUTING) { runnable_->run(); state_ = COMPLETE; @@ -68,7 +68,7 @@ class TimerManager::Dispatcher : public Runnable { public: Dispatcher(TimerManager* manager) : manager_(manager) {} - ~Dispatcher() {} + ~Dispatcher() override = default; /** * Dispatcher entry point @@ -76,7 +76,7 @@ class TimerManager::Dispatcher : public Runnable { * As long as dispatcher thread is running, pull tasks off the task taskMap_ * and execute. */ - void run() { + void run() override { { Synchronized s(manager_->monitor_); if (manager_->state_ == TimerManager::STARTING) { @@ -90,25 +90,26 @@ class TimerManager::Dispatcher : public Runnable { { Synchronized s(manager_->monitor_); task_iterator expiredTaskEnd; - int64_t now = Util::currentTime(); + auto now = std::chrono::steady_clock::now(); while (manager_->state_ == TimerManager::STARTED && (expiredTaskEnd = manager_->taskMap_.upper_bound(now)) == manager_->taskMap_.begin()) { - int64_t timeout = 0LL; + std::chrono::milliseconds timeout(0); if (!manager_->taskMap_.empty()) { - timeout = manager_->taskMap_.begin()->first - now; - } - assert((timeout != 0 && manager_->taskCount_ > 0) - || (timeout == 0 && manager_->taskCount_ == 0)); - try { - manager_->monitor_.wait(timeout); - } catch (TimedOutException&) { + timeout = std::chrono::duration_cast(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 = Util::currentTime(); + now = std::chrono::steady_clock::now(); } if (manager_->state_ == TimerManager::STARTED) { - for (task_iterator ix = manager_->taskMap_.begin(); ix != expiredTaskEnd; ix++) { + for (auto ix = manager_->taskMap_.begin(); ix != expiredTaskEnd; ix++) { shared_ptr task = ix->second; expiredTasks.insert(task); task->it_ = manager_->taskMap_.end(); @@ -121,10 +122,8 @@ class TimerManager::Dispatcher : public Runnable { } } - for (std::set >::iterator ix = expiredTasks.begin(); - ix != expiredTasks.end(); - ++ix) { - (*ix)->run(); + for (const auto & expiredTask : expiredTasks) { + expiredTask->run(); } } while (manager_->state_ == TimerManager::STARTED); @@ -133,7 +132,7 @@ class TimerManager::Dispatcher : public Runnable { Synchronized s(manager_->monitor_); if (manager_->state_ == TimerManager::STOPPING) { manager_->state_ = TimerManager::STOPPED; - manager_->monitor_.notify(); + manager_->monitor_.notifyAll(); } } return; @@ -152,7 +151,7 @@ class TimerManager::Dispatcher : public Runnable { TimerManager::TimerManager() : taskCount_(0), state_(TimerManager::UNINITIALIZED), - dispatcher_(shared_ptr(new Dispatcher(this))) { + dispatcher_(std::make_shared(this)) { } #if defined(_MSC_VER) @@ -221,7 +220,7 @@ void TimerManager::stop() { taskMap_.clear(); // Remove dispatcher's reference to us. - dispatcher_->manager_ = NULL; + dispatcher_->manager_ = nullptr; } } @@ -239,64 +238,39 @@ size_t TimerManager::taskCount() const { return taskCount_; } -TimerManager::Timer TimerManager::add(shared_ptr task, int64_t timeout) { - int64_t now = Util::currentTime(); - timeout += now; - - { - 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 : timeout < taskMap_.begin()->first; - - shared_ptr timer(new Task(task)); - taskCount_++; - timer->it_ = taskMap_.insert(std::pair >(timeout, 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; - } +TimerManager::Timer TimerManager::add(shared_ptr task, const std::chrono::milliseconds &timeout) { + return add(task, std::chrono::steady_clock::now() + timeout); } TimerManager::Timer TimerManager::add(shared_ptr task, - const struct THRIFT_TIMESPEC& value) { - - int64_t expiration; - Util::toMilliseconds(expiration, value); - - int64_t now = Util::currentTime(); + const std::chrono::time_point& abstime) { + auto now = std::chrono::steady_clock::now(); - if (expiration < now) { + if (abstime < now) { throw InvalidArgumentException(); } + Synchronized s(monitor_); + if (state_ != TimerManager::STARTED) { + throw IllegalStateException(); + } - return add(task, expiration - now); -} - -TimerManager::Timer TimerManager::add(shared_ptr task, - const struct timeval& value) { - - int64_t expiration; - Util::toMilliseconds(expiration, value); + // 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; - int64_t now = Util::currentTime(); + shared_ptr timer(new Task(task)); + taskCount_++; + timer->it_ = taskMap_.emplace(abstime, timer); - if (expiration < now) { - throw InvalidArgumentException(); + // 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 add(task, expiration - now); + return timer; } void TimerManager::remove(shared_ptr task) { @@ -305,7 +279,7 @@ void TimerManager::remove(shared_ptr task) { throw IllegalStateException(); } bool found = false; - for (task_iterator ix = taskMap_.begin(); ix != taskMap_.end();) { + for (auto ix = taskMap_.begin(); ix != taskMap_.end();) { if (*ix->second == task) { found = true; taskCount_--; diff --git a/lib/cpp/src/thrift/concurrency/TimerManager.h b/lib/cpp/src/thrift/concurrency/TimerManager.h index 2bfc6a755bc..44d4738d5f2 100644 --- a/lib/cpp/src/thrift/concurrency/TimerManager.h +++ b/lib/cpp/src/thrift/concurrency/TimerManager.h @@ -20,13 +20,11 @@ #ifndef _THRIFT_CONCURRENCY_TIMERMANAGER_H_ #define _THRIFT_CONCURRENCY_TIMERMANAGER_H_ 1 -#include #include -#include +#include -#include +#include #include -#include namespace apache { namespace thrift { @@ -43,15 +41,15 @@ class TimerManager { public: class Task; - typedef stdcxx::weak_ptr Timer; + typedef std::weak_ptr Timer; TimerManager(); virtual ~TimerManager(); - virtual stdcxx::shared_ptr threadFactory() const; + virtual std::shared_ptr threadFactory() const; - virtual void threadFactory(stdcxx::shared_ptr value); + virtual void threadFactory(std::shared_ptr value); /** * Starts the timer manager service @@ -74,25 +72,17 @@ class TimerManager { * @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(stdcxx::shared_ptr task, int64_t timeout); + virtual Timer add(std::shared_ptr task, const std::chrono::milliseconds &timeout); + Timer add(std::shared_ptr 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 timeout Absolute time in the future to execute task. + * @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(stdcxx::shared_ptr task, const struct THRIFT_TIMESPEC& timeout); - - /** - * Adds a task to be executed at some time in the future by a worker thread. - * - * @param task The task to execute - * @param timeout Absolute time in the future to execute task. - * @return Handle of the timer, which can be used to remove the timer. - */ - virtual Timer add(stdcxx::shared_ptr task, const struct timeval& timeout); + virtual Timer add(std::shared_ptr task, const std::chrono::time_point& abstime); /** * Removes a pending task @@ -106,7 +96,7 @@ class TimerManager { * @throws UncancellableTaskException Specified task is already being * executed or has completed execution. */ - virtual void remove(stdcxx::shared_ptr task); + virtual void remove(std::shared_ptr task); /** * Removes a single pending task @@ -127,17 +117,17 @@ class TimerManager { virtual STATE state() const; private: - stdcxx::shared_ptr threadFactory_; + std::shared_ptr threadFactory_; friend class Task; - std::multimap > taskMap_; + std::multimap, std::shared_ptr > taskMap_; size_t taskCount_; Monitor monitor_; STATE state_; class Dispatcher; friend class Dispatcher; - stdcxx::shared_ptr dispatcher_; - stdcxx::shared_ptr dispatcherThread_; - typedef std::multimap >::iterator task_iterator; + std::shared_ptr dispatcher_; + std::shared_ptr dispatcherThread_; + using task_iterator = decltype(taskMap_)::iterator; typedef std::pair task_range; }; } diff --git a/lib/cpp/src/thrift/concurrency/Util.h b/lib/cpp/src/thrift/concurrency/Util.h deleted file mode 100644 index 1a915993f39..00000000000 --- a/lib/cpp/src/thrift/concurrency/Util.h +++ /dev/null @@ -1,151 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -#ifndef _THRIFT_CONCURRENCY_UTIL_H_ -#define _THRIFT_CONCURRENCY_UTIL_H_ 1 - -#include -#include -#include -#include - -#ifdef HAVE_SYS_TIME_H -#include -#endif - -#include - -namespace apache { -namespace thrift { -namespace concurrency { - -/** - * Utility methods - * - * This class contains basic utility methods for converting time formats, - * and other common platform-dependent concurrency operations. - * It should not be included in API headers for other concurrency library - * headers, since it will, by definition, pull in all sorts of horrid - * platform dependent stuff. Rather it should be inluded directly in - * concurrency library implementation source. - * - * @version $Id:$ - */ -class Util { - - static const int64_t NS_PER_S = 1000000000LL; - static const int64_t US_PER_S = 1000000LL; - static const int64_t MS_PER_S = 1000LL; - - static const int64_t NS_PER_MS = NS_PER_S / MS_PER_S; - static const int64_t NS_PER_US = NS_PER_S / US_PER_S; - static const int64_t US_PER_MS = US_PER_S / MS_PER_S; - -public: - /** - * Converts millisecond timestamp into a THRIFT_TIMESPEC struct - * - * @param struct THRIFT_TIMESPEC& result - * @param time or duration in milliseconds - */ - static void toTimespec(struct THRIFT_TIMESPEC& result, int64_t value) { - result.tv_sec = value / MS_PER_S; // ms to s - result.tv_nsec = (value % MS_PER_S) * NS_PER_MS; // ms to ns - } - - static void toTimeval(struct timeval& result, int64_t value) { - result.tv_sec = static_cast(value / MS_PER_S); // ms to s - result.tv_usec = static_cast((value % MS_PER_S) * US_PER_MS); // ms to us - } - - static void toTicks(int64_t& result, - int64_t secs, - int64_t oldTicks, - int64_t oldTicksPerSec, - int64_t newTicksPerSec) { - result = secs * newTicksPerSec; - result += oldTicks * newTicksPerSec / oldTicksPerSec; - - int64_t oldPerNew = oldTicksPerSec / newTicksPerSec; - if (oldPerNew && ((oldTicks % oldPerNew) >= (oldPerNew / 2))) { - ++result; - } - } - /** - * Converts struct THRIFT_TIMESPEC to arbitrary-sized ticks since epoch - */ - static void toTicks(int64_t& result, const struct THRIFT_TIMESPEC& value, int64_t ticksPerSec) { - return toTicks(result, value.tv_sec, value.tv_nsec, NS_PER_S, ticksPerSec); - } - - /** - * Converts struct timeval to arbitrary-sized ticks since epoch - */ - static void toTicks(int64_t& result, const struct timeval& value, int64_t ticksPerSec) { - return toTicks(result, (unsigned long)value.tv_sec, (unsigned long)value.tv_usec, US_PER_S, ticksPerSec); - } - - /** - * Converts struct THRIFT_TIMESPEC to milliseconds - */ - static void toMilliseconds(int64_t& result, const struct THRIFT_TIMESPEC& value) { - return toTicks(result, value, MS_PER_S); - } - - /** - * Converts struct timeval to milliseconds - */ - static void toMilliseconds(int64_t& result, const struct timeval& value) { - return toTicks(result, value, MS_PER_S); - } - - /** - * Converts struct THRIFT_TIMESPEC to microseconds - */ - static void toUsec(int64_t& result, const struct THRIFT_TIMESPEC& value) { - return toTicks(result, value, US_PER_S); - } - - /** - * Converts struct timeval to microseconds - */ - static void toUsec(int64_t& result, const struct timeval& value) { - return toTicks(result, value, US_PER_S); - } - - /** - * Get current time as a number of arbitrary-size ticks from epoch - */ - static int64_t currentTimeTicks(int64_t ticksPerSec); - - /** - * Get current time as milliseconds from epoch - */ - static int64_t currentTime() { return currentTimeTicks(MS_PER_S); } - - /** - * Get current time as micros from epoch - */ - static int64_t currentTimeUsec() { return currentTimeTicks(US_PER_S); } -}; -} -} -} // apache::thrift::concurrency - -#endif // #ifndef _THRIFT_CONCURRENCY_UTIL_H_ diff --git a/lib/cpp/src/thrift/processor/PeekProcessor.cpp b/lib/cpp/src/thrift/processor/PeekProcessor.cpp index fa11a721c6b..4cd58b8dbb9 100644 --- a/lib/cpp/src/thrift/processor/PeekProcessor.cpp +++ b/lib/cpp/src/thrift/processor/PeekProcessor.cpp @@ -31,29 +31,28 @@ PeekProcessor::PeekProcessor() { memoryBuffer_.reset(new TMemoryBuffer()); targetTransport_ = memoryBuffer_; } -PeekProcessor::~PeekProcessor() { -} +PeekProcessor::~PeekProcessor() = default; -void PeekProcessor::initialize(stdcxx::shared_ptr actualProcessor, - stdcxx::shared_ptr protocolFactory, - stdcxx::shared_ptr transportFactory) { +void PeekProcessor::initialize(std::shared_ptr actualProcessor, + std::shared_ptr protocolFactory, + std::shared_ptr transportFactory) { actualProcessor_ = actualProcessor; pipedProtocol_ = protocolFactory->getProtocol(targetTransport_); transportFactory_ = transportFactory; transportFactory_->initializeTargetTransport(targetTransport_); } -stdcxx::shared_ptr PeekProcessor::getPipedTransport(stdcxx::shared_ptr in) { +std::shared_ptr PeekProcessor::getPipedTransport(std::shared_ptr in) { return transportFactory_->getTransport(in); } -void PeekProcessor::setTargetTransport(stdcxx::shared_ptr targetTransport) { +void PeekProcessor::setTargetTransport(std::shared_ptr targetTransport) { targetTransport_ = targetTransport; - if (stdcxx::dynamic_pointer_cast(targetTransport_)) { - memoryBuffer_ = stdcxx::dynamic_pointer_cast(targetTransport); - } else if (stdcxx::dynamic_pointer_cast(targetTransport_)) { - memoryBuffer_ = stdcxx::dynamic_pointer_cast( - stdcxx::dynamic_pointer_cast(targetTransport_)->getTargetTransport()); + if (std::dynamic_pointer_cast(targetTransport_)) { + memoryBuffer_ = std::dynamic_pointer_cast(targetTransport); + } else if (std::dynamic_pointer_cast(targetTransport_)) { + memoryBuffer_ = std::dynamic_pointer_cast( + std::dynamic_pointer_cast(targetTransport_)->getTargetTransport()); } if (!memoryBuffer_) { @@ -62,8 +61,8 @@ void PeekProcessor::setTargetTransport(stdcxx::shared_ptr targetTran } } -bool PeekProcessor::process(stdcxx::shared_ptr in, - stdcxx::shared_ptr out, +bool PeekProcessor::process(std::shared_ptr in, + std::shared_ptr out, void* connectionContext) { std::string fname; @@ -120,7 +119,7 @@ void PeekProcessor::peekBuffer(uint8_t* buffer, uint32_t size) { (void)size; } -void PeekProcessor::peek(stdcxx::shared_ptr in, TType ftype, int16_t fid) { +void PeekProcessor::peek(std::shared_ptr in, TType ftype, int16_t fid) { (void)fid; in->skip(ftype); } diff --git a/lib/cpp/src/thrift/processor/PeekProcessor.h b/lib/cpp/src/thrift/processor/PeekProcessor.h index f5c10da682b..ae565fc4bbd 100644 --- a/lib/cpp/src/thrift/processor/PeekProcessor.h +++ b/lib/cpp/src/thrift/processor/PeekProcessor.h @@ -25,7 +25,7 @@ #include #include #include -#include +#include namespace apache { namespace thrift { @@ -40,41 +40,41 @@ class PeekProcessor : public apache::thrift::TProcessor { public: PeekProcessor(); - virtual ~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( - stdcxx::shared_ptr actualProcessor, - stdcxx::shared_ptr protocolFactory, - stdcxx::shared_ptr transportFactory); + std::shared_ptr actualProcessor, + std::shared_ptr protocolFactory, + std::shared_ptr transportFactory); - stdcxx::shared_ptr getPipedTransport( - stdcxx::shared_ptr in); + std::shared_ptr getPipedTransport( + std::shared_ptr in); - void setTargetTransport(stdcxx::shared_ptr targetTransport); + void setTargetTransport(std::shared_ptr targetTransport); - virtual bool process(stdcxx::shared_ptr in, - stdcxx::shared_ptr out, - void* connectionContext); + bool process(std::shared_ptr in, + std::shared_ptr 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(stdcxx::shared_ptr in, + virtual void peek(std::shared_ptr in, apache::thrift::protocol::TType ftype, int16_t fid); virtual void peekEnd(); private: - stdcxx::shared_ptr actualProcessor_; - stdcxx::shared_ptr pipedProtocol_; - stdcxx::shared_ptr transportFactory_; - stdcxx::shared_ptr memoryBuffer_; - stdcxx::shared_ptr targetTransport_; + std::shared_ptr actualProcessor_; + std::shared_ptr pipedProtocol_; + std::shared_ptr transportFactory_; + std::shared_ptr memoryBuffer_; + std::shared_ptr targetTransport_; }; } } diff --git a/lib/cpp/src/thrift/processor/StatsProcessor.h b/lib/cpp/src/thrift/processor/StatsProcessor.h index 8f6725f000c..e98efb82c81 100644 --- a/lib/cpp/src/thrift/processor/StatsProcessor.h +++ b/lib/cpp/src/thrift/processor/StatsProcessor.h @@ -20,7 +20,7 @@ #ifndef STATSPROCESSOR_H #define STATSPROCESSOR_H -#include +#include #include #include #include @@ -38,8 +38,8 @@ class StatsProcessor : public apache::thrift::TProcessor { StatsProcessor(bool print, bool frequency) : print_(print), frequency_(frequency) {} virtual ~StatsProcessor(){}; - virtual bool process(stdcxx::shared_ptr piprot, - stdcxx::shared_ptr poprot, + virtual bool process(std::shared_ptr piprot, + std::shared_ptr poprot, void* serverContext) { piprot_ = piprot; @@ -229,7 +229,7 @@ class StatsProcessor : public apache::thrift::TProcessor { } } - stdcxx::shared_ptr piprot_; + std::shared_ptr piprot_; std::map frequency_map_; bool print_; diff --git a/lib/cpp/src/thrift/processor/TMultiplexedProcessor.h b/lib/cpp/src/thrift/processor/TMultiplexedProcessor.h index aa3d49f8111..85c0affc265 100644 --- a/lib/cpp/src/thrift/processor/TMultiplexedProcessor.h +++ b/lib/cpp/src/thrift/processor/TMultiplexedProcessor.h @@ -36,13 +36,13 @@ namespace protocol { */ class StoredMessageProtocol : public TProtocolDecorator { public: - StoredMessageProtocol(stdcxx::shared_ptr _protocol, + StoredMessageProtocol(std::shared_ptr _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) { + uint32_t readMessageBegin_virt(std::string& _name, TMessageType& _type, int32_t& _seqid) override { _name = name; _type = type; @@ -65,19 +65,19 @@ class StoredMessageProtocol : public TProtocolDecorator { * processors with it, as shown in the following example:

* *
- * stdcxx::shared_ptr processor(new TMultiplexedProcessor()); + * std::shared_ptr processor(new TMultiplexedProcessor()); * * processor->registerProcessor( * "Calculator", - * stdcxx::shared_ptr( new CalculatorProcessor( - * stdcxx::shared_ptr( new CalculatorHandler())))); + * std::shared_ptr( new CalculatorProcessor( + * std::shared_ptr( new CalculatorHandler())))); * * processor->registerProcessor( * "WeatherReport", - * stdcxx::shared_ptr( new WeatherReportProcessor( - * stdcxx::shared_ptr( new WeatherReportHandler())))); + * std::shared_ptr( new WeatherReportProcessor( + * std::shared_ptr( new WeatherReportHandler())))); * - * stdcxx::shared_ptr transport(new TServerSocket(9090)); + * std::shared_ptr transport(new TServerSocket(9090)); * TSimpleServer server(processor, transport); * * server.serve(); @@ -85,7 +85,7 @@ class StoredMessageProtocol : public TProtocolDecorator { */ class TMultiplexedProcessor : public TProcessor { public: - typedef std::map > services_t; + typedef std::map > services_t; /** * 'Register' a service with this TMultiplexedProcessor. This @@ -98,7 +98,7 @@ class TMultiplexedProcessor : public TProcessor { * as "handlers", e.g. WeatherReportHandler, * implementing WeatherReportIf interface. */ - void registerProcessor(const std::string& serviceName, stdcxx::shared_ptr processor) { + void registerProcessor(const std::string& serviceName, std::shared_ptr processor) { services[serviceName] = processor; } @@ -106,15 +106,15 @@ class TMultiplexedProcessor : public TProcessor { * Register a service to be called to process queries without service name * \param [in] processor Implementation of a service. */ - void registerDefault(const stdcxx::shared_ptr& processor) { + void registerDefault(const std::shared_ptr& processor) { defaultProcessor = processor; } /** * Chew up invalid input and return an exception to throw. */ - TException protocol_error(stdcxx::shared_ptr in, - stdcxx::shared_ptr out, + TException protocol_error(std::shared_ptr in, + std::shared_ptr out, const std::string& name, int32_t seqid, const std::string& msg) const { @@ -147,9 +147,9 @@ class TMultiplexedProcessor : public TProcessor { * the service name was not found in the message, or if the service * name was not found in the service map. */ - bool process(stdcxx::shared_ptr in, - stdcxx::shared_ptr out, - void* connectionContext) { + bool process(std::shared_ptr in, + std::shared_ptr out, + void* connectionContext) override { std::string name; protocol::TMessageType type; int32_t seqid; @@ -174,14 +174,14 @@ class TMultiplexedProcessor : public TProcessor { // name and the name of the method to call. if (tokens.size() == 2) { // Search for a processor associated with this service name. - services_t::iterator it = services.find(tokens[0]); + auto it = services.find(tokens[0]); if (it != services.end()) { - stdcxx::shared_ptr processor = it->second; + std::shared_ptr processor = it->second; // Let the processor registered for this service name // process the message. return processor - ->process(stdcxx::shared_ptr( + ->process(std::shared_ptr( new protocol::StoredMessageProtocol(in, tokens[1], type, seqid)), out, connectionContext); @@ -195,7 +195,7 @@ class TMultiplexedProcessor : public TProcessor { if (defaultProcessor) { // non-multiplexed client forwards to default processor return defaultProcessor - ->process(stdcxx::shared_ptr( + ->process(std::shared_ptr( new protocol::StoredMessageProtocol(in, tokens[0], type, seqid)), out, connectionContext); @@ -216,7 +216,7 @@ class TMultiplexedProcessor : public TProcessor { //! If a non-multi client requests something, it goes to the //! default processor (if one is defined) for backwards compatibility. - stdcxx::shared_ptr defaultProcessor; + std::shared_ptr defaultProcessor; }; } } diff --git a/lib/cpp/src/thrift/protocol/TBase64Utils.cpp b/lib/cpp/src/thrift/protocol/TBase64Utils.cpp index beb76ebdff5..7474f5af8ce 100644 --- a/lib/cpp/src/thrift/protocol/TBase64Utils.cpp +++ b/lib/cpp/src/thrift/protocol/TBase64Utils.cpp @@ -19,8 +19,6 @@ #include -#include - using std::string; namespace apache { diff --git a/lib/cpp/src/thrift/protocol/TBinaryProtocol.h b/lib/cpp/src/thrift/protocol/TBinaryProtocol.h index f28d27872b9..b43144017c3 100644 --- a/lib/cpp/src/thrift/protocol/TBinaryProtocol.h +++ b/lib/cpp/src/thrift/protocol/TBinaryProtocol.h @@ -23,7 +23,7 @@ #include #include -#include +#include namespace apache { namespace thrift { @@ -41,7 +41,7 @@ class TBinaryProtocolT : public TVirtualProtocol trans) + TBinaryProtocolT(std::shared_ptr trans) : TVirtualProtocol >(trans), trans_(trans.get()), string_limit_(0), @@ -49,7 +49,7 @@ class TBinaryProtocolT : public TVirtualProtocol trans, + TBinaryProtocolT(std::shared_ptr trans, int32_t string_limit, int32_t container_limit, bool strict_read, @@ -166,6 +166,24 @@ class TBinaryProtocolT : public TVirtualProtocolcheckReadBytesAvailable(set.size_ * getMinSerializedSize(set.elemType_)); + } + + void checkReadBytesAvailable(TList& list) + { + trans_->checkReadBytesAvailable(list.size_ * getMinSerializedSize(list.elemType_)); + } + + void checkReadBytesAvailable(TMap& map) + { + int elmSize = getMinSerializedSize(map.keyType_) + getMinSerializedSize(map.valueType_); + trans_->checkReadBytesAvailable(map.size_ * elmSize); + } + protected: template uint32_t readStringBody(StrType& str, int32_t sz); @@ -201,7 +219,7 @@ class TBinaryProtocolFactoryT : public TProtocolFactory { strict_read_(strict_read), strict_write_(strict_write) {} - virtual ~TBinaryProtocolFactoryT() {} + ~TBinaryProtocolFactoryT() override = default; void setStringSizeLimit(int32_t string_limit) { string_limit_ = string_limit; } @@ -212,8 +230,8 @@ class TBinaryProtocolFactoryT : public TProtocolFactory { strict_write_ = strict_write; } - stdcxx::shared_ptr getProtocol(stdcxx::shared_ptr trans) { - stdcxx::shared_ptr specific_trans = stdcxx::dynamic_pointer_cast(trans); + std::shared_ptr getProtocol(std::shared_ptr trans) override { + std::shared_ptr specific_trans = std::dynamic_pointer_cast(trans); TProtocol* prot; if (specific_trans) { prot = new TBinaryProtocolT(specific_trans, @@ -229,7 +247,7 @@ class TBinaryProtocolFactoryT : public TProtocolFactory { strict_write_); } - return stdcxx::shared_ptr(prot); + return std::shared_ptr(prot); } private: diff --git a/lib/cpp/src/thrift/protocol/TBinaryProtocol.tcc b/lib/cpp/src/thrift/protocol/TBinaryProtocol.tcc index d6f6dbb3d88..755f24386e3 100644 --- a/lib/cpp/src/thrift/protocol/TBinaryProtocol.tcc +++ b/lib/cpp/src/thrift/protocol/TBinaryProtocol.tcc @@ -21,6 +21,7 @@ #define _THRIFT_PROTOCOL_TBINARYPROTOCOL_TCC_ 1 #include +#include #include @@ -144,31 +145,31 @@ uint32_t TBinaryProtocolT::writeByte(const int8_t byte) template uint32_t TBinaryProtocolT::writeI16(const int16_t i16) { - int16_t net = (int16_t)ByteOrder_::toWire16(i16); + auto net = (int16_t)ByteOrder_::toWire16(i16); this->trans_->write((uint8_t*)&net, 2); return 2; } template uint32_t TBinaryProtocolT::writeI32(const int32_t i32) { - int32_t net = (int32_t)ByteOrder_::toWire32(i32); + auto net = (int32_t)ByteOrder_::toWire32(i32); this->trans_->write((uint8_t*)&net, 4); return 4; } template uint32_t TBinaryProtocolT::writeI64(const int64_t i64) { - int64_t net = (int64_t)ByteOrder_::toWire64(i64); + auto net = (int64_t)ByteOrder_::toWire64(i64); this->trans_->write((uint8_t*)&net, 8); return 8; } template uint32_t TBinaryProtocolT::writeDouble(const double dub) { - BOOST_STATIC_ASSERT(sizeof(double) == sizeof(uint64_t)); - BOOST_STATIC_ASSERT(std::numeric_limits::is_iec559); + static_assert(sizeof(double) == sizeof(uint64_t), "sizeof(double) == sizeof(uint64_t)"); + static_assert(std::numeric_limits::is_iec559, "std::numeric_limits::is_iec559"); - uint64_t bits = bitwise_cast(dub); + auto bits = bitwise_cast(dub); bits = ByteOrder_::toWire64(bits); this->trans_->write((uint8_t*)&bits, 8); return 8; @@ -179,7 +180,7 @@ template uint32_t TBinaryProtocolT::writeString(const StrType& str) { if (str.size() > static_cast((std::numeric_limits::max)())) throw TProtocolException(TProtocolException::SIZE_LIMIT); - uint32_t size = static_cast(str.size()); + auto size = static_cast(str.size()); uint32_t result = writeI32((int32_t)size); if (size > 0) { this->trans_->write((uint8_t*)str.data(), size); @@ -285,6 +286,10 @@ uint32_t TBinaryProtocolT::readMapBegin(TType& keyType, throw TProtocolException(TProtocolException::SIZE_LIMIT); } size = (uint32_t)sizei; + + TMap map(keyType, valType, size); + checkReadBytesAvailable(map); + return result; } @@ -307,6 +312,10 @@ uint32_t TBinaryProtocolT::readListBegin(TType& elemType throw TProtocolException(TProtocolException::SIZE_LIMIT); } size = (uint32_t)sizei; + + TList list(elemType, size); + checkReadBytesAvailable(list); + return result; } @@ -329,6 +338,10 @@ uint32_t TBinaryProtocolT::readSetBegin(TType& elemType, throw TProtocolException(TProtocolException::SIZE_LIMIT); } size = (uint32_t)sizei; + + TSet set(elemType, size); + checkReadBytesAvailable(set); + return result; } @@ -388,8 +401,8 @@ uint32_t TBinaryProtocolT::readI64(int64_t& i64) { template uint32_t TBinaryProtocolT::readDouble(double& dub) { - BOOST_STATIC_ASSERT(sizeof(double) == sizeof(uint64_t)); - BOOST_STATIC_ASSERT(std::numeric_limits::is_iec559); + static_assert(sizeof(double) == sizeof(uint64_t), "sizeof(double) == sizeof(uint64_t)"); + static_assert(std::numeric_limits::is_iec559, "std::numeric_limits::is_iec559"); union bytes { uint8_t b[8]; @@ -437,7 +450,7 @@ uint32_t TBinaryProtocolT::readStringBody(StrType& str, // Try to borrow first const uint8_t* borrow_buf; uint32_t got = size; - if ((borrow_buf = this->trans_->borrow(NULL, &got))) { + if ((borrow_buf = this->trans_->borrow(nullptr, &got))) { str.assign((const char*)borrow_buf, size); this->trans_->consume(size); return size; @@ -447,6 +460,30 @@ uint32_t TBinaryProtocolT::readStringBody(StrType& str, this->trans_->readAll(reinterpret_cast(&str[0]), size); return (uint32_t)size; } + +// Return the minimum number of bytes a type will consume on the wire +template +int TBinaryProtocolT::getMinSerializedSize(TType type) +{ + switch (type) + { + case T_STOP: return 0; + case T_VOID: return 0; + case T_BOOL: return sizeof(int8_t); + case T_BYTE: return sizeof(int8_t); + case T_DOUBLE: return sizeof(double); + case T_I16: return sizeof(short); + case T_I32: return sizeof(int); + case T_I64: return sizeof(long); + case T_STRING: return sizeof(int); // string length + case T_STRUCT: return 0; // empty struct + case T_MAP: return sizeof(int); // element count + case T_SET: return sizeof(int); // element count + case T_LIST: return sizeof(int); // element count + default: throw TProtocolException(TProtocolException::UNKNOWN, "unrecognized type code"); + } +} + } } } // apache::thrift::protocol diff --git a/lib/cpp/src/thrift/protocol/TCompactProtocol.h b/lib/cpp/src/thrift/protocol/TCompactProtocol.h index e6024a9d309..6f990b2d67b 100644 --- a/lib/cpp/src/thrift/protocol/TCompactProtocol.h +++ b/lib/cpp/src/thrift/protocol/TCompactProtocol.h @@ -23,7 +23,7 @@ #include #include -#include +#include namespace apache { namespace thrift { @@ -74,33 +74,33 @@ class TCompactProtocolT : public TVirtualProtocol int16_t lastFieldId_; public: - TCompactProtocolT(stdcxx::shared_ptr trans) + TCompactProtocolT(std::shared_ptr trans) : TVirtualProtocol >(trans), trans_(trans.get()), lastFieldId_(0), string_limit_(0), - string_buf_(NULL), + string_buf_(nullptr), string_buf_size_(0), container_limit_(0) { - booleanField_.name = NULL; + booleanField_.name = nullptr; boolValue_.hasBoolValue = false; } - TCompactProtocolT(stdcxx::shared_ptr trans, + TCompactProtocolT(std::shared_ptr trans, int32_t string_limit, int32_t container_limit) : TVirtualProtocol >(trans), trans_(trans.get()), lastFieldId_(0), string_limit_(string_limit), - string_buf_(NULL), + string_buf_(nullptr), string_buf_size_(0), container_limit_(container_limit) { - booleanField_.name = NULL; + booleanField_.name = nullptr; boolValue_.hasBoolValue = false; } - ~TCompactProtocolT() { free(string_buf_); } + ~TCompactProtocolT() override { free(string_buf_); } /** * Writing functions @@ -140,6 +140,24 @@ class TCompactProtocolT : public TVirtualProtocol uint32_t writeBinary(const std::string& str); + int getMinSerializedSize(TType type); + + void checkReadBytesAvailable(TSet& set) + { + trans_->checkReadBytesAvailable(set.size_ * getMinSerializedSize(set.elemType_)); + } + + void checkReadBytesAvailable(TList& list) + { + trans_->checkReadBytesAvailable(list.size_ * getMinSerializedSize(list.elemType_)); + } + + void checkReadBytesAvailable(TMap& map) + { + int elmSize = getMinSerializedSize(map.keyType_) + getMinSerializedSize(map.valueType_); + trans_->checkReadBytesAvailable(map.size_ * elmSize); + } + /** * These methods are called by structs, but don't actually have any wired * output or purpose @@ -233,14 +251,14 @@ class TCompactProtocolFactoryT : public TProtocolFactory { TCompactProtocolFactoryT(int32_t string_limit, int32_t container_limit) : string_limit_(string_limit), container_limit_(container_limit) {} - virtual ~TCompactProtocolFactoryT() {} + ~TCompactProtocolFactoryT() override = default; void setStringSizeLimit(int32_t string_limit) { string_limit_ = string_limit; } void setContainerSizeLimit(int32_t container_limit) { container_limit_ = container_limit; } - stdcxx::shared_ptr getProtocol(stdcxx::shared_ptr trans) { - stdcxx::shared_ptr specific_trans = stdcxx::dynamic_pointer_cast(trans); + std::shared_ptr getProtocol(std::shared_ptr trans) override { + std::shared_ptr specific_trans = std::dynamic_pointer_cast(trans); TProtocol* prot; if (specific_trans) { prot = new TCompactProtocolT(specific_trans, string_limit_, container_limit_); @@ -248,7 +266,7 @@ class TCompactProtocolFactoryT : public TProtocolFactory { prot = new TCompactProtocol(trans, string_limit_, container_limit_); } - return stdcxx::shared_ptr(prot); + return std::shared_ptr(prot); } private: diff --git a/lib/cpp/src/thrift/protocol/TCompactProtocol.tcc b/lib/cpp/src/thrift/protocol/TCompactProtocol.tcc index d40c3313431..16780911c4b 100644 --- a/lib/cpp/src/thrift/protocol/TCompactProtocol.tcc +++ b/lib/cpp/src/thrift/protocol/TCompactProtocol.tcc @@ -198,7 +198,7 @@ template uint32_t TCompactProtocolT::writeBool(const bool value) { uint32_t wsize = 0; - if (booleanField_.name != NULL) { + if (booleanField_.name != nullptr) { // we haven't written the field header yet wsize += writeFieldBeginInternal(booleanField_.name, @@ -207,7 +207,7 @@ uint32_t TCompactProtocolT::writeBool(const bool value) { static_cast(value ? detail::compact::CT_BOOLEAN_TRUE : detail::compact::CT_BOOLEAN_FALSE)); - booleanField_.name = NULL; + booleanField_.name = nullptr; } else { // we're not part of a field, so just write the value wsize @@ -253,10 +253,10 @@ uint32_t TCompactProtocolT::writeI64(const int64_t i64) { */ template uint32_t TCompactProtocolT::writeDouble(const double dub) { - BOOST_STATIC_ASSERT(sizeof(double) == sizeof(uint64_t)); - BOOST_STATIC_ASSERT(std::numeric_limits::is_iec559); + static_assert(sizeof(double) == sizeof(uint64_t), "sizeof(double) == sizeof(uint64_t)"); + static_assert(std::numeric_limits::is_iec559, "std::numeric_limits::is_iec559"); - uint64_t bits = bitwise_cast(dub); + auto bits = bitwise_cast(dub); bits = THRIFT_htolell(bits); trans_->write((uint8_t*)&bits, 8); return 8; @@ -274,7 +274,7 @@ template uint32_t TCompactProtocolT::writeBinary(const std::string& str) { if(str.size() > (std::numeric_limits::max)()) throw TProtocolException(TProtocolException::SIZE_LIMIT); - uint32_t ssize = static_cast(str.size()); + auto ssize = static_cast(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 @@ -488,7 +488,7 @@ uint32_t TCompactProtocolT::readFieldBegin(std::string& name, } // mask off the 4 MSB of the type header. it could contain a field id delta. - int16_t modifier = (int16_t)(((uint8_t)byte & 0xf0) >> 4); + 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); @@ -538,6 +538,9 @@ uint32_t TCompactProtocolT::readMapBegin(TType& keyType, valType = getTType((int8_t)((uint8_t)kvType & 0xf)); size = (uint32_t)msize; + TMap map(keyType, valType, size); + checkReadBytesAvailable(map); + return rsize; } @@ -570,6 +573,9 @@ uint32_t TCompactProtocolT::readListBegin(TType& elemType, elemType = getTType((int8_t)(size_and_type & 0x0f)); size = (uint32_t)lsize; + TList list(elemType, size); + checkReadBytesAvailable(list); + return rsize; } @@ -653,8 +659,8 @@ uint32_t TCompactProtocolT::readI64(int64_t& i64) { */ template uint32_t TCompactProtocolT::readDouble(double& dub) { - BOOST_STATIC_ASSERT(sizeof(double) == sizeof(uint64_t)); - BOOST_STATIC_ASSERT(std::numeric_limits::is_iec559); + static_assert(sizeof(double) == sizeof(uint64_t), "sizeof(double) == sizeof(uint64_t)"); + static_assert(std::numeric_limits::is_iec559, "std::numeric_limits::is_iec559"); union { uint64_t bits; @@ -695,9 +701,9 @@ uint32_t TCompactProtocolT::readBinary(std::string& str) { } // Use the heap here to prevent stack overflow for v. large strings - if (size > string_buf_size_ || string_buf_ == NULL) { + if (size > string_buf_size_ || string_buf_ == nullptr) { void* new_string_buf = std::realloc(string_buf_, (uint32_t)size); - if (new_string_buf == NULL) { + if (new_string_buf == nullptr) { throw std::bad_alloc(); } string_buf_ = (uint8_t*)new_string_buf; @@ -706,6 +712,8 @@ uint32_t TCompactProtocolT::readBinary(std::string& str) { trans_->readAll(string_buf_, size); str.assign((char*)string_buf_, size); + trans_->checkReadBytesAvailable(rsize + (uint32_t)size); + return rsize + (uint32_t)size; } @@ -735,7 +743,7 @@ uint32_t TCompactProtocolT::readVarint64(int64_t& i64) { const uint8_t* borrowed = trans_->borrow(buf, &buf_size); // Fast path. - if (borrowed != NULL) { + if (borrowed != nullptr) { while (true) { uint8_t byte = borrowed[rsize]; rsize++; @@ -821,6 +829,30 @@ TType TCompactProtocolT::getTType(int8_t type) { } } +// Return the minimum number of bytes a type will consume on the wire +template +int TCompactProtocolT::getMinSerializedSize(TType type) +{ + switch (type) + { + case T_STOP: return 0; + case T_VOID: return 0; + case T_BOOL: return sizeof(int8_t); + case T_DOUBLE: return 8; // uses fixedLongToBytes() which always writes 8 bytes + case T_BYTE: return sizeof(int8_t); + case T_I16: return sizeof(int8_t); // zigzag + case T_I32: return sizeof(int8_t); // zigzag + case T_I64: return sizeof(int8_t); // zigzag + case T_STRING: return sizeof(int8_t); // string length + case T_STRUCT: return 0; // empty struct + case T_MAP: return sizeof(int8_t); // element count + case T_SET: return sizeof(int8_t); // element count + case T_LIST: return sizeof(int8_t); // element count + default: throw TProtocolException(TProtocolException::UNKNOWN, "unrecognized type code"); + } +} + + }}} // apache::thrift::protocol #endif // _THRIFT_PROTOCOL_TCOMPACTPROTOCOL_TCC_ diff --git a/lib/cpp/src/thrift/protocol/TDebugProtocol.cpp b/lib/cpp/src/thrift/protocol/TDebugProtocol.cpp index d3c6bebf3a1..0e6d4a2aac3 100644 --- a/lib/cpp/src/thrift/protocol/TDebugProtocol.cpp +++ b/lib/cpp/src/thrift/protocol/TDebugProtocol.cpp @@ -24,7 +24,6 @@ #include #include #include -#include using std::string; diff --git a/lib/cpp/src/thrift/protocol/TDebugProtocol.h b/lib/cpp/src/thrift/protocol/TDebugProtocol.h index 301d05aa4c1..41bb0d4ec94 100644 --- a/lib/cpp/src/thrift/protocol/TDebugProtocol.h +++ b/lib/cpp/src/thrift/protocol/TDebugProtocol.h @@ -22,7 +22,7 @@ #include -#include +#include namespace apache { namespace thrift { @@ -51,7 +51,7 @@ class TDebugProtocol : public TVirtualProtocol { enum write_state_t { UNINIT, STRUCT, LIST, SET, MAP_KEY, MAP_VALUE }; public: - TDebugProtocol(stdcxx::shared_ptr trans) + TDebugProtocol(std::shared_ptr trans) : TVirtualProtocol(trans), trans_(trans.get()), string_limit_(DEFAULT_STRING_LIMIT), @@ -138,11 +138,11 @@ class TDebugProtocol : public TVirtualProtocol { */ class TDebugProtocolFactory : public TProtocolFactory { public: - TDebugProtocolFactory() {} - virtual ~TDebugProtocolFactory() {} + TDebugProtocolFactory() = default; + ~TDebugProtocolFactory() override = default; - stdcxx::shared_ptr getProtocol(stdcxx::shared_ptr trans) { - return stdcxx::shared_ptr(new TDebugProtocol(trans)); + std::shared_ptr getProtocol(std::shared_ptr trans) override { + return std::shared_ptr(new TDebugProtocol(trans)); } }; } @@ -159,8 +159,8 @@ template std::string ThriftDebugString(const ThriftStruct& ts) { using namespace apache::thrift::transport; using namespace apache::thrift::protocol; - TMemoryBuffer* buffer = new TMemoryBuffer; - stdcxx::shared_ptr trans(buffer); + auto* buffer = new TMemoryBuffer; + std::shared_ptr trans(buffer); TDebugProtocol protocol(trans); ts.write(&protocol); @@ -178,7 +178,7 @@ std::string DebugString(const std::vector& vec) { using namespace apache::thrift::transport; using namespace apache::thrift::protocol; TMemoryBuffer* buffer = new TMemoryBuffer; - stdcxx::shared_ptr trans(buffer); + std::shared_ptr trans(buffer); TDebugProtocol protocol(trans); // I am gross! diff --git a/lib/cpp/src/thrift/protocol/TEnum.h b/lib/cpp/src/thrift/protocol/TEnum.h new file mode 100644 index 00000000000..9636785e327 --- /dev/null +++ b/lib/cpp/src/thrift/protocol/TEnum.h @@ -0,0 +1,66 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#ifndef _THRIFT_ENUM_H_ +#define _THRIFT_ENUM_H_ + +namespace apache { +namespace thrift { +namespace protocol { + +/** + * 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 +}; + +}}} // apache::thrift::protocol + +#endif // #define _THRIFT_ENUM_H_ diff --git a/lib/cpp/src/thrift/protocol/THeaderProtocol.cpp b/lib/cpp/src/thrift/protocol/THeaderProtocol.cpp index 26676172f35..6242e30b854 100644 --- a/lib/cpp/src/thrift/protocol/THeaderProtocol.cpp +++ b/lib/cpp/src/thrift/protocol/THeaderProtocol.cpp @@ -26,8 +26,7 @@ #include -#include -#include +#include namespace apache { namespace thrift { @@ -42,11 +41,11 @@ void THeaderProtocol::resetProtocol() { switch (protoId_) { case T_BINARY_PROTOCOL: - proto_ = stdcxx::make_shared >(trans_); + proto_ = std::make_shared >(trans_); break; case T_COMPACT_PROTOCOL: - proto_ = stdcxx::make_shared >(trans_); + proto_ = std::make_shared >(trans_); break; default: diff --git a/lib/cpp/src/thrift/protocol/THeaderProtocol.h b/lib/cpp/src/thrift/protocol/THeaderProtocol.h index 8cd501736e3..0d5018596f7 100644 --- a/lib/cpp/src/thrift/protocol/THeaderProtocol.h +++ b/lib/cpp/src/thrift/protocol/THeaderProtocol.h @@ -25,7 +25,7 @@ #include #include -#include +#include using apache::thrift::transport::THeaderTransport; @@ -43,27 +43,27 @@ class THeaderProtocol : public TVirtualProtocol { public: void resetProtocol(); - explicit THeaderProtocol(const stdcxx::shared_ptr& trans, + explicit THeaderProtocol(const std::shared_ptr& trans, uint16_t protoId = T_COMPACT_PROTOCOL) - : TVirtualProtocol(stdcxx::shared_ptr(new THeaderTransport(trans))), - trans_(stdcxx::dynamic_pointer_cast(getTransport())), + : TVirtualProtocol(std::shared_ptr(new THeaderTransport(trans))), + trans_(std::dynamic_pointer_cast(getTransport())), protoId_(protoId) { trans_->setProtocolId(protoId); resetProtocol(); } - THeaderProtocol(const stdcxx::shared_ptr& inTrans, - const stdcxx::shared_ptr& outTrans, + THeaderProtocol(const std::shared_ptr& inTrans, + const std::shared_ptr& outTrans, uint16_t protoId = T_COMPACT_PROTOCOL) : TVirtualProtocol( - stdcxx::shared_ptr(new THeaderTransport(inTrans, outTrans))), - trans_(stdcxx::dynamic_pointer_cast(getTransport())), + std::shared_ptr(new THeaderTransport(inTrans, outTrans))), + trans_(std::dynamic_pointer_cast(getTransport())), protoId_(protoId) { trans_->setProtocolId(protoId); resetProtocol(); } - ~THeaderProtocol() {} + ~THeaderProtocol() override = default; /** * Functions to work with headers by calling into THeaderTransport @@ -182,25 +182,25 @@ class THeaderProtocol : public TVirtualProtocol { uint32_t readBinary(std::string& binary); protected: - stdcxx::shared_ptr trans_; + std::shared_ptr trans_; - stdcxx::shared_ptr proto_; + std::shared_ptr proto_; uint32_t protoId_; }; class THeaderProtocolFactory : public TProtocolFactory { public: - virtual stdcxx::shared_ptr getProtocol(stdcxx::shared_ptr trans) { - THeaderProtocol* headerProtocol + std::shared_ptr getProtocol(std::shared_ptr trans) override { + auto* headerProtocol = new THeaderProtocol(trans, trans, T_BINARY_PROTOCOL); - return stdcxx::shared_ptr(headerProtocol); + return std::shared_ptr(headerProtocol); } - virtual stdcxx::shared_ptr getProtocol( - stdcxx::shared_ptr inTrans, - stdcxx::shared_ptr outTrans) { - THeaderProtocol* headerProtocol = new THeaderProtocol(inTrans, outTrans, T_BINARY_PROTOCOL); - return stdcxx::shared_ptr(headerProtocol); + std::shared_ptr getProtocol( + std::shared_ptr inTrans, + std::shared_ptr outTrans) override { + auto* headerProtocol = new THeaderProtocol(inTrans, outTrans, T_BINARY_PROTOCOL); + return std::shared_ptr(headerProtocol); } }; } diff --git a/lib/cpp/src/thrift/protocol/TJSONProtocol.cpp b/lib/cpp/src/thrift/protocol/TJSONProtocol.cpp index 80def7ff704..6e4e8ef0db6 100644 --- a/lib/cpp/src/thrift/protocol/TJSONProtocol.cpp +++ b/lib/cpp/src/thrift/protocol/TJSONProtocol.cpp @@ -20,8 +20,6 @@ #include #include -#include -#include #include #include @@ -49,7 +47,6 @@ static const uint8_t kJSONPairSeparator = ':'; static const uint8_t kJSONElemSeparator = ','; static const uint8_t kJSONBackslash = '\\'; static const uint8_t kJSONStringDelimiter = '"'; -static const uint8_t kJSONZeroChar = '0'; static const uint8_t kJSONEscapeChar = 'u'; static const std::string kJSONEscapePrefix("\\u00"); @@ -304,9 +301,9 @@ static bool isLowSurrogate(uint16_t val) { class TJSONContext { public: - TJSONContext(){}; + TJSONContext() = default; - virtual ~TJSONContext(){}; + virtual ~TJSONContext() = default; /** * Write context data to the transport. Default is to do nothing. @@ -337,7 +334,7 @@ class JSONPairContext : public TJSONContext { public: JSONPairContext() : first_(true), colon_(true) {} - uint32_t write(TTransport& trans) { + uint32_t write(TTransport& trans) override { if (first_) { first_ = false; colon_ = true; @@ -349,7 +346,7 @@ class JSONPairContext : public TJSONContext { } } - uint32_t read(TJSONProtocol::LookaheadReader& reader) { + uint32_t read(TJSONProtocol::LookaheadReader& reader) override { if (first_) { first_ = false; colon_ = true; @@ -362,7 +359,7 @@ class JSONPairContext : public TJSONContext { } // Numbers must be turned into strings if they are the key part of a pair - virtual bool escapeNum() { return colon_; } + bool escapeNum() override { return colon_; } private: bool first_; @@ -375,7 +372,7 @@ class JSONListContext : public TJSONContext { public: JSONListContext() : first_(true) {} - uint32_t write(TTransport& trans) { + uint32_t write(TTransport& trans) override { if (first_) { first_ = false; return 0; @@ -385,7 +382,7 @@ class JSONListContext : public TJSONContext { } } - uint32_t read(TJSONProtocol::LookaheadReader& reader) { + uint32_t read(TJSONProtocol::LookaheadReader& reader) override { if (first_) { first_ = false; return 0; @@ -398,17 +395,16 @@ class JSONListContext : public TJSONContext { bool first_; }; -TJSONProtocol::TJSONProtocol(stdcxx::shared_ptr ptrans) +TJSONProtocol::TJSONProtocol(std::shared_ptr ptrans) : TVirtualProtocol(ptrans), trans_(ptrans.get()), context_(new TJSONContext()), reader_(*ptrans) { } -TJSONProtocol::~TJSONProtocol() { -} +TJSONProtocol::~TJSONProtocol() = default; -void TJSONProtocol::pushContext(stdcxx::shared_ptr c) { +void TJSONProtocol::pushContext(std::shared_ptr c) { contexts_.push(context_); context_ = c; } @@ -478,10 +474,10 @@ uint32_t TJSONProtocol::writeJSONBase64(const std::string& str) { result += 2; // For quotes trans_->write(&kJSONStringDelimiter, 1); uint8_t b[4]; - const uint8_t* bytes = (const uint8_t*)str.c_str(); + const auto* bytes = (const uint8_t*)str.c_str(); if (str.length() > (std::numeric_limits::max)()) throw TProtocolException(TProtocolException::SIZE_LIMIT); - uint32_t len = static_cast(str.length()); + auto len = static_cast(str.length()); while (len >= 3) { // Encode 3 bytes at a time base64_encode(bytes, 3, b); @@ -539,9 +535,9 @@ uint32_t TJSONProtocol::writeJSONDouble(double num) { std::string val; bool special = false; - switch (boost::math::fpclassify(num)) { + switch (std::fpclassify(num)) { case FP_INFINITE: - if (boost::math::signbit(num)) { + if (std::signbit(num)) { val = kThriftNegativeInfinity; } else { val = kThriftInfinity; @@ -576,7 +572,7 @@ uint32_t TJSONProtocol::writeJSONDouble(double num) { uint32_t TJSONProtocol::writeJSONObjectStart() { uint32_t result = context_->write(*trans_); trans_->write(&kJSONObjectStart, 1); - pushContext(stdcxx::shared_ptr(new JSONPairContext())); + pushContext(std::shared_ptr(new JSONPairContext())); return result + 1; } @@ -589,7 +585,7 @@ uint32_t TJSONProtocol::writeJSONObjectEnd() { uint32_t TJSONProtocol::writeJSONArrayStart() { uint32_t result = context_->write(*trans_); trans_->write(&kJSONArrayStart, 1); - pushContext(stdcxx::shared_ptr(new JSONListContext())); + pushContext(std::shared_ptr(new JSONListContext())); return result + 1; } @@ -799,10 +795,10 @@ uint32_t TJSONProtocol::readJSONString(std::string& str, bool skipContext) { uint32_t TJSONProtocol::readJSONBase64(std::string& str) { std::string tmp; uint32_t result = readJSONString(tmp); - uint8_t* b = (uint8_t*)tmp.c_str(); + auto* b = (uint8_t*)tmp.c_str(); if (tmp.length() > (std::numeric_limits::max)()) throw TProtocolException(TProtocolException::SIZE_LIMIT); - uint32_t len = static_cast(tmp.length()); + auto len = static_cast(tmp.length()); str.clear(); // Ignore padding if (len >= 2) { @@ -899,7 +895,7 @@ uint32_t TJSONProtocol::readJSONDouble(double& num) { } try { num = fromString(str); - } catch (std::runtime_error& e) { + } catch (const std::runtime_error&) { throw TProtocolException(TProtocolException::INVALID_DATA, "Expected numeric value; got \"" + str + "\""); } @@ -912,7 +908,7 @@ uint32_t TJSONProtocol::readJSONDouble(double& num) { result += readJSONNumericChars(str); try { num = fromString(str); - } catch (std::runtime_error& e) { + } catch (const std::runtime_error&) { throw TProtocolException(TProtocolException::INVALID_DATA, "Expected numeric value; got \"" + str + "\""); } @@ -923,7 +919,7 @@ uint32_t TJSONProtocol::readJSONDouble(double& num) { uint32_t TJSONProtocol::readJSONObjectStart() { uint32_t result = context_->read(reader_); result += readJSONSyntaxChar(kJSONObjectStart); - pushContext(stdcxx::shared_ptr(new JSONPairContext())); + pushContext(std::shared_ptr(new JSONPairContext())); return result; } @@ -936,7 +932,7 @@ uint32_t TJSONProtocol::readJSONObjectEnd() { uint32_t TJSONProtocol::readJSONArrayStart() { uint32_t result = context_->read(reader_); result += readJSONSyntaxChar(kJSONArrayStart); - pushContext(stdcxx::shared_ptr(new JSONListContext())); + pushContext(std::shared_ptr(new JSONListContext())); return result; } @@ -950,7 +946,7 @@ uint32_t TJSONProtocol::readMessageBegin(std::string& name, TMessageType& messageType, int32_t& seqid) { uint32_t result = readJSONArrayStart(); - uint64_t tmpVal = 0; + int64_t tmpVal = 0; result += readJSONInteger(tmpVal); if (tmpVal != kThriftVersion1) { throw TProtocolException(TProtocolException::BAD_VERSION, "Message contained bad version."); @@ -959,8 +955,9 @@ uint32_t TJSONProtocol::readMessageBegin(std::string& name, result += readJSONInteger(tmpVal); messageType = (TMessageType)tmpVal; result += readJSONInteger(tmpVal); - if (tmpVal > static_cast((std::numeric_limits::max)())) - throw TProtocolException(TProtocolException::SIZE_LIMIT); + if (tmpVal > (std::numeric_limits::max)() || + tmpVal < (std::numeric_limits::min)()) + throw TProtocolException(TProtocolException::INVALID_DATA, "sequence id is not int32_t"); seqid = static_cast(tmpVal); return result; } @@ -1016,6 +1013,10 @@ uint32_t TJSONProtocol::readMapBegin(TType& keyType, TType& valType, uint32_t& s throw TProtocolException(TProtocolException::SIZE_LIMIT); size = static_cast(tmpVal); result += readJSONObjectStart(); + + TMap map(keyType, valType, size); + checkReadBytesAvailable(map); + return result; } @@ -1035,6 +1036,10 @@ uint32_t TJSONProtocol::readListBegin(TType& elemType, uint32_t& size) { if (tmpVal > (std::numeric_limits::max)()) throw TProtocolException(TProtocolException::SIZE_LIMIT); size = static_cast(tmpVal); + + TList list(elemType, size); + checkReadBytesAvailable(list); + return result; } @@ -1052,6 +1057,10 @@ uint32_t TJSONProtocol::readSetBegin(TType& elemType, uint32_t& size) { if (tmpVal > (std::numeric_limits::max)()) throw TProtocolException(TProtocolException::SIZE_LIMIT); size = static_cast(tmpVal); + + TSet set(elemType, size); + checkReadBytesAvailable(set); + return result; } @@ -1066,7 +1075,7 @@ uint32_t TJSONProtocol::readBool(bool& 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) { - int16_t tmp = (int16_t)byte; + auto tmp = (int16_t)byte; uint32_t result = readJSONInteger(tmp); assert(tmp < 256); byte = (int8_t)tmp; @@ -1096,6 +1105,29 @@ uint32_t TJSONProtocol::readString(std::string& str) { uint32_t TJSONProtocol::readBinary(std::string& str) { return readJSONBase64(str); } + +// Return the minimum number of bytes a type will consume on the wire +int TJSONProtocol::getMinSerializedSize(TType type) +{ + switch (type) + { + case T_STOP: return 0; + case T_VOID: return 0; + case T_BOOL: return 1; // written as int + case T_BYTE: return 1; + case T_DOUBLE: return 1; + case T_I16: return 1; + case T_I32: return 1; + case T_I64: return 1; + case T_STRING: return 2; // empty string + case T_STRUCT: return 2; // empty struct + case T_MAP: return 2; // empty map + case T_SET: return 2; // empty set + case T_LIST: return 2; // empty list + default: throw TProtocolException(TProtocolException::UNKNOWN, "unrecognized type code"); + } +} + } } } // apache::thrift::protocol diff --git a/lib/cpp/src/thrift/protocol/TJSONProtocol.h b/lib/cpp/src/thrift/protocol/TJSONProtocol.h index 16dff561c4e..e775240ab16 100644 --- a/lib/cpp/src/thrift/protocol/TJSONProtocol.h +++ b/lib/cpp/src/thrift/protocol/TJSONProtocol.h @@ -96,12 +96,12 @@ class TJSONContext; */ class TJSONProtocol : public TVirtualProtocol { public: - TJSONProtocol(stdcxx::shared_ptr ptrans); + TJSONProtocol(std::shared_ptr ptrans); - ~TJSONProtocol(); + ~TJSONProtocol() override; private: - void pushContext(stdcxx::shared_ptr c); + void pushContext(std::shared_ptr c); void popContext(); @@ -245,10 +245,28 @@ class TJSONProtocol : public TVirtualProtocol { uint32_t readBinary(std::string& str); + int getMinSerializedSize(TType type); + + void checkReadBytesAvailable(TSet& set) + { + trans_->checkReadBytesAvailable(set.size_ * getMinSerializedSize(set.elemType_)); + } + + void checkReadBytesAvailable(TList& list) + { + trans_->checkReadBytesAvailable(list.size_ * getMinSerializedSize(list.elemType_)); + } + + void checkReadBytesAvailable(TMap& map) + { + int elmSize = getMinSerializedSize(map.keyType_) + getMinSerializedSize(map.valueType_); + trans_->checkReadBytesAvailable(map.size_ * elmSize); + } + class LookaheadReader { public: - LookaheadReader(TTransport& trans) : trans_(&trans), hasData_(false) {} + LookaheadReader(TTransport& trans) : trans_(&trans), hasData_(false), data_(0) {} uint8_t read() { if (hasData_) { @@ -276,8 +294,8 @@ class TJSONProtocol : public TVirtualProtocol { private: TTransport* trans_; - std::stack > contexts_; - stdcxx::shared_ptr context_; + std::stack > contexts_; + std::shared_ptr context_; LookaheadReader reader_; }; @@ -286,12 +304,12 @@ class TJSONProtocol : public TVirtualProtocol { */ class TJSONProtocolFactory : public TProtocolFactory { public: - TJSONProtocolFactory() {} + TJSONProtocolFactory() = default; - virtual ~TJSONProtocolFactory() {} + ~TJSONProtocolFactory() override = default; - stdcxx::shared_ptr getProtocol(stdcxx::shared_ptr trans) { - return stdcxx::shared_ptr(new TJSONProtocol(trans)); + std::shared_ptr getProtocol(std::shared_ptr trans) override { + return std::shared_ptr(new TJSONProtocol(trans)); } }; } @@ -308,8 +326,8 @@ template std::string ThriftJSONString(const ThriftStruct& ts) { using namespace apache::thrift::transport; using namespace apache::thrift::protocol; - TMemoryBuffer* buffer = new TMemoryBuffer; - stdcxx::shared_ptr trans(buffer); + auto* buffer = new TMemoryBuffer; + std::shared_ptr trans(buffer); TJSONProtocol protocol(trans); ts.write(&protocol); diff --git a/lib/cocoa/src/TSharedProcessorFactory.m b/lib/cpp/src/thrift/protocol/TList.h similarity index 61% rename from lib/cocoa/src/TSharedProcessorFactory.m rename to lib/cpp/src/thrift/protocol/TList.h index 3d55f479319..bf2c1f9de6c 100644 --- a/lib/cocoa/src/TSharedProcessorFactory.m +++ b/lib/cpp/src/thrift/protocol/TList.h @@ -17,33 +17,39 @@ * under the License. */ +#ifndef _THRIFT_TLIST_H_ +#define _THRIFT_TLIST_H_ -#import "TSharedProcessorFactory.h" +#include +namespace apache { +namespace thrift { +namespace protocol { -@interface TSharedProcessorFactory () +// using namespace apache::thrift::protocol; -@property(strong, nonatomic) id sharedProcessor; - -@end - - -@implementation TSharedProcessorFactory +/** + * Helper class that encapsulates list metadata. + * + */ +class TList { +public: + TList() : elemType_(T_STOP), + size_(0) { + } --(id) initWithSharedProcessor:(id)sharedProcessor -{ - self = [super init]; - if (self) { - _sharedProcessor = sharedProcessor; + TList(TType t = T_STOP, int s = 0) + : elemType_(t), + size_(s) { + } - return self; + TType elemType_; + int size_; +}; } - --(id) processorForTransport:(id)transport -{ - return _sharedProcessor; } +} // apache::thrift::protocol -@end +#endif // #ifndef _THRIFT_TLIST_H_ diff --git a/compiler/cpp/test/plugin/cpp_plugin.cc b/lib/cpp/src/thrift/protocol/TMap.h similarity index 61% rename from compiler/cpp/test/plugin/cpp_plugin.cc rename to lib/cpp/src/thrift/protocol/TMap.h index 6cc19f2a3fc..b52ea8faf3b 100644 --- a/compiler/cpp/test/plugin/cpp_plugin.cc +++ b/lib/cpp/src/thrift/protocol/TMap.h @@ -17,27 +17,43 @@ * under the License. */ -#include "thrift/plugin/plugin.h" -#include "thrift/generate/t_generator.h" +#ifndef _THRIFT_TMAP_H_ +#define _THRIFT_TMAP_H_ + +#include namespace apache { namespace thrift { -namespace plugin { - -class MyCppGenerator : public GeneratorPlugin { - virtual int generate(::t_program* program, - const std::map& parsed_options) { - t_generator* gen = t_generator_registry::get_generator(program, "cpp", parsed_options, ""); - gen->generate_program(); - delete gen; - return 0; +namespace protocol { + +using namespace apache::thrift::protocol; + +/** + * Helper class that encapsulates map metadata. + * + */ +class TMap { +public: + TMap() + : keyType_(T_STOP), + valueType_(T_STOP), + size_(0) { + } + + TMap(TType k, TType v, int s) + : keyType_(k), + valueType_(v), + size_(s) { + + } + + TType keyType_; + TType valueType_; + int size_; }; } } -} +} // apache::thrift::protocol -int main(int argc, char* argv[]) { - apache::thrift::plugin::MyCppGenerator p; - return p.exec(argc, argv); -} +#endif // #ifndef _THRIFT_TMAP_H_ diff --git a/lib/cpp/src/thrift/protocol/TMultiplexedProtocol.h b/lib/cpp/src/thrift/protocol/TMultiplexedProtocol.h index dd7e88ffc80..0dc9605840c 100644 --- a/lib/cpp/src/thrift/protocol/TMultiplexedProtocol.h +++ b/lib/cpp/src/thrift/protocol/TMultiplexedProtocol.h @@ -25,7 +25,7 @@ namespace apache { namespace thrift { namespace protocol { -using stdcxx::shared_ptr; +using std::shared_ptr; /** * TMultiplexedProtocol is a protocol-independent concrete decorator @@ -69,7 +69,7 @@ class TMultiplexedProtocol : public TProtocolDecorator { */ TMultiplexedProtocol(shared_ptr _protocol, const std::string& _serviceName) : TProtocolDecorator(_protocol), serviceName(_serviceName), separator(":") {} - virtual ~TMultiplexedProtocol() {} + ~TMultiplexedProtocol() override = default; /** * Prepends the service name to the function name, separated by TMultiplexedProtocol::SEPARATOR. @@ -82,7 +82,7 @@ class TMultiplexedProtocol : public TProtocolDecorator { */ uint32_t writeMessageBegin_virt(const std::string& _name, const TMessageType _type, - const int32_t _seqid); + const int32_t _seqid) override; private: const std::string serviceName; diff --git a/lib/cpp/src/thrift/protocol/TProtocol.cpp b/lib/cpp/src/thrift/protocol/TProtocol.cpp index c378aca6143..b460455ffbe 100644 --- a/lib/cpp/src/thrift/protocol/TProtocol.cpp +++ b/lib/cpp/src/thrift/protocol/TProtocol.cpp @@ -23,11 +23,11 @@ namespace apache { namespace thrift { namespace protocol { -TProtocol::~TProtocol() {} +TProtocol::~TProtocol() = default; uint32_t TProtocol::skip_virt(TType type) { return ::apache::thrift::protocol::skip(*this, type); } -TProtocolFactory::~TProtocolFactory() {} +TProtocolFactory::~TProtocolFactory() = default; }}} // apache::thrift::protocol diff --git a/lib/cpp/src/thrift/protocol/TProtocol.h b/lib/cpp/src/thrift/protocol/TProtocol.h index aa5beea92e9..867ceb079e7 100644 --- a/lib/cpp/src/thrift/protocol/TProtocol.h +++ b/lib/cpp/src/thrift/protocol/TProtocol.h @@ -27,9 +27,12 @@ #include #include +#include +#include +#include +#include -#include -#include +#include #ifdef HAVE_NETINET_IN_H #include @@ -48,7 +51,7 @@ // http://cellperformance.beyond3d.com/articles/2006/06/understanding-strict-aliasing.html template static inline To bitwise_cast(From from) { - BOOST_STATIC_ASSERT(sizeof(From) == sizeof(To)); + static_assert(sizeof(From) == sizeof(To), "sizeof(From) == sizeof(To)"); // BAD!!! These are all broken with -O2. //return *reinterpret_cast(&from); // BAD!!! @@ -89,15 +92,18 @@ static inline To bitwise_cast(From from) { # define __THRIFT_LITTLE_ENDIAN LITTLE_ENDIAN # define __THRIFT_BIG_ENDIAN BIG_ENDIAN # else -# include -# include -# define __THRIFT_BYTE_ORDER BOOST_BYTE_ORDER +# include +# 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 -# define __THRIFT_LITTLE_ENDIAN __THRIFT_BYTE_ORDER -# define __THRIFT_BIG_ENDIAN 0 # else -# define __THRIFT_LITTLE_ENDIAN 0 -# define __THRIFT_BIG_ENDIAN __THRIFT_BYTE_ORDER # endif # endif #endif @@ -168,45 +174,6 @@ 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 @@ -550,12 +517,12 @@ class TProtocol { } virtual uint32_t skip_virt(TType type); - inline stdcxx::shared_ptr getTransport() { return ptrans_; } + inline std::shared_ptr getTransport() { return ptrans_; } // TODO: remove these two calls, they are for backwards // compatibility - inline stdcxx::shared_ptr getInputTransport() { return ptrans_; } - inline stdcxx::shared_ptr getOutputTransport() { return ptrans_; } + inline std::shared_ptr getInputTransport() { return ptrans_; } + inline std::shared_ptr getOutputTransport() { return ptrans_; } // input and output recursion depth are kept separate so that one protocol // can be used concurrently for both input and output. @@ -576,15 +543,38 @@ class TProtocol { uint32_t getRecursionLimit() const {return recursion_limit_;} void setRecurisionLimit(uint32_t depth) {recursion_limit_ = depth;} + // Returns the minimum amount of bytes needed to store the smallest possible instance of TType. + virtual int getMinSerializedSize(TType type) { + THRIFT_UNUSED_VARIABLE(type); + return 0; + } + protected: - TProtocol(stdcxx::shared_ptr ptrans) - : ptrans_(ptrans), input_recursion_depth_(0), output_recursion_depth_(0), recursion_limit_(DEFAULT_RECURSION_LIMIT) + TProtocol(std::shared_ptr ptrans) + : ptrans_(ptrans), input_recursion_depth_(0), output_recursion_depth_(0), + recursion_limit_(ptrans->getConfiguration()->getRecursionLimit()) {} - stdcxx::shared_ptr ptrans_; + virtual void checkReadBytesAvailable(TSet& set) + { + ptrans_->checkReadBytesAvailable(set.size_ * getMinSerializedSize(set.elemType_)); + } + + virtual void checkReadBytesAvailable(TList& list) + { + ptrans_->checkReadBytesAvailable(list.size_ * getMinSerializedSize(list.elemType_)); + } + + virtual void checkReadBytesAvailable(TMap& map) + { + int elmSize = getMinSerializedSize(map.keyType_) + getMinSerializedSize(map.valueType_); + ptrans_->checkReadBytesAvailable(map.size_ * elmSize); + } + + std::shared_ptr ptrans_; private: - TProtocol() {} + TProtocol() = default; uint32_t input_recursion_depth_; uint32_t output_recursion_depth_; uint32_t recursion_limit_; @@ -595,13 +585,13 @@ class TProtocol { */ class TProtocolFactory { public: - TProtocolFactory() {} + TProtocolFactory() = default; virtual ~TProtocolFactory(); - virtual stdcxx::shared_ptr getProtocol(stdcxx::shared_ptr trans) = 0; - virtual stdcxx::shared_ptr getProtocol(stdcxx::shared_ptr inTrans, - stdcxx::shared_ptr outTrans) { + virtual std::shared_ptr getProtocol(std::shared_ptr trans) = 0; + virtual std::shared_ptr getProtocol(std::shared_ptr inTrans, + std::shared_ptr outTrans) { (void)outTrans; return getProtocol(inTrans); } @@ -747,16 +737,12 @@ uint32_t skip(Protocol_& prot, TType type) { result += prot.readListEnd(); return result; } - case T_STOP: - case T_VOID: - case T_U64: - case T_UTF8: - case T_UTF16: - break; default: - throw TProtocolException(TProtocolException::INVALID_DATA); + break; } - return 0; + + throw TProtocolException(TProtocolException::INVALID_DATA, + "invalid TType"); } }}} // apache::thrift::protocol diff --git a/lib/cpp/src/thrift/protocol/TProtocolDecorator.h b/lib/cpp/src/thrift/protocol/TProtocolDecorator.h index a353b796886..5258159f149 100644 --- a/lib/cpp/src/thrift/protocol/TProtocolDecorator.h +++ b/lib/cpp/src/thrift/protocol/TProtocolDecorator.h @@ -21,12 +21,12 @@ #define THRIFT_TPROTOCOLDECORATOR_H_ 1 #include -#include +#include namespace apache { namespace thrift { namespace protocol { -using stdcxx::shared_ptr; +using std::shared_ptr; /** * TProtocolDecorator forwards all requests to an enclosed @@ -39,107 +39,107 @@ using stdcxx::shared_ptr; */ class TProtocolDecorator : public TProtocol { public: - virtual ~TProtocolDecorator() {} + ~TProtocolDecorator() override = default; // Desc: Initializes the protocol decorator object. TProtocolDecorator(shared_ptr proto) : TProtocol(proto->getTransport()), protocol(proto) {} - virtual uint32_t writeMessageBegin_virt(const std::string& name, + uint32_t writeMessageBegin_virt(const std::string& name, const TMessageType messageType, - const int32_t seqid) { + const int32_t seqid) override { return protocol->writeMessageBegin(name, messageType, seqid); } - virtual uint32_t writeMessageEnd_virt() { return protocol->writeMessageEnd(); } - virtual uint32_t writeStructBegin_virt(const char* name) { + uint32_t writeMessageEnd_virt() override { return protocol->writeMessageEnd(); } + uint32_t writeStructBegin_virt(const char* name) override { return protocol->writeStructBegin(name); } - virtual uint32_t writeStructEnd_virt() { return protocol->writeStructEnd(); } + uint32_t writeStructEnd_virt() override { return protocol->writeStructEnd(); } - virtual uint32_t writeFieldBegin_virt(const char* name, + uint32_t writeFieldBegin_virt(const char* name, const TType fieldType, - const int16_t fieldId) { + const int16_t fieldId) override { return protocol->writeFieldBegin(name, fieldType, fieldId); } - virtual uint32_t writeFieldEnd_virt() { return protocol->writeFieldEnd(); } - virtual uint32_t writeFieldStop_virt() { return protocol->writeFieldStop(); } + uint32_t writeFieldEnd_virt() override { return protocol->writeFieldEnd(); } + uint32_t writeFieldStop_virt() override { return protocol->writeFieldStop(); } - virtual uint32_t writeMapBegin_virt(const TType keyType, + uint32_t writeMapBegin_virt(const TType keyType, const TType valType, - const uint32_t size) { + const uint32_t size) override { return protocol->writeMapBegin(keyType, valType, size); } - virtual uint32_t writeMapEnd_virt() { return protocol->writeMapEnd(); } + uint32_t writeMapEnd_virt() override { return protocol->writeMapEnd(); } - virtual uint32_t writeListBegin_virt(const TType elemType, const uint32_t size) { + uint32_t writeListBegin_virt(const TType elemType, const uint32_t size) override { return protocol->writeListBegin(elemType, size); } - virtual uint32_t writeListEnd_virt() { return protocol->writeListEnd(); } + uint32_t writeListEnd_virt() override { return protocol->writeListEnd(); } - virtual uint32_t writeSetBegin_virt(const TType elemType, const uint32_t size) { + uint32_t writeSetBegin_virt(const TType elemType, const uint32_t size) override { return protocol->writeSetBegin(elemType, size); } - virtual uint32_t writeSetEnd_virt() { return protocol->writeSetEnd(); } + uint32_t writeSetEnd_virt() override { return protocol->writeSetEnd(); } - virtual uint32_t writeBool_virt(const bool value) { return protocol->writeBool(value); } - virtual uint32_t writeByte_virt(const int8_t byte) { return protocol->writeByte(byte); } - virtual uint32_t writeI16_virt(const int16_t i16) { return protocol->writeI16(i16); } - virtual uint32_t writeI32_virt(const int32_t i32) { return protocol->writeI32(i32); } - virtual uint32_t writeI64_virt(const int64_t i64) { return protocol->writeI64(i64); } + 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); } - virtual uint32_t writeDouble_virt(const double dub) { return protocol->writeDouble(dub); } - virtual uint32_t writeString_virt(const std::string& str) { return protocol->writeString(str); } - virtual uint32_t writeBinary_virt(const std::string& str) { return protocol->writeBinary(str); } + 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); } - virtual uint32_t readMessageBegin_virt(std::string& name, + uint32_t readMessageBegin_virt(std::string& name, TMessageType& messageType, - int32_t& seqid) { + int32_t& seqid) override { return protocol->readMessageBegin(name, messageType, seqid); } - virtual uint32_t readMessageEnd_virt() { return protocol->readMessageEnd(); } + uint32_t readMessageEnd_virt() override { return protocol->readMessageEnd(); } - virtual uint32_t readStructBegin_virt(std::string& name) { + uint32_t readStructBegin_virt(std::string& name) override { return protocol->readStructBegin(name); } - virtual uint32_t readStructEnd_virt() { return protocol->readStructEnd(); } + uint32_t readStructEnd_virt() override { return protocol->readStructEnd(); } - virtual uint32_t readFieldBegin_virt(std::string& name, TType& fieldType, int16_t& fieldId) { + uint32_t readFieldBegin_virt(std::string& name, TType& fieldType, int16_t& fieldId) override { return protocol->readFieldBegin(name, fieldType, fieldId); } - virtual uint32_t readFieldEnd_virt() { return protocol->readFieldEnd(); } + uint32_t readFieldEnd_virt() override { return protocol->readFieldEnd(); } - virtual uint32_t readMapBegin_virt(TType& keyType, TType& valType, uint32_t& size) { + uint32_t readMapBegin_virt(TType& keyType, TType& valType, uint32_t& size) override { return protocol->readMapBegin(keyType, valType, size); } - virtual uint32_t readMapEnd_virt() { return protocol->readMapEnd(); } + uint32_t readMapEnd_virt() override { return protocol->readMapEnd(); } - virtual uint32_t readListBegin_virt(TType& elemType, uint32_t& size) { + uint32_t readListBegin_virt(TType& elemType, uint32_t& size) override { return protocol->readListBegin(elemType, size); } - virtual uint32_t readListEnd_virt() { return protocol->readListEnd(); } + uint32_t readListEnd_virt() override { return protocol->readListEnd(); } - virtual uint32_t readSetBegin_virt(TType& elemType, uint32_t& size) { + uint32_t readSetBegin_virt(TType& elemType, uint32_t& size) override { return protocol->readSetBegin(elemType, size); } - virtual uint32_t readSetEnd_virt() { return protocol->readSetEnd(); } + uint32_t readSetEnd_virt() override { return protocol->readSetEnd(); } - virtual uint32_t readBool_virt(bool& value) { return protocol->readBool(value); } - virtual uint32_t readBool_virt(std::vector::reference value) { + uint32_t readBool_virt(bool& value) override { return protocol->readBool(value); } + uint32_t readBool_virt(std::vector::reference value) override { return protocol->readBool(value); } - virtual uint32_t readByte_virt(int8_t& byte) { return protocol->readByte(byte); } + uint32_t readByte_virt(int8_t& byte) override { return protocol->readByte(byte); } - virtual uint32_t readI16_virt(int16_t& i16) { return protocol->readI16(i16); } - virtual uint32_t readI32_virt(int32_t& i32) { return protocol->readI32(i32); } - virtual uint32_t readI64_virt(int64_t& i64) { return protocol->readI64(i64); } + 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); } - virtual uint32_t readDouble_virt(double& dub) { return protocol->readDouble(dub); } + uint32_t readDouble_virt(double& dub) override { return protocol->readDouble(dub); } - virtual uint32_t readString_virt(std::string& str) { return protocol->readString(str); } - virtual uint32_t readBinary_virt(std::string& str) { return protocol->readBinary(str); } + 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 protocol; diff --git a/lib/cpp/src/thrift/protocol/TProtocolException.h b/lib/cpp/src/thrift/protocol/TProtocolException.h index 6e536b42956..4ace9046e6a 100644 --- a/lib/cpp/src/thrift/protocol/TProtocolException.h +++ b/lib/cpp/src/thrift/protocol/TProtocolException.h @@ -59,7 +59,7 @@ class TProtocolException : public apache::thrift::TException { TProtocolException(TProtocolExceptionType type, const std::string& message) : apache::thrift::TException(message), type_(type) {} - virtual ~TProtocolException() throw() {} + ~TProtocolException() noexcept override = default; /** * Returns an error code that provides information about the type of error @@ -69,7 +69,7 @@ class TProtocolException : public apache::thrift::TException { */ TProtocolExceptionType getType() const { return type_; } - virtual const char* what() const throw() { + const char* what() const noexcept override { if (message_.empty()) { switch (type_) { case UNKNOWN: @@ -84,6 +84,8 @@ class TProtocolException : public apache::thrift::TException { 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)"; } diff --git a/lib/cpp/src/thrift/protocol/TProtocolTap.h b/lib/cpp/src/thrift/protocol/TProtocolTap.h index 176d4fdc041..d000ba61a3b 100644 --- a/lib/cpp/src/thrift/protocol/TProtocolTap.h +++ b/lib/cpp/src/thrift/protocol/TProtocolTap.h @@ -36,7 +36,7 @@ using apache::thrift::transport::TTransport; */ class TProtocolTap : public TVirtualProtocol { public: - TProtocolTap(stdcxx::shared_ptr source, stdcxx::shared_ptr sink) + TProtocolTap(std::shared_ptr source, std::shared_ptr sink) : TVirtualProtocol(source->getTransport()), source_(source), sink_(sink) {} uint32_t readMessageBegin(std::string& name, TMessageType& messageType, int32_t& seqid) { @@ -167,8 +167,8 @@ class TProtocolTap : public TVirtualProtocol { } private: - stdcxx::shared_ptr source_; - stdcxx::shared_ptr sink_; + std::shared_ptr source_; + std::shared_ptr sink_; }; } } diff --git a/lib/cpp/src/thrift/protocol/TSet.h b/lib/cpp/src/thrift/protocol/TSet.h new file mode 100644 index 00000000000..3a4718cdce4 --- /dev/null +++ b/lib/cpp/src/thrift/protocol/TSet.h @@ -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. + */ + +#ifndef _THRIFT_TSET_H_ +#define _THRIFT_TSET_H_ + +#include +#include + +namespace apache { +namespace thrift { +namespace protocol { + +using namespace apache::thrift::protocol; + +/** + * Helper class that encapsulates set metadata. + * + */ +class TSet { +public: + TSet() : elemType_(T_STOP), size_(0) { + + } + + TSet(TType t, int s) + : elemType_(t), + size_(s) { + + } + + TSet(TList list) + : elemType_(list.elemType_), + size_(list.size_) { + + } + + TType elemType_; + int size_; +}; +} +} +} // apache::thrift::protocol + +#endif // #ifndef _THRIFT_TSET_H_ diff --git a/lib/cpp/src/thrift/protocol/TVirtualProtocol.h b/lib/cpp/src/thrift/protocol/TVirtualProtocol.h index 628b494a485..b7fe929af20 100644 --- a/lib/cpp/src/thrift/protocol/TVirtualProtocol.h +++ b/lib/cpp/src/thrift/protocol/TVirtualProtocol.h @@ -301,7 +301,7 @@ class TProtocolDefaults : public TProtocol { uint32_t skip(TType type) { return ::apache::thrift::protocol::skip(*this, type); } protected: - TProtocolDefaults(stdcxx::shared_ptr ptrans) : TProtocol(ptrans) {} + TProtocolDefaults(std::shared_ptr ptrans) : TProtocol(ptrans) {} }; /** @@ -315,81 +315,81 @@ class TVirtualProtocol : public Super_ { * Writing functions. */ - virtual uint32_t writeMessageBegin_virt(const std::string& name, + uint32_t writeMessageBegin_virt(const std::string& name, const TMessageType messageType, - const int32_t seqid) { + const int32_t seqid) override { return static_cast(this)->writeMessageBegin(name, messageType, seqid); } - virtual uint32_t writeMessageEnd_virt() { + uint32_t writeMessageEnd_virt() override { return static_cast(this)->writeMessageEnd(); } - virtual uint32_t writeStructBegin_virt(const char* name) { + uint32_t writeStructBegin_virt(const char* name) override { return static_cast(this)->writeStructBegin(name); } - virtual uint32_t writeStructEnd_virt() { return static_cast(this)->writeStructEnd(); } + uint32_t writeStructEnd_virt() override { return static_cast(this)->writeStructEnd(); } - virtual uint32_t writeFieldBegin_virt(const char* name, + uint32_t writeFieldBegin_virt(const char* name, const TType fieldType, - const int16_t fieldId) { + const int16_t fieldId) override { return static_cast(this)->writeFieldBegin(name, fieldType, fieldId); } - virtual uint32_t writeFieldEnd_virt() { return static_cast(this)->writeFieldEnd(); } + uint32_t writeFieldEnd_virt() override { return static_cast(this)->writeFieldEnd(); } - virtual uint32_t writeFieldStop_virt() { return static_cast(this)->writeFieldStop(); } + uint32_t writeFieldStop_virt() override { return static_cast(this)->writeFieldStop(); } - virtual uint32_t writeMapBegin_virt(const TType keyType, + uint32_t writeMapBegin_virt(const TType keyType, const TType valType, - const uint32_t size) { + const uint32_t size) override { return static_cast(this)->writeMapBegin(keyType, valType, size); } - virtual uint32_t writeMapEnd_virt() { return static_cast(this)->writeMapEnd(); } + uint32_t writeMapEnd_virt() override { return static_cast(this)->writeMapEnd(); } - virtual uint32_t writeListBegin_virt(const TType elemType, const uint32_t size) { + uint32_t writeListBegin_virt(const TType elemType, const uint32_t size) override { return static_cast(this)->writeListBegin(elemType, size); } - virtual uint32_t writeListEnd_virt() { return static_cast(this)->writeListEnd(); } + uint32_t writeListEnd_virt() override { return static_cast(this)->writeListEnd(); } - virtual uint32_t writeSetBegin_virt(const TType elemType, const uint32_t size) { + uint32_t writeSetBegin_virt(const TType elemType, const uint32_t size) override { return static_cast(this)->writeSetBegin(elemType, size); } - virtual uint32_t writeSetEnd_virt() { return static_cast(this)->writeSetEnd(); } + uint32_t writeSetEnd_virt() override { return static_cast(this)->writeSetEnd(); } - virtual uint32_t writeBool_virt(const bool value) { + uint32_t writeBool_virt(const bool value) override { return static_cast(this)->writeBool(value); } - virtual uint32_t writeByte_virt(const int8_t byte) { + uint32_t writeByte_virt(const int8_t byte) override { return static_cast(this)->writeByte(byte); } - virtual uint32_t writeI16_virt(const int16_t i16) { + uint32_t writeI16_virt(const int16_t i16) override { return static_cast(this)->writeI16(i16); } - virtual uint32_t writeI32_virt(const int32_t i32) { + uint32_t writeI32_virt(const int32_t i32) override { return static_cast(this)->writeI32(i32); } - virtual uint32_t writeI64_virt(const int64_t i64) { + uint32_t writeI64_virt(const int64_t i64) override { return static_cast(this)->writeI64(i64); } - virtual uint32_t writeDouble_virt(const double dub) { + uint32_t writeDouble_virt(const double dub) override { return static_cast(this)->writeDouble(dub); } - virtual uint32_t writeString_virt(const std::string& str) { + uint32_t writeString_virt(const std::string& str) override { return static_cast(this)->writeString(str); } - virtual uint32_t writeBinary_virt(const std::string& str) { + uint32_t writeBinary_virt(const std::string& str) override { return static_cast(this)->writeBinary(str); } @@ -397,81 +397,81 @@ class TVirtualProtocol : public Super_ { * Reading functions */ - virtual uint32_t readMessageBegin_virt(std::string& name, + uint32_t readMessageBegin_virt(std::string& name, TMessageType& messageType, - int32_t& seqid) { + int32_t& seqid) override { return static_cast(this)->readMessageBegin(name, messageType, seqid); } - virtual uint32_t readMessageEnd_virt() { return static_cast(this)->readMessageEnd(); } + uint32_t readMessageEnd_virt() override { return static_cast(this)->readMessageEnd(); } - virtual uint32_t readStructBegin_virt(std::string& name) { + uint32_t readStructBegin_virt(std::string& name) override { return static_cast(this)->readStructBegin(name); } - virtual uint32_t readStructEnd_virt() { return static_cast(this)->readStructEnd(); } + uint32_t readStructEnd_virt() override { return static_cast(this)->readStructEnd(); } - virtual uint32_t readFieldBegin_virt(std::string& name, TType& fieldType, int16_t& fieldId) { + uint32_t readFieldBegin_virt(std::string& name, TType& fieldType, int16_t& fieldId) override { return static_cast(this)->readFieldBegin(name, fieldType, fieldId); } - virtual uint32_t readFieldEnd_virt() { return static_cast(this)->readFieldEnd(); } + uint32_t readFieldEnd_virt() override { return static_cast(this)->readFieldEnd(); } - virtual uint32_t readMapBegin_virt(TType& keyType, TType& valType, uint32_t& size) { + uint32_t readMapBegin_virt(TType& keyType, TType& valType, uint32_t& size) override { return static_cast(this)->readMapBegin(keyType, valType, size); } - virtual uint32_t readMapEnd_virt() { return static_cast(this)->readMapEnd(); } + uint32_t readMapEnd_virt() override { return static_cast(this)->readMapEnd(); } - virtual uint32_t readListBegin_virt(TType& elemType, uint32_t& size) { + uint32_t readListBegin_virt(TType& elemType, uint32_t& size) override { return static_cast(this)->readListBegin(elemType, size); } - virtual uint32_t readListEnd_virt() { return static_cast(this)->readListEnd(); } + uint32_t readListEnd_virt() override { return static_cast(this)->readListEnd(); } - virtual uint32_t readSetBegin_virt(TType& elemType, uint32_t& size) { + uint32_t readSetBegin_virt(TType& elemType, uint32_t& size) override { return static_cast(this)->readSetBegin(elemType, size); } - virtual uint32_t readSetEnd_virt() { return static_cast(this)->readSetEnd(); } + uint32_t readSetEnd_virt() override { return static_cast(this)->readSetEnd(); } - virtual uint32_t readBool_virt(bool& value) { + uint32_t readBool_virt(bool& value) override { return static_cast(this)->readBool(value); } - virtual uint32_t readBool_virt(std::vector::reference value) { + uint32_t readBool_virt(std::vector::reference value) override { return static_cast(this)->readBool(value); } - virtual uint32_t readByte_virt(int8_t& byte) { + uint32_t readByte_virt(int8_t& byte) override { return static_cast(this)->readByte(byte); } - virtual uint32_t readI16_virt(int16_t& i16) { + uint32_t readI16_virt(int16_t& i16) override { return static_cast(this)->readI16(i16); } - virtual uint32_t readI32_virt(int32_t& i32) { + uint32_t readI32_virt(int32_t& i32) override { return static_cast(this)->readI32(i32); } - virtual uint32_t readI64_virt(int64_t& i64) { + uint32_t readI64_virt(int64_t& i64) override { return static_cast(this)->readI64(i64); } - virtual uint32_t readDouble_virt(double& dub) { + uint32_t readDouble_virt(double& dub) override { return static_cast(this)->readDouble(dub); } - virtual uint32_t readString_virt(std::string& str) { + uint32_t readString_virt(std::string& str) override { return static_cast(this)->readString(str); } - virtual uint32_t readBinary_virt(std::string& str) { + uint32_t readBinary_virt(std::string& str) override { return static_cast(this)->readBinary(str); } - virtual uint32_t skip_virt(TType type) { return static_cast(this)->skip(type); } + uint32_t skip_virt(TType type) override { return static_cast(this)->skip(type); } /* * Provide a default skip() implementation that uses non-virtual read @@ -484,7 +484,7 @@ class TVirtualProtocol : public Super_ { * correct parent implementation, if desired. */ uint32_t skip(TType type) { - Protocol_* const prot = static_cast(this); + auto* const prot = static_cast(this); return ::apache::thrift::protocol::skip(*prot, type); } @@ -504,7 +504,7 @@ class TVirtualProtocol : public Super_ { using Super_::readBool; // so we don't hide readBool(bool&) protected: - TVirtualProtocol(stdcxx::shared_ptr ptrans) : Super_(ptrans) {} + TVirtualProtocol(std::shared_ptr ptrans) : Super_(ptrans) {} }; } } diff --git a/lib/cpp/src/thrift/qt/TQIODeviceTransport.cpp b/lib/cpp/src/thrift/qt/TQIODeviceTransport.cpp index 0e46f11fa53..1537fc60d92 100644 --- a/lib/cpp/src/thrift/qt/TQIODeviceTransport.cpp +++ b/lib/cpp/src/thrift/qt/TQIODeviceTransport.cpp @@ -23,12 +23,12 @@ #include #include -#include +#include namespace apache { namespace thrift { -using stdcxx::shared_ptr; +using std::shared_ptr; namespace transport { @@ -46,7 +46,7 @@ void TQIODeviceTransport::open() { } } -bool TQIODeviceTransport::isOpen() { +bool TQIODeviceTransport::isOpen() const { return dev_->isOpen(); } @@ -157,7 +157,7 @@ void TQIODeviceTransport::flush() { uint8_t* TQIODeviceTransport::borrow(uint8_t* buf, uint32_t* len) { (void)buf; (void)len; - return NULL; + return nullptr; } void TQIODeviceTransport::consume(uint32_t len) { diff --git a/lib/cpp/src/thrift/qt/TQIODeviceTransport.h b/lib/cpp/src/thrift/qt/TQIODeviceTransport.h index 9087f2c41c5..a3b511def0c 100644 --- a/lib/cpp/src/thrift/qt/TQIODeviceTransport.h +++ b/lib/cpp/src/thrift/qt/TQIODeviceTransport.h @@ -20,7 +20,7 @@ #ifndef _THRIFT_ASYNC_TQIODEVICE_TRANSPORT_H_ #define _THRIFT_ASYNC_TQIODEVICE_TRANSPORT_H_ 1 -#include +#include #include @@ -36,13 +36,13 @@ namespace transport { class TQIODeviceTransport : public apache::thrift::transport::TVirtualTransport { public: - explicit TQIODeviceTransport(stdcxx::shared_ptr dev); - virtual ~TQIODeviceTransport(); + explicit TQIODeviceTransport(std::shared_ptr dev); + ~TQIODeviceTransport() override; - void open(); - bool isOpen(); - bool peek(); - void close(); + 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); @@ -50,7 +50,7 @@ class TQIODeviceTransport void write(const uint8_t* buf, uint32_t len); uint32_t write_partial(const uint8_t* buf, uint32_t len); - void flush(); + void flush() override; uint8_t* borrow(uint8_t* buf, uint32_t* len); void consume(uint32_t len); @@ -59,7 +59,7 @@ class TQIODeviceTransport TQIODeviceTransport(const TQIODeviceTransport&); TQIODeviceTransport& operator=(const TQIODeviceTransport&); - stdcxx::shared_ptr dev_; + std::shared_ptr dev_; }; } } diff --git a/lib/cpp/src/thrift/qt/TQTcpServer.cpp b/lib/cpp/src/thrift/qt/TQTcpServer.cpp index c4669d78265..04044823e56 100644 --- a/lib/cpp/src/thrift/qt/TQTcpServer.cpp +++ b/lib/cpp/src/thrift/qt/TQTcpServer.cpp @@ -17,14 +17,15 @@ * under the License. */ +#include +#include + #include #include #include #include -#include - #include #include @@ -33,10 +34,10 @@ using apache::thrift::protocol::TProtocolFactory; using apache::thrift::transport::TTransport; using apache::thrift::transport::TTransportException; using apache::thrift::transport::TQIODeviceTransport; -using apache::thrift::stdcxx::bind; -using apache::thrift::stdcxx::function; -using apache::thrift::stdcxx::placeholders::_1; -using apache::thrift::stdcxx::shared_ptr; +using std::bind; +using std::function; +using std::placeholders::_1; +using std::shared_ptr; QT_USE_NAMESPACE @@ -66,8 +67,7 @@ TQTcpServer::TQTcpServer(shared_ptr server, connect(server.get(), SIGNAL(newConnection()), SLOT(processIncoming())); } -TQTcpServer::~TQTcpServer() { -} +TQTcpServer::~TQTcpServer() = default; void TQTcpServer::processIncoming() { while (server_->hasPendingConnections()) { @@ -90,7 +90,7 @@ void TQTcpServer::processIncoming() { } ctxMap_[connection.get()] - = shared_ptr(new ConnectionContext(connection, transport, iprot, oprot)); + = std::make_shared(connection, transport, iprot, oprot); connect(connection.get(), SIGNAL(readyRead()), SLOT(beginDecode())); @@ -99,7 +99,7 @@ void TQTcpServer::processIncoming() { } void TQTcpServer::beginDecode() { - QTcpSocket* connection(qobject_cast(sender())); + auto* connection(qobject_cast(sender())); Q_ASSERT(connection); if (ctxMap_.find(connection) == ctxMap_.end()) { @@ -124,7 +124,7 @@ void TQTcpServer::beginDecode() { } void TQTcpServer::socketClosed() { - QTcpSocket* connection(qobject_cast(sender())); + auto* connection(qobject_cast(sender())); Q_ASSERT(connection); scheduleDeleteConnectionContext(connection); } diff --git a/lib/cpp/src/thrift/qt/TQTcpServer.h b/lib/cpp/src/thrift/qt/TQTcpServer.h index 0d32afa0a77..25994ab8d01 100644 --- a/lib/cpp/src/thrift/qt/TQTcpServer.h +++ b/lib/cpp/src/thrift/qt/TQTcpServer.h @@ -23,7 +23,7 @@ #include #include -#include +#include namespace apache { namespace thrift { @@ -47,11 +47,11 @@ class TAsyncProcessor; class TQTcpServer : public QObject { Q_OBJECT public: - TQTcpServer(stdcxx::shared_ptr server, - stdcxx::shared_ptr processor, - stdcxx::shared_ptr protocolFactory, - QObject* parent = NULL); - virtual ~TQTcpServer(); + TQTcpServer(std::shared_ptr server, + std::shared_ptr processor, + std::shared_ptr protocolFactory, + QObject* parent = nullptr); + ~TQTcpServer() override; private Q_SLOTS: void processIncoming(); @@ -65,13 +65,13 @@ private Q_SLOTS: struct ConnectionContext; void scheduleDeleteConnectionContext(QTcpSocket* connection); - void finish(stdcxx::shared_ptr ctx, bool healthy); + void finish(std::shared_ptr ctx, bool healthy); - stdcxx::shared_ptr server_; - stdcxx::shared_ptr processor_; - stdcxx::shared_ptr pfact_; + std::shared_ptr server_; + std::shared_ptr processor_; + std::shared_ptr pfact_; - typedef std::map > ConnectionContextMap; + typedef std::map > ConnectionContextMap; ConnectionContextMap ctxMap_; }; } diff --git a/lib/cpp/src/thrift/server/TConnectedClient.cpp b/lib/cpp/src/thrift/server/TConnectedClient.cpp index 33ec3a980ee..9a78e3e9b18 100644 --- a/lib/cpp/src/thrift/server/TConnectedClient.cpp +++ b/lib/cpp/src/thrift/server/TConnectedClient.cpp @@ -28,7 +28,7 @@ using apache::thrift::protocol::TProtocol; using apache::thrift::server::TServerEventHandler; using apache::thrift::transport::TTransport; using apache::thrift::transport::TTransportException; -using stdcxx::shared_ptr; +using std::shared_ptr; using std::string; TConnectedClient::TConnectedClient(const shared_ptr& processor, @@ -42,11 +42,10 @@ TConnectedClient::TConnectedClient(const shared_ptr& processor, outputProtocol_(outputProtocol), eventHandler_(eventHandler), client_(client), - opaqueContext_(0) { + opaqueContext_(nullptr) { } -TConnectedClient::~TConnectedClient() { -} +TConnectedClient::~TConnectedClient() = default; void TConnectedClient::run() { if (eventHandler_) { diff --git a/lib/cpp/src/thrift/server/TConnectedClient.h b/lib/cpp/src/thrift/server/TConnectedClient.h index 2f9d4c9de94..071571a8867 100644 --- a/lib/cpp/src/thrift/server/TConnectedClient.h +++ b/lib/cpp/src/thrift/server/TConnectedClient.h @@ -20,7 +20,7 @@ #ifndef _THRIFT_SERVER_TCONNECTEDCLIENT_H_ #define _THRIFT_SERVER_TCONNECTEDCLIENT_H_ 1 -#include +#include #include #include #include @@ -49,16 +49,16 @@ class TConnectedClient : public apache::thrift::concurrency::Runnable { * @param[in] client the TTransport representing the client */ TConnectedClient( - const stdcxx::shared_ptr& processor, - const stdcxx::shared_ptr& inputProtocol, - const stdcxx::shared_ptr& outputProtocol, - const stdcxx::shared_ptr& eventHandler, - const stdcxx::shared_ptr& client); + const std::shared_ptr& processor, + const std::shared_ptr& inputProtocol, + const std::shared_ptr& outputProtocol, + const std::shared_ptr& eventHandler, + const std::shared_ptr& client); /** * Destructor. */ - virtual ~TConnectedClient(); + ~TConnectedClient() override; /** * Drive the client until it is done. @@ -76,7 +76,7 @@ class TConnectedClient : public apache::thrift::concurrency::Runnable { * handle unexpected exceptions by logging * cleanup() */ - virtual void run() /* override */; + void run() override /* override */; protected: /** @@ -92,11 +92,11 @@ class TConnectedClient : public apache::thrift::concurrency::Runnable { virtual void cleanup(); private: - stdcxx::shared_ptr processor_; - stdcxx::shared_ptr inputProtocol_; - stdcxx::shared_ptr outputProtocol_; - stdcxx::shared_ptr eventHandler_; - stdcxx::shared_ptr client_; + std::shared_ptr processor_; + std::shared_ptr inputProtocol_; + std::shared_ptr outputProtocol_; + std::shared_ptr eventHandler_; + std::shared_ptr client_; /** * Context acquired from the eventHandler_ if one exists. diff --git a/lib/cpp/src/thrift/server/TNonblockingServer.cpp b/lib/cpp/src/thrift/server/TNonblockingServer.cpp index 194d59fa085..ea394c80452 100644 --- a/lib/cpp/src/thrift/server/TNonblockingServer.cpp +++ b/lib/cpp/src/thrift/server/TNonblockingServer.cpp @@ -22,7 +22,7 @@ #include #include #include -#include +#include #include #include @@ -84,7 +84,7 @@ using namespace apache::thrift::transport; using namespace apache::thrift::concurrency; using apache::thrift::transport::TSocket; using apache::thrift::transport::TTransportException; -using stdcxx::shared_ptr; +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 }; @@ -119,10 +119,10 @@ class TNonblockingServer::TConnection { TNonblockingServer* server_; /// TProcessor - stdcxx::shared_ptr processor_; + std::shared_ptr processor_; /// Object wrapping network socket - stdcxx::shared_ptr tSocket_; + std::shared_ptr tSocket_; /// Libevent object struct event event_; @@ -164,23 +164,23 @@ class TNonblockingServer::TConnection { int32_t callsForResize_; /// Transport to read from - stdcxx::shared_ptr inputTransport_; + std::shared_ptr inputTransport_; /// Transport that processor writes to - stdcxx::shared_ptr outputTransport_; + std::shared_ptr outputTransport_; /// extra transport generated by transport factory (e.g. BufferedRouterTransport) - stdcxx::shared_ptr factoryInputTransport_; - stdcxx::shared_ptr factoryOutputTransport_; + std::shared_ptr factoryInputTransport_; + std::shared_ptr factoryOutputTransport_; /// Protocol decoder - stdcxx::shared_ptr inputProtocol_; + std::shared_ptr inputProtocol_; /// Protocol encoder - stdcxx::shared_ptr outputProtocol_; + std::shared_ptr outputProtocol_; /// Server event handler, if any - stdcxx::shared_ptr serverEventHandler_; + std::shared_ptr serverEventHandler_; /// Thrift call context, if any void* connectionContext_; @@ -213,9 +213,9 @@ class TNonblockingServer::TConnection { class Task; /// Constructor - TConnection(stdcxx::shared_ptr socket, + TConnection(std::shared_ptr socket, TNonblockingIOThread* ioThread) { - readBuffer_ = NULL; + readBuffer_ = nullptr; readBufferSize_ = 0; ioThread_ = ioThread; @@ -249,7 +249,7 @@ class TNonblockingServer::TConnection { void init(TNonblockingIOThread* ioThread); /// set socket for connection - void setSocket(stdcxx::shared_ptr socket); + void setSocket(std::shared_ptr socket); /** * This is called when the application transitions from one state into @@ -305,10 +305,10 @@ class TNonblockingServer::TConnection { TAppState getState() const { return appState_; } /// return the TSocket transport wrapping this network connection - stdcxx::shared_ptr getTSocket() const { return tSocket_; } + std::shared_ptr getTSocket() const { return tSocket_; } /// return the server event handler if any - stdcxx::shared_ptr getServerEventHandler() { return serverEventHandler_; } + std::shared_ptr getServerEventHandler() { return serverEventHandler_; } /// return the Thrift connection context if any void* getConnectionContext() { return connectionContext_; } @@ -316,9 +316,9 @@ class TNonblockingServer::TConnection { class TNonblockingServer::TConnection::Task : public Runnable { public: - Task(stdcxx::shared_ptr processor, - stdcxx::shared_ptr input, - stdcxx::shared_ptr output, + Task(std::shared_ptr processor, + std::shared_ptr input, + std::shared_ptr output, TConnection* connection) : processor_(processor), input_(input), @@ -327,7 +327,7 @@ class TNonblockingServer::TConnection::Task : public Runnable { serverEventHandler_(connection_->getServerEventHandler()), connectionContext_(connection_->getConnectionContext()) {} - void run() { + void run() override { try { for (;;) { if (serverEventHandler_) { @@ -363,11 +363,11 @@ class TNonblockingServer::TConnection::Task : public Runnable { TConnection* getTConnection() { return connection_; } private: - stdcxx::shared_ptr processor_; - stdcxx::shared_ptr input_; - stdcxx::shared_ptr output_; + std::shared_ptr processor_; + std::shared_ptr input_; + std::shared_ptr output_; TConnection* connection_; - stdcxx::shared_ptr serverEventHandler_; + std::shared_ptr serverEventHandler_; void* connectionContext_; }; @@ -380,7 +380,7 @@ void TNonblockingServer::TConnection::init(TNonblockingIOThread* ioThread) { readBufferPos_ = 0; readWant_ = 0; - writeBuffer_ = NULL; + writeBuffer_ = nullptr; writeBufferSize_ = 0; writeBufferPos_ = 0; largestWriteBufferSize_ = 0; @@ -407,14 +407,14 @@ void TNonblockingServer::TConnection::init(TNonblockingIOThread* ioThread) { if (serverEventHandler_) { connectionContext_ = serverEventHandler_->createContext(inputProtocol_, outputProtocol_); } else { - connectionContext_ = NULL; + connectionContext_ = nullptr; } // Get the processor processor_ = server_->getProcessor(inputProtocol_, outputProtocol_, tSocket_); } -void TNonblockingServer::TConnection::setSocket(stdcxx::shared_ptr socket) { +void TNonblockingServer::TConnection::setSocket(std::shared_ptr socket) { tSocket_ = socket; } @@ -492,7 +492,11 @@ void TNonblockingServer::TConnection::workSocket() { case SOCKET_RECV: // It is an error to be in this state if we already have all the data - assert(readBufferPos_ < readWant_); + if (!(readBufferPos_ < readWant_)) { + GlobalOutput.printf("TNonblockingServer: frame size too short"); + close(); + return; + } try { // Read from the socket @@ -570,7 +574,7 @@ 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() == NULL; + return getOutputProtocolFactory() == nullptr; } /** @@ -610,7 +614,7 @@ void TNonblockingServer::TConnection::transition() { // We are setting up a Task to do this work and we will wait on it // Create task and dispatch to the thread manager - stdcxx::shared_ptr task = stdcxx::shared_ptr( + std::shared_ptr task = std::shared_ptr( new Task(processor_, inputProtocol_, outputProtocol_, this)); // The application is now waiting on the task to finish appState_ = APP_WAIT_TASK; @@ -687,7 +691,7 @@ void TNonblockingServer::TConnection::transition() { socketState_ = SOCKET_SEND; // Put the frame size into the write buffer - int32_t frameSize = (int32_t)htonl(writeBufferSize_ - 4); + auto frameSize = (int32_t)htonl(writeBufferSize_ - 4); memcpy(writeBuffer_, &frameSize, 4); // Socket into write mode @@ -720,7 +724,7 @@ void TNonblockingServer::TConnection::transition() { case APP_INIT: // Clear write buffer variables - writeBuffer_ = NULL; + writeBuffer_ = nullptr; writeBufferPos_ = 0; writeBufferSize_ = 0; @@ -749,8 +753,8 @@ void TNonblockingServer::TConnection::transition() { newSize *= 2; } - uint8_t* newBuffer = (uint8_t*)std::realloc(readBuffer_, newSize); - if (newBuffer == NULL) { + auto* newBuffer = (uint8_t*)std::realloc(readBuffer_, newSize); + if (newBuffer == nullptr) { // nothing else to be done... throw std::bad_alloc(); } @@ -829,7 +833,7 @@ void TNonblockingServer::TConnection::setFlags(short eventFlags) { event_base_set(ioThread_->getEventBase(), &event_); // Add the event - if (event_add(&event_, 0) == -1) { + if (event_add(&event_, nullptr) == -1) { GlobalOutput.perror("TConnection::setFlags(): could not event_add", THRIFT_GET_SOCKET_ERROR); } } @@ -843,7 +847,7 @@ void TNonblockingServer::TConnection::close() { if (serverEventHandler_) { serverEventHandler_->deleteContext(connectionContext_, inputProtocol_, outputProtocol_); } - ioThread_ = NULL; + ioThread_ = nullptr; // Close the socket tSocket_->close(); @@ -862,7 +866,7 @@ void TNonblockingServer::TConnection::close() { void TNonblockingServer::TConnection::checkIdleBufferMemLimit(size_t readLimit, size_t writeLimit) { if (readLimit > 0 && readBufferSize_ > readLimit) { free(readBuffer_); - readBuffer_ = NULL; + readBuffer_ = nullptr; readBufferSize_ = 0; } @@ -888,9 +892,9 @@ TNonblockingServer::~TNonblockingServer() { // 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()) { - stdcxx::shared_ptr iot = ioThreads_.back(); + std::shared_ptr iot = ioThreads_.back(); ioThreads_.pop_back(); - iot->setThread(stdcxx::shared_ptr()); + iot->setThread(std::shared_ptr()); } } @@ -898,7 +902,7 @@ TNonblockingServer::~TNonblockingServer() { * Creates a new connection either by reusing an object off the stack or * by allocating a new one entirely */ -TNonblockingServer::TConnection* TNonblockingServer::createConnection(stdcxx::shared_ptr socket) { +TNonblockingServer::TConnection* TNonblockingServer::createConnection(std::shared_ptr socket) { // Check the stack Guard g(connMutex_); @@ -910,7 +914,7 @@ TNonblockingServer::TConnection* TNonblockingServer::createConnection(stdcxx::sh TNonblockingIOThread* ioThread = ioThreads_[selectedThreadIdx].get(); // Check the connection stack to see if we can re-use - TConnection* result = NULL; + TConnection* result = nullptr; if (connectionStack_.empty()) { result = new TConnection(socket, ioThread); ++numTConnections_; @@ -954,7 +958,7 @@ void TNonblockingServer::handleEvent(THRIFT_SOCKET fd, short which) { assert(fd == serverSocket_); // Going to accept a new client socket - stdcxx::shared_ptr clientSocket; + std::shared_ptr clientSocket; clientSocket = serverTransport_->accept(); if (clientSocket) { @@ -979,7 +983,7 @@ void TNonblockingServer::handleEvent(THRIFT_SOCKET fd, short which) { TConnection* clientConnection = createConnection(clientSocket); // Fail fast if we could not create a TConnection object - if (clientConnection == NULL) { + if (clientConnection == nullptr) { GlobalOutput.printf("thriftServerEventHandler: failed TConnection factory"); clientSocket->close(); return; @@ -1017,13 +1021,13 @@ void TNonblockingServer::createAndListenOnSocket() { } -void TNonblockingServer::setThreadManager(stdcxx::shared_ptr threadManager) { +void TNonblockingServer::setThreadManager(std::shared_ptr threadManager) { threadManager_ = threadManager; if (threadManager) { threadManager->setExpireCallback( - apache::thrift::stdcxx::bind(&TNonblockingServer::expireClose, + std::bind(&TNonblockingServer::expireClose, this, - apache::thrift::stdcxx::placeholders::_1)); + std::placeholders::_1)); threadPoolProcessing_ = true; } else { threadPoolProcessing_ = false; @@ -1055,7 +1059,7 @@ bool TNonblockingServer::serverOverloaded() { bool TNonblockingServer::drainPendingTask() { if (threadManager_) { - stdcxx::shared_ptr task = threadManager_->removeNextPending(); + std::shared_ptr task = threadManager_->removeNextPending(); if (task) { TConnection* connection = static_cast(task.get())->getTConnection(); assert(connection && connection->getServer() && connection->getState() == APP_WAIT_TASK); @@ -1066,7 +1070,7 @@ bool TNonblockingServer::drainPendingTask() { return false; } -void TNonblockingServer::expireClose(stdcxx::shared_ptr task) { +void TNonblockingServer::expireClose(std::shared_ptr task) { TConnection* connection = static_cast(task.get())->getTConnection(); assert(connection && connection->getServer() && connection->getState() == APP_WAIT_TASK); connection->forceClose(); @@ -1074,8 +1078,8 @@ void TNonblockingServer::expireClose(stdcxx::shared_ptr task) { void TNonblockingServer::stop() { // Breaks the event loop in all threads so that they end ASAP. - for (uint32_t i = 0; i < ioThreads_.size(); ++i) { - ioThreads_[i]->stop(); + for (auto & ioThread : ioThreads_) { + ioThread->stop(); } } @@ -1118,12 +1122,7 @@ void TNonblockingServer::registerEvents(event_base* user_event_base) { // Launch all the secondary IO threads in separate threads if (ioThreads_.size() > 1) { - ioThreadFactory_.reset(new PlatformThreadFactory( -#if !USE_BOOST_THREAD && !USE_STD_THREAD - PlatformThreadFactory::OTHER, // scheduler - PlatformThreadFactory::NORMAL, // priority - 1, // stack size (MB) -#endif + ioThreadFactory_.reset(new ThreadFactory( false // detached )); @@ -1148,7 +1147,7 @@ void TNonblockingServer::registerEvents(event_base* user_event_base) { void TNonblockingServer::serve() { if (ioThreads_.empty()) - registerEvents(NULL); + registerEvents(nullptr); // Run the primary (listener) IO thread loop in our main thread; this will // only return when the server is shutting down. @@ -1167,10 +1166,13 @@ TNonblockingIOThread::TNonblockingIOThread(TNonblockingServer* server, bool useHighPriority) : server_(server), number_(number), + threadId_{}, listenSocket_(listenSocket), useHighPriority_(useHighPriority), - eventBase_(NULL), - ownEventBase_(false) { + eventBase_(nullptr), + ownEventBase_(false), + serverEvent_{}, + notificationEvent_{} { notificationPipeFDs_[0] = -1; notificationPipeFDs_[1] = -1; } @@ -1191,13 +1193,13 @@ TNonblockingIOThread::~TNonblockingIOThread() { listenSocket_ = THRIFT_INVALID_SOCKET; } - for (int i = 0; i < 2; ++i) { - if (notificationPipeFDs_[i] >= 0) { - if (0 != ::THRIFT_CLOSESOCKET(notificationPipeFDs_[i])) { + for (auto notificationPipeFD : notificationPipeFDs_) { + if (notificationPipeFD >= 0) { + if (0 != ::THRIFT_CLOSESOCKET(notificationPipeFD)) { GlobalOutput.perror("TNonblockingIOThread notificationPipe close(): ", THRIFT_GET_SOCKET_ERROR); } - notificationPipeFDs_[i] = THRIFT_INVALID_SOCKET; + notificationPipeFD = THRIFT_INVALID_SOCKET; } } } @@ -1213,13 +1215,13 @@ void TNonblockingIOThread::createNotificationPipe() { ::THRIFT_CLOSESOCKET(notificationPipeFDs_[1]); throw TException("TNonblockingServer::createNotificationPipe() THRIFT_O_NONBLOCK"); } - for (int i = 0; i < 2; ++i) { + for (auto notificationPipeFD : notificationPipeFDs_) { #if LIBEVENT_VERSION_NUMBER < 0x02000000 int flags; - if ((flags = THRIFT_FCNTL(notificationPipeFDs_[i], F_GETFD, 0)) < 0 - || THRIFT_FCNTL(notificationPipeFDs_[i], F_SETFD, flags | FD_CLOEXEC) < 0) { + if ((flags = THRIFT_FCNTL(notificationPipeFD, F_GETFD, 0)) < 0 + || THRIFT_FCNTL(notificationPipeFD, F_SETFD, flags | FD_CLOEXEC) < 0) { #else - if (evutil_make_socket_closeonexec(notificationPipeFDs_[i]) < 0) { + if (evutil_make_socket_closeonexec(notificationPipeFD) < 0) { #endif ::THRIFT_CLOSESOCKET(notificationPipeFDs_[0]); ::THRIFT_CLOSESOCKET(notificationPipeFDs_[1]); @@ -1236,9 +1238,9 @@ void TNonblockingIOThread::createNotificationPipe() { void TNonblockingIOThread::registerEvents() { threadId_ = Thread::get_current(); - assert(eventBase_ == 0); + assert(eventBase_ == nullptr); eventBase_ = getServer()->getUserEventBase(); - if (eventBase_ == NULL) { + if (eventBase_ == nullptr) { eventBase_ = event_base_new(); ownEventBase_ = true; } @@ -1260,7 +1262,7 @@ void TNonblockingIOThread::registerEvents() { event_base_set(eventBase_, &serverEvent_); // Add the event and start up the server - if (-1 == event_add(&serverEvent_, 0)) { + if (-1 == event_add(&serverEvent_, nullptr)) { throw TException( "TNonblockingServer::serve(): " "event_add() failed on server listen event"); @@ -1281,7 +1283,7 @@ void TNonblockingIOThread::registerEvents() { event_base_set(eventBase_, ¬ificationEvent_); // Add the event and start up the server - if (-1 == event_add(¬ificationEvent_, 0)) { + if (-1 == event_add(¬ificationEvent_, nullptr)) { throw TException( "TNonblockingServer::serve(): " "event_add() failed on task-done notification event"); @@ -1290,7 +1292,7 @@ void TNonblockingIOThread::registerEvents() { } bool TNonblockingIOThread::notify(TNonblockingServer::TConnection* conn) { - THRIFT_SOCKET fd = getNotificationSendFD(); + auto fd = getNotificationSendFD(); if (fd < 0) { return false; } @@ -1339,7 +1341,7 @@ bool TNonblockingIOThread::notify(TNonblockingServer::TConnection* conn) { FD_ZERO(&efds); FD_SET(fd, &wfds); FD_SET(fd, &efds); - ret = select(static_cast(fd + 1), NULL, &wfds, &efds, NULL); + ret = select(static_cast(fd + 1), nullptr, &wfds, &efds, nullptr); if (ret < 0) { return false; } else if (ret == 0) { @@ -1373,16 +1375,16 @@ bool TNonblockingIOThread::notify(TNonblockingServer::TConnection* conn) { /* static */ void TNonblockingIOThread::notifyHandler(evutil_socket_t fd, short which, void* v) { - TNonblockingIOThread* ioThread = (TNonblockingIOThread*)v; + auto* ioThread = (TNonblockingIOThread*)v; assert(ioThread); (void)which; while (true) { - TNonblockingServer::TConnection* connection = 0; + TNonblockingServer::TConnection* connection = nullptr; const int kSize = sizeof(connection); long nBytes = recv(fd, cast_sockopt(&connection), kSize, 0); if (nBytes == kSize) { - if (connection == NULL) { + if (connection == nullptr) { // this is the command to stop our thread, exit the handler! ioThread->breakLoop(false); return; @@ -1425,7 +1427,7 @@ void TNonblockingIOThread::breakLoop(bool error) { // same thread, this means the thread can't be blocking in the event // loop either. if (!Thread::is_current(threadId_)) { - notify(NULL); + notify(nullptr); } else { // cause the loop to stop ASAP - even if it has things to do in it event_base_loopbreak(eventBase_); @@ -1462,14 +1464,14 @@ void TNonblockingIOThread::setCurrentThreadHighPriority(bool value) { } void TNonblockingIOThread::run() { - if (eventBase_ == NULL) { + if (eventBase_ == nullptr) { registerEvents(); } if (useHighPriority_) { setCurrentThreadHighPriority(true); } - if (eventBase_ != NULL) + if (eventBase_ != nullptr) { GlobalOutput.printf("TNonblockingServer: IO thread #%d entering loop...", number_); // Run libevent engine, never returns, invokes calls to eventHandler diff --git a/lib/cpp/src/thrift/server/TNonblockingServer.h b/lib/cpp/src/thrift/server/TNonblockingServer.h index f95a7290115..65e569dcf03 100644 --- a/lib/cpp/src/thrift/server/TNonblockingServer.h +++ b/lib/cpp/src/thrift/server/TNonblockingServer.h @@ -21,7 +21,7 @@ #define _THRIFT_SERVER_TNONBLOCKINGSERVER_H_ 1 #include -#include +#include #include #include #include @@ -30,7 +30,7 @@ #include #include #include -#include +#include #include #include #include @@ -53,7 +53,6 @@ using apache::thrift::transport::TNonblockingServerTransport; using apache::thrift::protocol::TProtocol; using apache::thrift::concurrency::Runnable; using apache::thrift::concurrency::ThreadManager; -using apache::thrift::concurrency::PlatformThreadFactory; using apache::thrift::concurrency::ThreadFactory; using apache::thrift::concurrency::Thread; using apache::thrift::concurrency::Mutex; @@ -159,17 +158,17 @@ class TNonblockingServer : public TServer { /// The optional user-provided event-base (for single-thread servers) event_base* userEventBase_; - /// For processing via thread pool, may be NULL - stdcxx::shared_ptr threadManager_; + /// For processing via thread pool, may be nullptr + std::shared_ptr threadManager_; /// Is thread pool processing? bool threadPoolProcessing_; // Factory to create the IO threads - stdcxx::shared_ptr ioThreadFactory_; + std::shared_ptr ioThreadFactory_; // Vector of IOThread objects that will handle our IO - std::vector > ioThreads_; + std::vector > ioThreads_; // Index of next IO Thread to be used (for round-robin) uint32_t nextIOThread_; @@ -264,7 +263,7 @@ class TNonblockingServer : public TServer { /* */ - stdcxx::shared_ptr serverTransport_; + std::shared_ptr serverTransport_; /** * Called when server socket had something happen. We accept all waiting @@ -280,7 +279,7 @@ class TNonblockingServer : public TServer { numIOThreads_ = DEFAULT_IO_THREADS; nextIOThread_ = 0; useHighPriorityIOThreads_ = false; - userEventBase_ = NULL; + userEventBase_ = nullptr; threadPoolProcessing_ = false; numTConnections_ = 0; numActiveProcessors_ = 0; @@ -301,24 +300,24 @@ class TNonblockingServer : public TServer { } public: - TNonblockingServer(const stdcxx::shared_ptr& processorFactory, - const stdcxx::shared_ptr& serverTransport) + TNonblockingServer(const std::shared_ptr& processorFactory, + const std::shared_ptr& serverTransport) : TServer(processorFactory), serverTransport_(serverTransport) { init(); } - TNonblockingServer(const stdcxx::shared_ptr& processor, - const stdcxx::shared_ptr& serverTransport) + TNonblockingServer(const std::shared_ptr& processor, + const std::shared_ptr& serverTransport) : TServer(processor), serverTransport_(serverTransport) { init(); } - TNonblockingServer(const stdcxx::shared_ptr& processorFactory, - const stdcxx::shared_ptr& protocolFactory, - const stdcxx::shared_ptr& serverTransport, - const stdcxx::shared_ptr& threadManager - = stdcxx::shared_ptr()) + TNonblockingServer(const std::shared_ptr& processorFactory, + const std::shared_ptr& protocolFactory, + const std::shared_ptr& serverTransport, + const std::shared_ptr& threadManager + = std::shared_ptr()) : TServer(processorFactory), serverTransport_(serverTransport) { init(); @@ -327,11 +326,11 @@ class TNonblockingServer : public TServer { setThreadManager(threadManager); } - TNonblockingServer(const stdcxx::shared_ptr& processor, - const stdcxx::shared_ptr& protocolFactory, - const stdcxx::shared_ptr& serverTransport, - const stdcxx::shared_ptr& threadManager - = stdcxx::shared_ptr()) + TNonblockingServer(const std::shared_ptr& processor, + const std::shared_ptr& protocolFactory, + const std::shared_ptr& serverTransport, + const std::shared_ptr& threadManager + = std::shared_ptr()) : TServer(processor), serverTransport_(serverTransport) { init(); @@ -340,14 +339,14 @@ class TNonblockingServer : public TServer { setThreadManager(threadManager); } - TNonblockingServer(const stdcxx::shared_ptr& processorFactory, - const stdcxx::shared_ptr& inputTransportFactory, - const stdcxx::shared_ptr& outputTransportFactory, - const stdcxx::shared_ptr& inputProtocolFactory, - const stdcxx::shared_ptr& outputProtocolFactory, - const stdcxx::shared_ptr& serverTransport, - const stdcxx::shared_ptr& threadManager - = stdcxx::shared_ptr()) + TNonblockingServer(const std::shared_ptr& processorFactory, + const std::shared_ptr& inputTransportFactory, + const std::shared_ptr& outputTransportFactory, + const std::shared_ptr& inputProtocolFactory, + const std::shared_ptr& outputProtocolFactory, + const std::shared_ptr& serverTransport, + const std::shared_ptr& threadManager + = std::shared_ptr()) : TServer(processorFactory), serverTransport_(serverTransport) { init(); @@ -358,14 +357,14 @@ class TNonblockingServer : public TServer { setThreadManager(threadManager); } - TNonblockingServer(const stdcxx::shared_ptr& processor, - const stdcxx::shared_ptr& inputTransportFactory, - const stdcxx::shared_ptr& outputTransportFactory, - const stdcxx::shared_ptr& inputProtocolFactory, - const stdcxx::shared_ptr& outputProtocolFactory, - const stdcxx::shared_ptr& serverTransport, - const stdcxx::shared_ptr& threadManager - = stdcxx::shared_ptr()) + TNonblockingServer(const std::shared_ptr& processor, + const std::shared_ptr& inputTransportFactory, + const std::shared_ptr& outputTransportFactory, + const std::shared_ptr& inputProtocolFactory, + const std::shared_ptr& outputProtocolFactory, + const std::shared_ptr& serverTransport, + const std::shared_ptr& threadManager + = std::shared_ptr()) : TServer(processor), serverTransport_(serverTransport) { init(); @@ -376,19 +375,17 @@ class TNonblockingServer : public TServer { setThreadManager(threadManager); } - ~TNonblockingServer(); + ~TNonblockingServer() override; - void setThreadManager(stdcxx::shared_ptr threadManager); + void setThreadManager(std::shared_ptr threadManager); int getListenPort() { return serverTransport_->getListenPort(); } - stdcxx::shared_ptr getThreadManager() { return threadManager_; } + std::shared_ptr 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. We always use a - * PosixThreadFactory for the IO worker threads, because they must joinable - * for clean shutdown. + * the call to serve() and has no effect afterwards. */ void setNumIOThreads(size_t numThreads) { numIOThreads_ = numThreads; @@ -421,7 +418,7 @@ class TNonblockingServer : public TServer { bool isThreadPoolProcessing() const { return threadPoolProcessing_; } - void addTask(stdcxx::shared_ptr task) { + void addTask(std::shared_ptr task) { threadManager_->add(task, 0LL, taskExpireTime_); } @@ -671,12 +668,12 @@ class TNonblockingServer : public TServer { * Main workhorse function, starts up the server listening on a port and * loops over the libevent handler. */ - void serve(); + void serve() override; /** * Causes the server to terminate gracefully (can be called from any thread). */ - void stop(); + void stop() override; /// Creates a socket to listen on and binds it to the local port. void createAndListenOnSocket(); @@ -709,7 +706,7 @@ class TNonblockingServer : public TServer { * * @param task the runnable associated with the expired task. */ - void expireClose(stdcxx::shared_ptr task); + void expireClose(std::shared_ptr task); /** * Return an initialized connection object. Creates or recovers from @@ -721,7 +718,7 @@ class TNonblockingServer : public TServer { * @param addrLen the length of addr * @return pointer to initialized TConnection object. */ - TConnection* createConnection(stdcxx::shared_ptr socket); + TConnection* createConnection(std::shared_ptr socket); /** * Returns a connection to pool or deletion. If the connection pool @@ -743,7 +740,7 @@ class TNonblockingIOThread : public Runnable { THRIFT_SOCKET listenSocket, bool useHighPriority); - ~TNonblockingIOThread(); + ~TNonblockingIOThread() override; // Returns the event-base for this thread. event_base* getEventBase() const { return eventBase_; } @@ -765,16 +762,16 @@ class TNonblockingIOThread : public Runnable { evutil_socket_t getNotificationRecvFD() const { return notificationPipeFDs_[0]; } // Returns the actual thread object associated with this IO thread. - stdcxx::shared_ptr getThread() const { return thread_; } + std::shared_ptr getThread() const { return thread_; } // Sets the actual thread object associated with this IO thread. - void setThread(const stdcxx::shared_ptr& t) { thread_ = t; } + void setThread(const std::shared_ptr& 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(). - virtual void run(); + void run() override; // Exits the event loop as soon as possible. void stop(); @@ -853,7 +850,7 @@ class TNonblockingIOThread : public Runnable { evutil_socket_t notificationPipeFDs_[2]; /// Actual IO Thread - stdcxx::shared_ptr thread_; + std::shared_ptr thread_; }; } } diff --git a/lib/cpp/src/thrift/server/TServer.h b/lib/cpp/src/thrift/server/TServer.h index f4cd7bc1408..d2eabde1211 100644 --- a/lib/cpp/src/thrift/server/TServer.h +++ b/lib/cpp/src/thrift/server/TServer.h @@ -25,7 +25,7 @@ #include #include -#include +#include namespace apache { namespace thrift { @@ -48,7 +48,7 @@ using apache::thrift::transport::TTransportFactory; */ class TServerEventHandler { public: - virtual ~TServerEventHandler() {} + virtual ~TServerEventHandler() = default; /** * Called before the server begins. @@ -58,11 +58,11 @@ class TServerEventHandler { /** * Called when a new client has connected and is about to being processing. */ - virtual void* createContext(stdcxx::shared_ptr input, - stdcxx::shared_ptr output) { + virtual void* createContext(std::shared_ptr input, + std::shared_ptr output) { (void)input; (void)output; - return NULL; + return nullptr; } /** @@ -70,8 +70,8 @@ class TServerEventHandler { * context. */ virtual void deleteContext(void* serverContext, - stdcxx::shared_ptr input, - stdcxx::shared_ptr output) { + std::shared_ptr input, + std::shared_ptr output) { (void)serverContext; (void)input; (void)output; @@ -80,7 +80,7 @@ class TServerEventHandler { /** * Called when a client is about to call the processor. */ - virtual void processContext(void* serverContext, stdcxx::shared_ptr transport) { + virtual void processContext(void* serverContext, std::shared_ptr transport) { (void)serverContext; (void)transport; } @@ -89,7 +89,7 @@ class TServerEventHandler { /** * Prevent direct instantiation. */ - TServerEventHandler() {} + TServerEventHandler() = default; }; /** @@ -98,71 +98,71 @@ class TServerEventHandler { */ class TServer : public concurrency::Runnable { public: - virtual ~TServer() {} + ~TServer() override = default; virtual void serve() = 0; virtual void stop() {} // Allows running the server as a Runnable thread - virtual void run() { serve(); } + void run() override { serve(); } - stdcxx::shared_ptr getProcessorFactory() { return processorFactory_; } + std::shared_ptr getProcessorFactory() { return processorFactory_; } - stdcxx::shared_ptr getServerTransport() { return serverTransport_; } + std::shared_ptr getServerTransport() { return serverTransport_; } - stdcxx::shared_ptr getInputTransportFactory() { return inputTransportFactory_; } + std::shared_ptr getInputTransportFactory() { return inputTransportFactory_; } - stdcxx::shared_ptr getOutputTransportFactory() { + std::shared_ptr getOutputTransportFactory() { return outputTransportFactory_; } - stdcxx::shared_ptr getInputProtocolFactory() { return inputProtocolFactory_; } + std::shared_ptr getInputProtocolFactory() { return inputProtocolFactory_; } - stdcxx::shared_ptr getOutputProtocolFactory() { return outputProtocolFactory_; } + std::shared_ptr getOutputProtocolFactory() { return outputProtocolFactory_; } - stdcxx::shared_ptr getEventHandler() { return eventHandler_; } + std::shared_ptr getEventHandler() { return eventHandler_; } protected: - TServer(const stdcxx::shared_ptr& processorFactory) + TServer(const std::shared_ptr& processorFactory) : processorFactory_(processorFactory) { - setInputTransportFactory(stdcxx::shared_ptr(new TTransportFactory())); - setOutputTransportFactory(stdcxx::shared_ptr(new TTransportFactory())); - setInputProtocolFactory(stdcxx::shared_ptr(new TBinaryProtocolFactory())); - setOutputProtocolFactory(stdcxx::shared_ptr(new TBinaryProtocolFactory())); + setInputTransportFactory(std::shared_ptr(new TTransportFactory())); + setOutputTransportFactory(std::shared_ptr(new TTransportFactory())); + setInputProtocolFactory(std::shared_ptr(new TBinaryProtocolFactory())); + setOutputProtocolFactory(std::shared_ptr(new TBinaryProtocolFactory())); } - TServer(const stdcxx::shared_ptr& processor) + TServer(const std::shared_ptr& processor) : processorFactory_(new TSingletonProcessorFactory(processor)) { - setInputTransportFactory(stdcxx::shared_ptr(new TTransportFactory())); - setOutputTransportFactory(stdcxx::shared_ptr(new TTransportFactory())); - setInputProtocolFactory(stdcxx::shared_ptr(new TBinaryProtocolFactory())); - setOutputProtocolFactory(stdcxx::shared_ptr(new TBinaryProtocolFactory())); + setInputTransportFactory(std::shared_ptr(new TTransportFactory())); + setOutputTransportFactory(std::shared_ptr(new TTransportFactory())); + setInputProtocolFactory(std::shared_ptr(new TBinaryProtocolFactory())); + setOutputProtocolFactory(std::shared_ptr(new TBinaryProtocolFactory())); } - TServer(const stdcxx::shared_ptr& processorFactory, - const stdcxx::shared_ptr& serverTransport) + TServer(const std::shared_ptr& processorFactory, + const std::shared_ptr& serverTransport) : processorFactory_(processorFactory), serverTransport_(serverTransport) { - setInputTransportFactory(stdcxx::shared_ptr(new TTransportFactory())); - setOutputTransportFactory(stdcxx::shared_ptr(new TTransportFactory())); - setInputProtocolFactory(stdcxx::shared_ptr(new TBinaryProtocolFactory())); - setOutputProtocolFactory(stdcxx::shared_ptr(new TBinaryProtocolFactory())); + setInputTransportFactory(std::shared_ptr(new TTransportFactory())); + setOutputTransportFactory(std::shared_ptr(new TTransportFactory())); + setInputProtocolFactory(std::shared_ptr(new TBinaryProtocolFactory())); + setOutputProtocolFactory(std::shared_ptr(new TBinaryProtocolFactory())); } - TServer(const stdcxx::shared_ptr& processor, - const stdcxx::shared_ptr& serverTransport) + TServer(const std::shared_ptr& processor, + const std::shared_ptr& serverTransport) : processorFactory_(new TSingletonProcessorFactory(processor)), serverTransport_(serverTransport) { - setInputTransportFactory(stdcxx::shared_ptr(new TTransportFactory())); - setOutputTransportFactory(stdcxx::shared_ptr(new TTransportFactory())); - setInputProtocolFactory(stdcxx::shared_ptr(new TBinaryProtocolFactory())); - setOutputProtocolFactory(stdcxx::shared_ptr(new TBinaryProtocolFactory())); + setInputTransportFactory(std::shared_ptr(new TTransportFactory())); + setOutputTransportFactory(std::shared_ptr(new TTransportFactory())); + setInputProtocolFactory(std::shared_ptr(new TBinaryProtocolFactory())); + setOutputProtocolFactory(std::shared_ptr(new TBinaryProtocolFactory())); } - TServer(const stdcxx::shared_ptr& processorFactory, - const stdcxx::shared_ptr& serverTransport, - const stdcxx::shared_ptr& transportFactory, - const stdcxx::shared_ptr& protocolFactory) + TServer(const std::shared_ptr& processorFactory, + const std::shared_ptr& serverTransport, + const std::shared_ptr& transportFactory, + const std::shared_ptr& protocolFactory) : processorFactory_(processorFactory), serverTransport_(serverTransport), inputTransportFactory_(transportFactory), @@ -170,10 +170,10 @@ class TServer : public concurrency::Runnable { inputProtocolFactory_(protocolFactory), outputProtocolFactory_(protocolFactory) {} - TServer(const stdcxx::shared_ptr& processor, - const stdcxx::shared_ptr& serverTransport, - const stdcxx::shared_ptr& transportFactory, - const stdcxx::shared_ptr& protocolFactory) + TServer(const std::shared_ptr& processor, + const std::shared_ptr& serverTransport, + const std::shared_ptr& transportFactory, + const std::shared_ptr& protocolFactory) : processorFactory_(new TSingletonProcessorFactory(processor)), serverTransport_(serverTransport), inputTransportFactory_(transportFactory), @@ -181,12 +181,12 @@ class TServer : public concurrency::Runnable { inputProtocolFactory_(protocolFactory), outputProtocolFactory_(protocolFactory) {} - TServer(const stdcxx::shared_ptr& processorFactory, - const stdcxx::shared_ptr& serverTransport, - const stdcxx::shared_ptr& inputTransportFactory, - const stdcxx::shared_ptr& outputTransportFactory, - const stdcxx::shared_ptr& inputProtocolFactory, - const stdcxx::shared_ptr& outputProtocolFactory) + TServer(const std::shared_ptr& processorFactory, + const std::shared_ptr& serverTransport, + const std::shared_ptr& inputTransportFactory, + const std::shared_ptr& outputTransportFactory, + const std::shared_ptr& inputProtocolFactory, + const std::shared_ptr& outputProtocolFactory) : processorFactory_(processorFactory), serverTransport_(serverTransport), inputTransportFactory_(inputTransportFactory), @@ -194,12 +194,12 @@ class TServer : public concurrency::Runnable { inputProtocolFactory_(inputProtocolFactory), outputProtocolFactory_(outputProtocolFactory) {} - TServer(const stdcxx::shared_ptr& processor, - const stdcxx::shared_ptr& serverTransport, - const stdcxx::shared_ptr& inputTransportFactory, - const stdcxx::shared_ptr& outputTransportFactory, - const stdcxx::shared_ptr& inputProtocolFactory, - const stdcxx::shared_ptr& outputProtocolFactory) + TServer(const std::shared_ptr& processor, + const std::shared_ptr& serverTransport, + const std::shared_ptr& inputTransportFactory, + const std::shared_ptr& outputTransportFactory, + const std::shared_ptr& inputProtocolFactory, + const std::shared_ptr& outputProtocolFactory) : processorFactory_(new TSingletonProcessorFactory(processor)), serverTransport_(serverTransport), inputTransportFactory_(inputTransportFactory), @@ -214,9 +214,9 @@ class TServer : public concurrency::Runnable { * call). This allows the TProcessorFactory to return a different processor * for each connection if it desires. */ - stdcxx::shared_ptr getProcessor(stdcxx::shared_ptr inputProtocol, - stdcxx::shared_ptr outputProtocol, - stdcxx::shared_ptr transport) { + std::shared_ptr getProcessor(std::shared_ptr inputProtocol, + std::shared_ptr outputProtocol, + std::shared_ptr transport) { TConnectionInfo connInfo; connInfo.input = inputProtocol; connInfo.output = outputProtocol; @@ -225,35 +225,35 @@ class TServer : public concurrency::Runnable { } // Class variables - stdcxx::shared_ptr processorFactory_; - stdcxx::shared_ptr serverTransport_; + std::shared_ptr processorFactory_; + std::shared_ptr serverTransport_; - stdcxx::shared_ptr inputTransportFactory_; - stdcxx::shared_ptr outputTransportFactory_; + std::shared_ptr inputTransportFactory_; + std::shared_ptr outputTransportFactory_; - stdcxx::shared_ptr inputProtocolFactory_; - stdcxx::shared_ptr outputProtocolFactory_; + std::shared_ptr inputProtocolFactory_; + std::shared_ptr outputProtocolFactory_; - stdcxx::shared_ptr eventHandler_; + std::shared_ptr eventHandler_; public: - void setInputTransportFactory(stdcxx::shared_ptr inputTransportFactory) { + void setInputTransportFactory(std::shared_ptr inputTransportFactory) { inputTransportFactory_ = inputTransportFactory; } - void setOutputTransportFactory(stdcxx::shared_ptr outputTransportFactory) { + void setOutputTransportFactory(std::shared_ptr outputTransportFactory) { outputTransportFactory_ = outputTransportFactory; } - void setInputProtocolFactory(stdcxx::shared_ptr inputProtocolFactory) { + void setInputProtocolFactory(std::shared_ptr inputProtocolFactory) { inputProtocolFactory_ = inputProtocolFactory; } - void setOutputProtocolFactory(stdcxx::shared_ptr outputProtocolFactory) { + void setOutputProtocolFactory(std::shared_ptr outputProtocolFactory) { outputProtocolFactory_ = outputProtocolFactory; } - void setServerEventHandler(stdcxx::shared_ptr eventHandler) { + void setServerEventHandler(std::shared_ptr eventHandler) { eventHandler_ = eventHandler; } }; diff --git a/lib/cpp/src/thrift/server/TServerFramework.cpp b/lib/cpp/src/thrift/server/TServerFramework.cpp index ae38336d49e..302cbf1662f 100644 --- a/lib/cpp/src/thrift/server/TServerFramework.cpp +++ b/lib/cpp/src/thrift/server/TServerFramework.cpp @@ -29,8 +29,8 @@ namespace server { using apache::thrift::concurrency::Synchronized; using apache::thrift::protocol::TProtocol; using apache::thrift::protocol::TProtocolFactory; -using apache::thrift::stdcxx::bind; -using apache::thrift::stdcxx::shared_ptr; +using std::bind; +using std::shared_ptr; using apache::thrift::transport::TServerTransport; using apache::thrift::transport::TTransport; using apache::thrift::transport::TTransportException; @@ -91,8 +91,7 @@ TServerFramework::TServerFramework(const shared_ptr& processor, limit_(INT64_MAX) { } -TServerFramework::~TServerFramework() { -} +TServerFramework::~TServerFramework() = default; template static void releaseOneDescriptor(const string& name, T& pTransport) { @@ -161,14 +160,15 @@ void TServerFramework::serve() { outputProtocol, eventHandler_, client), - bind(&TServerFramework::disposeConnectedClient, this, stdcxx::placeholders::_1))); + 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. + if (ttx.getType() == TTransportException::TIMED_OUT + || ttx.getType() == TTransportException::CLIENT_DISCONNECT) { + // Accept timeout and client disconnect - continue processing. continue; } else if (ttx.getType() == TTransportException::END_OF_FILE || ttx.getType() == TTransportException::INTERRUPTED) { diff --git a/lib/cpp/src/thrift/server/TServerFramework.h b/lib/cpp/src/thrift/server/TServerFramework.h index 706fd490a0c..dac79ef5959 100644 --- a/lib/cpp/src/thrift/server/TServerFramework.h +++ b/lib/cpp/src/thrift/server/TServerFramework.h @@ -20,7 +20,7 @@ #ifndef _THRIFT_SERVER_TSERVERFRAMEWORK_H_ #define _THRIFT_SERVER_TSERVERFRAMEWORK_H_ 1 -#include +#include #include #include #include @@ -48,34 +48,34 @@ namespace server { class TServerFramework : public TServer { public: TServerFramework( - const stdcxx::shared_ptr& processorFactory, - const stdcxx::shared_ptr& serverTransport, - const stdcxx::shared_ptr& transportFactory, - const stdcxx::shared_ptr& protocolFactory); + const std::shared_ptr& processorFactory, + const std::shared_ptr& serverTransport, + const std::shared_ptr& transportFactory, + const std::shared_ptr& protocolFactory); TServerFramework( - const stdcxx::shared_ptr& processor, - const stdcxx::shared_ptr& serverTransport, - const stdcxx::shared_ptr& transportFactory, - const stdcxx::shared_ptr& protocolFactory); + const std::shared_ptr& processor, + const std::shared_ptr& serverTransport, + const std::shared_ptr& transportFactory, + const std::shared_ptr& protocolFactory); TServerFramework( - const stdcxx::shared_ptr& processorFactory, - const stdcxx::shared_ptr& serverTransport, - const stdcxx::shared_ptr& inputTransportFactory, - const stdcxx::shared_ptr& outputTransportFactory, - const stdcxx::shared_ptr& inputProtocolFactory, - const stdcxx::shared_ptr& outputProtocolFactory); + const std::shared_ptr& processorFactory, + const std::shared_ptr& serverTransport, + const std::shared_ptr& inputTransportFactory, + const std::shared_ptr& outputTransportFactory, + const std::shared_ptr& inputProtocolFactory, + const std::shared_ptr& outputProtocolFactory); TServerFramework( - const stdcxx::shared_ptr& processor, - const stdcxx::shared_ptr& serverTransport, - const stdcxx::shared_ptr& inputTransportFactory, - const stdcxx::shared_ptr& outputTransportFactory, - const stdcxx::shared_ptr& inputProtocolFactory, - const stdcxx::shared_ptr& outputProtocolFactory); + const std::shared_ptr& processor, + const std::shared_ptr& serverTransport, + const std::shared_ptr& inputTransportFactory, + const std::shared_ptr& outputTransportFactory, + const std::shared_ptr& inputProtocolFactory, + const std::shared_ptr& outputProtocolFactory); - virtual ~TServerFramework(); + ~TServerFramework() override; /** * Accept clients from the TServerTransport and add them for processing. @@ -84,12 +84,12 @@ class TServerFramework : public TServer { * Post-conditions (return guarantees): * The serverTransport will be closed. */ - virtual void serve(); + void serve() override; /** * Interrupt serve() so that it meets post-conditions and returns. */ - virtual void stop(); + void stop() override; /** * Get the concurrent client limit. @@ -130,7 +130,7 @@ class TServerFramework : public TServer { * * \param[in] pClient the newly connected client */ - virtual void onClientConnected(const stdcxx::shared_ptr& pClient) = 0; + virtual void onClientConnected(const std::shared_ptr& pClient) = 0; /** * A client has disconnected. @@ -149,7 +149,7 @@ class TServerFramework : public TServer { * client rate limiting after onClientConnected returns by blocking the * serve() thread if the limit has been reached. */ - void newlyConnectedClient(const stdcxx::shared_ptr& pClient); + void newlyConnectedClient(const std::shared_ptr& pClient); /** * Smart pointer client deletion. diff --git a/lib/cpp/src/thrift/server/TSimpleServer.cpp b/lib/cpp/src/thrift/server/TSimpleServer.cpp index a0afbbe7360..ba7a183db03 100644 --- a/lib/cpp/src/thrift/server/TSimpleServer.cpp +++ b/lib/cpp/src/thrift/server/TSimpleServer.cpp @@ -29,7 +29,7 @@ using apache::thrift::transport::TServerTransport; using apache::thrift::transport::TTransport; using apache::thrift::transport::TTransportException; using apache::thrift::transport::TTransportFactory; -using stdcxx::shared_ptr; +using std::shared_ptr; using std::string; TSimpleServer::TSimpleServer(const shared_ptr& processorFactory, @@ -78,8 +78,7 @@ TSimpleServer::TSimpleServer(const shared_ptr& processor, TServerFramework::setConcurrentClientLimit(1); } -TSimpleServer::~TSimpleServer() { -} +TSimpleServer::~TSimpleServer() = default; /** * The main body of customized implementation for TSimpleServer is quite simple: diff --git a/lib/cpp/src/thrift/server/TSimpleServer.h b/lib/cpp/src/thrift/server/TSimpleServer.h index ac4ed34ea2c..3afeb79d51d 100644 --- a/lib/cpp/src/thrift/server/TSimpleServer.h +++ b/lib/cpp/src/thrift/server/TSimpleServer.h @@ -34,41 +34,41 @@ namespace server { class TSimpleServer : public TServerFramework { public: TSimpleServer( - const stdcxx::shared_ptr& processorFactory, - const stdcxx::shared_ptr& serverTransport, - const stdcxx::shared_ptr& transportFactory, - const stdcxx::shared_ptr& protocolFactory); + const std::shared_ptr& processorFactory, + const std::shared_ptr& serverTransport, + const std::shared_ptr& transportFactory, + const std::shared_ptr& protocolFactory); TSimpleServer( - const stdcxx::shared_ptr& processor, - const stdcxx::shared_ptr& serverTransport, - const stdcxx::shared_ptr& transportFactory, - const stdcxx::shared_ptr& protocolFactory); + const std::shared_ptr& processor, + const std::shared_ptr& serverTransport, + const std::shared_ptr& transportFactory, + const std::shared_ptr& protocolFactory); TSimpleServer( - const stdcxx::shared_ptr& processorFactory, - const stdcxx::shared_ptr& serverTransport, - const stdcxx::shared_ptr& inputTransportFactory, - const stdcxx::shared_ptr& outputTransportFactory, - const stdcxx::shared_ptr& inputProtocolFactory, - const stdcxx::shared_ptr& outputProtocolFactory); + const std::shared_ptr& processorFactory, + const std::shared_ptr& serverTransport, + const std::shared_ptr& inputTransportFactory, + const std::shared_ptr& outputTransportFactory, + const std::shared_ptr& inputProtocolFactory, + const std::shared_ptr& outputProtocolFactory); TSimpleServer( - const stdcxx::shared_ptr& processor, - const stdcxx::shared_ptr& serverTransport, - const stdcxx::shared_ptr& inputTransportFactory, - const stdcxx::shared_ptr& outputTransportFactory, - const stdcxx::shared_ptr& inputProtocolFactory, - const stdcxx::shared_ptr& outputProtocolFactory); + const std::shared_ptr& processor, + const std::shared_ptr& serverTransport, + const std::shared_ptr& inputTransportFactory, + const std::shared_ptr& outputTransportFactory, + const std::shared_ptr& inputProtocolFactory, + const std::shared_ptr& outputProtocolFactory); - virtual ~TSimpleServer(); + ~TSimpleServer() override; protected: - virtual void onClientConnected(const stdcxx::shared_ptr& pClient) /* override */; - virtual void onClientDisconnected(TConnectedClient* pClient) /* override */; + void onClientConnected(const std::shared_ptr& pClient) override /* override */; + void onClientDisconnected(TConnectedClient* pClient) override /* override */; private: - void setConcurrentClientLimit(int64_t newLimit); // hide + void setConcurrentClientLimit(int64_t newLimit) override; // hide }; } } diff --git a/lib/cpp/src/thrift/server/TThreadPoolServer.cpp b/lib/cpp/src/thrift/server/TThreadPoolServer.cpp index f07ff849d22..121dde3ead1 100644 --- a/lib/cpp/src/thrift/server/TThreadPoolServer.cpp +++ b/lib/cpp/src/thrift/server/TThreadPoolServer.cpp @@ -30,7 +30,7 @@ using apache::thrift::transport::TServerTransport; using apache::thrift::transport::TTransport; using apache::thrift::transport::TTransportException; using apache::thrift::transport::TTransportFactory; -using stdcxx::shared_ptr; +using std::shared_ptr; using std::string; TThreadPoolServer::TThreadPoolServer(const shared_ptr& processorFactory, @@ -91,8 +91,7 @@ TThreadPoolServer::TThreadPoolServer(const shared_ptr& processor, taskExpiration_(0) { } -TThreadPoolServer::~TThreadPoolServer() { -} +TThreadPoolServer::~TThreadPoolServer() = default; void TThreadPoolServer::serve() { TServerFramework::serve(); @@ -115,7 +114,7 @@ void TThreadPoolServer::setTaskExpiration(int64_t value) { taskExpiration_ = value; } -stdcxx::shared_ptr +std::shared_ptr TThreadPoolServer::getThreadManager() const { return threadManager_; } diff --git a/lib/cpp/src/thrift/server/TThreadPoolServer.h b/lib/cpp/src/thrift/server/TThreadPoolServer.h index 94088d5d04a..a9411b86ca7 100644 --- a/lib/cpp/src/thrift/server/TThreadPoolServer.h +++ b/lib/cpp/src/thrift/server/TThreadPoolServer.h @@ -20,7 +20,7 @@ #ifndef _THRIFT_SERVER_TTHREADPOOLSERVER_H_ #define _THRIFT_SERVER_TTHREADPOOLSERVER_H_ 1 -#include +#include #include #include @@ -34,48 +34,48 @@ namespace server { class TThreadPoolServer : public TServerFramework { public: TThreadPoolServer( - const stdcxx::shared_ptr& processorFactory, - const stdcxx::shared_ptr& serverTransport, - const stdcxx::shared_ptr& transportFactory, - const stdcxx::shared_ptr& protocolFactory, - const stdcxx::shared_ptr& threadManager + const std::shared_ptr& processorFactory, + const std::shared_ptr& serverTransport, + const std::shared_ptr& transportFactory, + const std::shared_ptr& protocolFactory, + const std::shared_ptr& threadManager = apache::thrift::concurrency::ThreadManager::newSimpleThreadManager()); TThreadPoolServer( - const stdcxx::shared_ptr& processor, - const stdcxx::shared_ptr& serverTransport, - const stdcxx::shared_ptr& transportFactory, - const stdcxx::shared_ptr& protocolFactory, - const stdcxx::shared_ptr& threadManager + const std::shared_ptr& processor, + const std::shared_ptr& serverTransport, + const std::shared_ptr& transportFactory, + const std::shared_ptr& protocolFactory, + const std::shared_ptr& threadManager = apache::thrift::concurrency::ThreadManager::newSimpleThreadManager()); TThreadPoolServer( - const stdcxx::shared_ptr& processorFactory, - const stdcxx::shared_ptr& serverTransport, - const stdcxx::shared_ptr& inputTransportFactory, - const stdcxx::shared_ptr& outputTransportFactory, - const stdcxx::shared_ptr& inputProtocolFactory, - const stdcxx::shared_ptr& outputProtocolFactory, - const stdcxx::shared_ptr& threadManager + const std::shared_ptr& processorFactory, + const std::shared_ptr& serverTransport, + const std::shared_ptr& inputTransportFactory, + const std::shared_ptr& outputTransportFactory, + const std::shared_ptr& inputProtocolFactory, + const std::shared_ptr& outputProtocolFactory, + const std::shared_ptr& threadManager = apache::thrift::concurrency::ThreadManager::newSimpleThreadManager()); TThreadPoolServer( - const stdcxx::shared_ptr& processor, - const stdcxx::shared_ptr& serverTransport, - const stdcxx::shared_ptr& inputTransportFactory, - const stdcxx::shared_ptr& outputTransportFactory, - const stdcxx::shared_ptr& inputProtocolFactory, - const stdcxx::shared_ptr& outputProtocolFactory, - const stdcxx::shared_ptr& threadManager + const std::shared_ptr& processor, + const std::shared_ptr& serverTransport, + const std::shared_ptr& inputTransportFactory, + const std::shared_ptr& outputTransportFactory, + const std::shared_ptr& inputProtocolFactory, + const std::shared_ptr& outputProtocolFactory, + const std::shared_ptr& threadManager = apache::thrift::concurrency::ThreadManager::newSimpleThreadManager()); - virtual ~TThreadPoolServer(); + ~TThreadPoolServer() override; /** * Post-conditions (return guarantees): * There will be no clients connected. */ - virtual void serve(); + void serve() override; virtual int64_t getTimeout() const; virtual void setTimeout(int64_t value); @@ -83,15 +83,15 @@ class TThreadPoolServer : public TServerFramework { virtual int64_t getTaskExpiration() const; virtual void setTaskExpiration(int64_t value); - virtual stdcxx::shared_ptr getThreadManager() const; + virtual std::shared_ptr getThreadManager() const; protected: - virtual void onClientConnected(const stdcxx::shared_ptr& pClient) /* override */; - virtual void onClientDisconnected(TConnectedClient* pClient) /* override */; + void onClientConnected(const std::shared_ptr& pClient) override /* override */; + void onClientDisconnected(TConnectedClient* pClient) override /* override */; - stdcxx::shared_ptr threadManager_; - boost::atomic timeout_; - boost::atomic taskExpiration_; + std::shared_ptr threadManager_; + std::atomic timeout_; + std::atomic taskExpiration_; }; } diff --git a/lib/cpp/src/thrift/server/TThreadedServer.cpp b/lib/cpp/src/thrift/server/TThreadedServer.cpp index 3fe5aa6fc91..79dcc70f79c 100644 --- a/lib/cpp/src/thrift/server/TThreadedServer.cpp +++ b/lib/cpp/src/thrift/server/TThreadedServer.cpp @@ -18,8 +18,8 @@ */ #include -#include -#include +#include +#include #include namespace apache { @@ -32,8 +32,8 @@ using apache::thrift::concurrency::Thread; using apache::thrift::concurrency::ThreadFactory; using apache::thrift::protocol::TProtocol; using apache::thrift::protocol::TProtocolFactory; -using apache::thrift::stdcxx::make_shared; -using apache::thrift::stdcxx::shared_ptr; +using std::make_shared; +using std::shared_ptr; using apache::thrift::transport::TServerTransport; using apache::thrift::transport::TTransport; using apache::thrift::transport::TTransportException; @@ -89,8 +89,7 @@ TThreadedServer::TThreadedServer(const shared_ptr& processor, threadFactory_(threadFactory) { } -TThreadedServer::~TThreadedServer() { -} +TThreadedServer::~TThreadedServer() = default; void TThreadedServer::serve() { TServerFramework::serve(); @@ -107,7 +106,7 @@ void TThreadedServer::serve() { void TThreadedServer::drainDeadClients() { // we're in a monitor here while (!deadClientMap_.empty()) { - ClientMap::iterator it = deadClientMap_.begin(); + auto it = deadClientMap_.begin(); it->second->join(); deadClientMap_.erase(it); } @@ -125,9 +124,9 @@ void TThreadedServer::onClientConnected(const shared_ptr& pCli void TThreadedServer::onClientDisconnected(TConnectedClient* pClient) { Synchronized sync(clientMonitor_); drainDeadClients(); // use the outgoing thread to do some maintenance on our dead client backlog - ClientMap::iterator it = activeClientMap_.find(pClient); + auto it = activeClientMap_.find(pClient); if (it != activeClientMap_.end()) { - ClientMap::iterator end = it; + auto end = it; deadClientMap_.insert(it, ++end); activeClientMap_.erase(it); } @@ -140,8 +139,7 @@ TThreadedServer::TConnectedClientRunner::TConnectedClientRunner(const shared_ptr : pClient_(pClient) { } -TThreadedServer::TConnectedClientRunner::~TConnectedClientRunner() { -} +TThreadedServer::TConnectedClientRunner::~TConnectedClientRunner() = default; void TThreadedServer::TConnectedClientRunner::run() /* override */ { pClient_->run(); // Run the client diff --git a/lib/cpp/src/thrift/server/TThreadedServer.h b/lib/cpp/src/thrift/server/TThreadedServer.h index 1e0a824d395..756e5a06377 100644 --- a/lib/cpp/src/thrift/server/TThreadedServer.h +++ b/lib/cpp/src/thrift/server/TThreadedServer.h @@ -22,7 +22,7 @@ #include #include -#include +#include #include #include @@ -38,52 +38,52 @@ namespace server { class TThreadedServer : public TServerFramework { public: TThreadedServer( - const stdcxx::shared_ptr& processorFactory, - const stdcxx::shared_ptr& serverTransport, - const stdcxx::shared_ptr& transportFactory, - const stdcxx::shared_ptr& protocolFactory, - const stdcxx::shared_ptr& threadFactory - = stdcxx::shared_ptr( - new apache::thrift::concurrency::PlatformThreadFactory(false))); + const std::shared_ptr& processorFactory, + const std::shared_ptr& serverTransport, + const std::shared_ptr& transportFactory, + const std::shared_ptr& protocolFactory, + const std::shared_ptr& threadFactory + = std::shared_ptr( + new apache::thrift::concurrency::ThreadFactory(false))); TThreadedServer( - const stdcxx::shared_ptr& processor, - const stdcxx::shared_ptr& serverTransport, - const stdcxx::shared_ptr& transportFactory, - const stdcxx::shared_ptr& protocolFactory, - const stdcxx::shared_ptr& threadFactory - = stdcxx::shared_ptr( - new apache::thrift::concurrency::PlatformThreadFactory(false))); + const std::shared_ptr& processor, + const std::shared_ptr& serverTransport, + const std::shared_ptr& transportFactory, + const std::shared_ptr& protocolFactory, + const std::shared_ptr& threadFactory + = std::shared_ptr( + new apache::thrift::concurrency::ThreadFactory(false))); TThreadedServer( - const stdcxx::shared_ptr& processorFactory, - const stdcxx::shared_ptr& serverTransport, - const stdcxx::shared_ptr& inputTransportFactory, - const stdcxx::shared_ptr& outputTransportFactory, - const stdcxx::shared_ptr& inputProtocolFactory, - const stdcxx::shared_ptr& outputProtocolFactory, - const stdcxx::shared_ptr& threadFactory - = stdcxx::shared_ptr( - new apache::thrift::concurrency::PlatformThreadFactory(false))); + const std::shared_ptr& processorFactory, + const std::shared_ptr& serverTransport, + const std::shared_ptr& inputTransportFactory, + const std::shared_ptr& outputTransportFactory, + const std::shared_ptr& inputProtocolFactory, + const std::shared_ptr& outputProtocolFactory, + const std::shared_ptr& threadFactory + = std::shared_ptr( + new apache::thrift::concurrency::ThreadFactory(false))); TThreadedServer( - const stdcxx::shared_ptr& processor, - const stdcxx::shared_ptr& serverTransport, - const stdcxx::shared_ptr& inputTransportFactory, - const stdcxx::shared_ptr& outputTransportFactory, - const stdcxx::shared_ptr& inputProtocolFactory, - const stdcxx::shared_ptr& outputProtocolFactory, - const stdcxx::shared_ptr& threadFactory - = stdcxx::shared_ptr( - new apache::thrift::concurrency::PlatformThreadFactory(false))); - - virtual ~TThreadedServer(); + const std::shared_ptr& processor, + const std::shared_ptr& serverTransport, + const std::shared_ptr& inputTransportFactory, + const std::shared_ptr& outputTransportFactory, + const std::shared_ptr& inputProtocolFactory, + const std::shared_ptr& outputProtocolFactory, + const std::shared_ptr& threadFactory + = std::shared_ptr( + new apache::thrift::concurrency::ThreadFactory(false))); + + ~TThreadedServer() override; /** * Post-conditions (return guarantees): * There will be no clients connected. */ - virtual void serve(); + void serve() override; protected: /** @@ -95,14 +95,14 @@ class TThreadedServer : public TServerFramework { /** * Implementation of TServerFramework::onClientConnected */ - virtual void onClientConnected(const stdcxx::shared_ptr& pClient) /* override */; + void onClientConnected(const std::shared_ptr& pClient) override /* override */; /** * Implementation of TServerFramework::onClientDisconnected */ - virtual void onClientDisconnected(TConnectedClient *pClient) /* override */; + void onClientDisconnected(TConnectedClient *pClient) override /* override */; - stdcxx::shared_ptr threadFactory_; + std::shared_ptr threadFactory_; /** * A helper wrapper used to wrap the client in something we can use to maintain @@ -114,16 +114,16 @@ class TThreadedServer : public TServerFramework { class TConnectedClientRunner : public apache::thrift::concurrency::Runnable { public: - TConnectedClientRunner(const stdcxx::shared_ptr& pClient); - virtual ~TConnectedClientRunner(); - void run() /* override */; + TConnectedClientRunner(const std::shared_ptr& pClient); + ~TConnectedClientRunner() override; + void run() override /* override */; private: - stdcxx::shared_ptr pClient_; + std::shared_ptr pClient_; }; apache::thrift::concurrency::Monitor clientMonitor_; - typedef std::map > ClientMap; + typedef std::map > ClientMap; /** * A map of active clients diff --git a/lib/cpp/src/thrift/stdcxx.h b/lib/cpp/src/thrift/stdcxx.h deleted file mode 100644 index c8cabf52035..00000000000 --- a/lib/cpp/src/thrift/stdcxx.h +++ /dev/null @@ -1,129 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -#ifndef _THRIFT_STDCXX_H_ -#define _THRIFT_STDCXX_H_ 1 - -#include -#include - -/////////////////////////////////////////////////////////////////// -// -// functional (function, bind) -// -/////////////////////////////////////////////////////////////////// - -#if defined(BOOST_NO_CXX11_HDR_FUNCTIONAL) || (defined(_MSC_VER) && _MSC_VER < 1800) || defined(FORCE_BOOST_FUNCTIONAL) -#if (BOOST_VERSION <= 106500) -#include -#else -#include -#endif -#define _THRIFT_FUNCTIONAL_TR1_ 1 -#endif - -#if _THRIFT_FUNCTIONAL_TR1_ - - namespace apache { namespace thrift { namespace stdcxx { - - using ::std::tr1::bind; - using ::std::tr1::function; - - namespace placeholders { - using ::std::tr1::placeholders::_1; - using ::std::tr1::placeholders::_2; - using ::std::tr1::placeholders::_3; - using ::std::tr1::placeholders::_4; - using ::std::tr1::placeholders::_5; - using ::std::tr1::placeholders::_6; - using ::std::tr1::placeholders::_7; - using ::std::tr1::placeholders::_8; - using ::std::tr1::placeholders::_9; - } // apache::thrift::stdcxx::placeholders - }}} // apache::thrift::stdcxx - -#else - - #include - - namespace apache { namespace thrift { namespace stdcxx { - using ::std::bind; - using ::std::function; - - namespace placeholders { - using ::std::placeholders::_1; - using ::std::placeholders::_2; - using ::std::placeholders::_3; - using ::std::placeholders::_4; - using ::std::placeholders::_5; - using ::std::placeholders::_6; - using ::std::placeholders::_7; - using ::std::placeholders::_8; - using ::std::placeholders::_9; - } // apache::thrift::stdcxx::placeholders - }}} // apache::thrift::stdcxx - -#endif - -/////////////////////////////////////////////////////////////////// -// -// Smart Pointers -// -/////////////////////////////////////////////////////////////////// - -// We can use std for memory functions only if the compiler supports template aliasing -// The macro BOOST_NO_CXX11_SMART_PTR is defined as 1 under Visual Studio 2010 and 2012 -// which do not support the feature, so we must continue to use C++98 and boost on them. -// We cannot use __cplusplus to detect this either, since Microsoft advertises an older one. - -#if defined(BOOST_NO_CXX11_SMART_PTR) || (defined(_MSC_VER) && _MSC_VER < 1800) || defined(FORCE_BOOST_SMART_PTR) -#include -#else -#include -#endif - -namespace apache { namespace thrift { namespace stdcxx { - -#if defined(BOOST_NO_CXX11_SMART_PTR) || (defined(_MSC_VER) && _MSC_VER < 1800) || defined(FORCE_BOOST_SMART_PTR) - - using ::boost::const_pointer_cast; - using ::boost::dynamic_pointer_cast; - using ::boost::enable_shared_from_this; - using ::boost::make_shared; - using ::boost::scoped_ptr; - using ::boost::shared_ptr; - using ::boost::static_pointer_cast; - using ::boost::weak_ptr; - -#else - - using ::std::const_pointer_cast; - using ::std::dynamic_pointer_cast; - using ::std::enable_shared_from_this; - using ::std::make_shared; - template using scoped_ptr = std::unique_ptr; // compiler must support template aliasing - using ::std::shared_ptr; - using ::std::static_pointer_cast; - using ::std::weak_ptr; - -#endif - -}}} // apache::thrift::stdcxx - -#endif // #ifndef _THRIFT_STDCXX_H_ diff --git a/lib/cpp/src/thrift/thrift_export.h b/lib/cpp/src/thrift/thrift_export.h new file mode 100644 index 00000000000..f5c059fb7c2 --- /dev/null +++ b/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/lib/cpp/src/thrift/transport/PlatformSocket.h b/lib/cpp/src/thrift/transport/PlatformSocket.h index 95910580613..10df9446e3d 100644 --- a/lib/cpp/src/thrift/transport/PlatformSocket.h +++ b/lib/cpp/src/thrift/transport/PlatformSocket.h @@ -70,16 +70,10 @@ # 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_POLL WSAPoll +# define THRIFT_POLLFD pollfd +# define THRIFT_POLLIN POLLIN +# define THRIFT_POLLOUT POLLOUT # define THRIFT_SHUT_RDWR SD_BOTH # if !defined(AI_ADDRCONFIG) # define AI_ADDRCONFIG 0x00000400 diff --git a/lib/cpp/src/thrift/transport/SocketCommon.cpp b/lib/cpp/src/thrift/transport/SocketCommon.cpp new file mode 100644 index 00000000000..570f39a9397 --- /dev/null +++ b/lib/cpp/src/thrift/transport/SocketCommon.cpp @@ -0,0 +1,85 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + * @author: David Suárez + */ + +#ifndef THRIFT_SOCKETCOMMON_H +#define THRIFT_SOCKETCOMMON_H + +#ifndef _WIN32 + +#include + +#ifdef HAVE_UNISTD_H +#include +#endif + +#ifdef HAVE_SYS_UN_H +#include +#endif + +#include + +#include +#include +#include + +namespace apache { +namespace thrift { +namespace transport { + + +socklen_t fillUnixSocketAddr(struct sockaddr_un& address, std::string& path) +{ + // abstract namespace socket ? + bool isAbstractNamespace = path[0] == 0; + +#ifndef __linux__ + if (isAbstractNamespace) { + 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 + + /* + * For abstract namespace sockets, the path string is not null-terminated (as opposite to path based), so we + * rely in pass the string size, to the bind() call. + */ + size_t addr_len = isAbstractNamespace ? path.size() : path.size() + 1; + + if (addr_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"); + } + + address.sun_family = AF_UNIX; + memcpy(address.sun_path, path.c_str(), addr_len); + + return static_cast(sizeof((sockaddr_un*)nullptr)->sun_family + addr_len); +} + +} +} +} // apache::thrift::transport + +#endif // _WIN32 + +#endif //THRIFT_SOCKETCOMMON_H diff --git a/compiler/cpp/src/thrift/plugin/plugin.h b/lib/cpp/src/thrift/transport/SocketCommon.h similarity index 65% rename from compiler/cpp/src/thrift/plugin/plugin.h rename to lib/cpp/src/thrift/transport/SocketCommon.h index 705cd41ffae..78839c4e341 100644 --- a/compiler/cpp/src/thrift/plugin/plugin.h +++ b/lib/cpp/src/thrift/transport/SocketCommon.h @@ -15,30 +15,37 @@ * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. + * + * @author: David Suárez */ -#ifndef T_PLUGIN_PLUGIN_H -#define T_PLUGIN_PLUGIN_H +#ifndef THRIFT_SOCKETCOMMON_H +#define THRIFT_SOCKETCOMMON_H + +#ifndef _WIN32 -#include "thrift/Thrift.h" +#include -class t_program; +#ifdef HAVE_UNISTD_H +#include +#endif + +#ifdef HAVE_SYS_UN_H +#include +#endif + +#include namespace apache { namespace thrift { -namespace plugin { +namespace transport { -struct ThriftPluginError : public apache::thrift::TException { - ThriftPluginError(const std::string& msg) : apache::thrift::TException(msg) {} -}; +socklen_t fillUnixSocketAddr(struct sockaddr_un& address, std::string& path); -class GeneratorPlugin { -public: - int exec(int argc, char* argv[]); - virtual int generate(::t_program*, const std::map&) = 0; -}; -} } } +} // apache::thrift::transport -#endif +#endif // _WIN32 + +#endif //THRIFT_SOCKETCOMMON_H diff --git a/lib/cpp/src/thrift/transport/TBufferTransports.cpp b/lib/cpp/src/thrift/transport/TBufferTransports.cpp index 9ac2f844b90..45c0c9bf941 100644 --- a/lib/cpp/src/thrift/transport/TBufferTransports.cpp +++ b/lib/cpp/src/thrift/transport/TBufferTransports.cpp @@ -29,7 +29,7 @@ namespace thrift { namespace transport { uint32_t TBufferedTransport::readSlow(uint8_t* buf, uint32_t len) { - uint32_t have = static_cast(rBound_ - rBase_); + auto have = static_cast(rBound_ - rBase_); // We should only take the slow path if we can't satisfy the read // with the data already in the buffer. @@ -61,8 +61,8 @@ uint32_t TBufferedTransport::readSlow(uint8_t* buf, uint32_t len) { } void TBufferedTransport::writeSlow(const uint8_t* buf, uint32_t len) { - uint32_t have_bytes = static_cast(wBase_ - wBuf_.get()); - uint32_t space = static_cast(wBound_ - wBase_); + auto have_bytes = static_cast(wBase_ - wBuf_.get()); + auto space = static_cast(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(len)); @@ -112,14 +112,15 @@ void TBufferedTransport::writeSlow(const uint8_t* buf, uint32_t len) { 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 + // Simply return nullptr. We don't know if there is actually data available on // the underlying transport, so calling read() might block. - return NULL; + return nullptr; } void TBufferedTransport::flush() { + resetConsumedMessageSize(); // Write out any data waiting in the write buffer. - uint32_t have_bytes = static_cast(wBase_ - wBuf_.get()); + auto have_bytes = static_cast(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) @@ -134,7 +135,7 @@ void TBufferedTransport::flush() { uint32_t TFramedTransport::readSlow(uint8_t* buf, uint32_t len) { uint32_t want = len; - uint32_t have = static_cast(rBound_ - rBase_); + auto have = static_cast(rBound_ - rBase_); // We should only take the slow path if we can't satisfy the read // with the data already in the buffer. @@ -217,7 +218,7 @@ bool TFramedTransport::readFrame() { void TFramedTransport::writeSlow(const uint8_t* buf, uint32_t len) { // Double buffer size until sufficient. - uint32_t have = static_cast(wBase_ - wBuf_.get()); + auto have = static_cast(wBase_ - wBuf_.get()); uint32_t new_size = wBufSize_; if (len + have < have /* overflow */ || len + have > 0x7fffffff) { throw TTransportException(TTransportException::BAD_ARGS, @@ -231,7 +232,7 @@ void TFramedTransport::writeSlow(const uint8_t* buf, uint32_t len) { // so we can use realloc here. // Allocate new buffer. - uint8_t* new_buf = new uint8_t[new_size]; + auto* new_buf = new uint8_t[new_size]; // Copy the old buffer to the new one. memcpy(new_buf, wBuf_.get(), have); @@ -248,6 +249,7 @@ void TFramedTransport::writeSlow(const uint8_t* buf, uint32_t len) { } void TFramedTransport::flush() { + resetConsumedMessageSize(); int32_t sz_hbo, sz_nbo; assert(wBufSize_ > sizeof(sz_nbo)); @@ -292,12 +294,12 @@ const uint8_t* TFramedTransport::borrowSlow(uint8_t* buf, uint32_t* 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 NULL; + return nullptr; } uint32_t TFramedTransport::readEnd() { // include framing bytes - uint32_t bytes_read = static_cast(rBound_ - rBuf_.get() + sizeof(uint32_t)); + auto bytes_read = static_cast(rBound_ - rBuf_.get() + sizeof(uint32_t)); if (rBufSize_ > bufReclaimThresh_) { rBufSize_ = 0; @@ -335,7 +337,7 @@ uint32_t TMemoryBuffer::readSlow(uint8_t* buf, uint32_t len) { uint32_t TMemoryBuffer::readAppendToString(std::string& str, uint32_t len) { // Don't get some stupid assertion failure. - if (buffer_ == NULL) { + if (buffer_ == nullptr) { return 0; } @@ -361,19 +363,21 @@ void TMemoryBuffer::ensureCanWrite(uint32_t len) { } // 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(new_size) - bufferSize_); + const uint32_t current_used = bufferSize_ - avail; + const uint32_t required_buffer_size = len + current_used; + if (required_buffer_size > maxBufferSize_) { + throw TTransportException(TTransportException::BAD_ARGS, + "Internal buffer size overflow when requesting a buffer of size " + std::to_string(required_buffer_size)); } + // Always grow to the next bigger power of two: + const double suggested_buffer_size = std::exp2(std::ceil(std::log2(required_buffer_size))); + // Unless the power of two exceeds maxBufferSize_: + const uint64_t new_size = static_cast((std::min)(suggested_buffer_size, static_cast(maxBufferSize_))); + // Allocate into a new pointer so we don't bork ours if it fails. - uint8_t* new_buffer = static_cast(std::realloc(buffer_, new_size)); - if (new_buffer == NULL) { + auto* new_buffer = static_cast(std::realloc(buffer_, static_cast(new_size))); + if (new_buffer == nullptr) { throw std::bad_alloc(); } @@ -381,6 +385,7 @@ void TMemoryBuffer::ensureCanWrite(uint32_t len) { rBound_ = new_buffer + (rBound_ - buffer_); wBase_ = new_buffer + (wBase_ - buffer_); wBound_ = new_buffer + new_size; + // Note: with realloc() we do not need to free the previous buffer: buffer_ = new_buffer; bufferSize_ = static_cast(new_size); } @@ -408,7 +413,7 @@ const uint8_t* TMemoryBuffer::borrowSlow(uint8_t* buf, uint32_t* len) { *len = available_read(); return rBase_; } - return NULL; + return nullptr; } } } diff --git a/lib/cpp/src/thrift/transport/TBufferTransports.h b/lib/cpp/src/thrift/transport/TBufferTransports.h index 79aace625f8..179934ba054 100644 --- a/lib/cpp/src/thrift/transport/TBufferTransports.h +++ b/lib/cpp/src/thrift/transport/TBufferTransports.h @@ -62,6 +62,7 @@ class TBufferBase : public TVirtualTransport { * This method is meant to eventually be nonvirtual and inlinable. */ uint32_t read(uint8_t* buf, uint32_t len) { + checkReadBytesAvailable(len); uint8_t* new_rBase = rBase_ + len; if (TDB_LIKELY(new_rBase <= rBound_)) { std::memcpy(buf, rBase_, len); @@ -120,6 +121,7 @@ class TBufferBase : public TVirtualTransport { * Consume doesn't require a slow path. */ void consume(uint32_t len) { + countConsumedMessageBytes(len); if (TDB_LIKELY(static_cast(len) <= rBound_ - rBase_)) { rBase_ += len; } else { @@ -137,7 +139,7 @@ class TBufferBase : public TVirtualTransport { /** * Slow path borrow. * - * POSTCONDITION: return == NULL || rBound_ - rBase_ >= *len + * POSTCONDITION: return == nullptr || rBound_ - rBase_ >= *len */ virtual const uint8_t* borrowSlow(uint8_t* buf, uint32_t* len) = 0; @@ -148,7 +150,8 @@ class TBufferBase : public TVirtualTransport { * performance-sensitive operation, so it is okay to just leave it to * the concrete class to set up pointers correctly. */ - TBufferBase() : rBase_(NULL), rBound_(NULL), wBase_(NULL), wBound_(NULL) {} + TBufferBase(std::shared_ptr config = nullptr) + : TVirtualTransport(config), rBase_(nullptr), rBound_(nullptr), wBase_(nullptr), wBound_(nullptr) {} /// Convenience mutator for setting the read buffer. void setReadBuffer(uint8_t* buf, uint32_t len) { @@ -162,7 +165,7 @@ class TBufferBase : public TVirtualTransport { wBound_ = buf + len; } - virtual ~TBufferBase() {} + ~TBufferBase() override = default; /// Reads begin here. uint8_t* rBase_; @@ -186,8 +189,9 @@ class TBufferedTransport : public TVirtualTransport transport) - : transport_(transport), + TBufferedTransport(std::shared_ptr transport, std::shared_ptr config = nullptr) + : TVirtualTransport(config), + transport_(transport), rBufSize_(DEFAULT_BUFFER_SIZE), wBufSize_(DEFAULT_BUFFER_SIZE), rBuf_(new uint8_t[rBufSize_]), @@ -196,8 +200,9 @@ class TBufferedTransport : public TVirtualTransport transport, uint32_t sz) - : transport_(transport), + TBufferedTransport(std::shared_ptr transport, uint32_t sz, std::shared_ptr config = nullptr) + : TVirtualTransport(config), + transport_(transport), rBufSize_(sz), wBufSize_(sz), rBuf_(new uint8_t[rBufSize_]), @@ -206,8 +211,10 @@ class TBufferedTransport : public TVirtualTransport transport, uint32_t rsz, uint32_t wsz) - : transport_(transport), + TBufferedTransport(std::shared_ptr transport, uint32_t rsz, uint32_t wsz, + std::shared_ptr config = nullptr) + : TVirtualTransport(config), + transport_(transport), rBufSize_(rsz), wBufSize_(wsz), rBuf_(new uint8_t[rBufSize_]), @@ -215,37 +222,37 @@ class TBufferedTransport : public TVirtualTransportopen(); } + void open() override { transport_->open(); } - bool isOpen() { return transport_->isOpen(); } + bool isOpen() const override { return transport_->isOpen(); } - bool peek() { + bool peek() override { if (rBase_ == rBound_) { setReadBuffer(rBuf_.get(), transport_->read(rBuf_.get(), rBufSize_)); } return (rBound_ > rBase_); } - void close() { + void close() override { flush(); transport_->close(); } - virtual uint32_t readSlow(uint8_t* buf, uint32_t len); + uint32_t readSlow(uint8_t* buf, uint32_t len) override; - virtual void writeSlow(const uint8_t* buf, uint32_t len); + void writeSlow(const uint8_t* buf, uint32_t len) override; - void flush(); + void flush() override; /** * Returns the origin of the underlying transport */ - virtual const std::string getOrigin() { return transport_->getOrigin(); } + 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. + * 1/ If len is at most rBufSize_, borrow will never return nullptr. * Depending on the underlying transport, it could throw an exception * or hang forever. * 2/ Some borrow requests may copy bytes internally. However, @@ -253,9 +260,9 @@ class TBufferedTransport : public TVirtualTransport getUnderlyingTransport() { return transport_; } + std::shared_ptr getUnderlyingTransport() { return transport_; } /* * TVirtualTransport provides a default implementation of readAll(). @@ -270,7 +277,7 @@ class TBufferedTransport : public TVirtualTransport transport_; + std::shared_ptr transport_; uint32_t rBufSize_; uint32_t wBufSize_; @@ -284,15 +291,15 @@ class TBufferedTransport : public TVirtualTransport getTransport(stdcxx::shared_ptr trans) { - return stdcxx::shared_ptr(new TBufferedTransport(trans)); + std::shared_ptr getTransport(std::shared_ptr trans) override { + return std::shared_ptr(new TBufferedTransport(trans)); } }; @@ -309,8 +316,9 @@ class TFramedTransport : public TVirtualTransport static const int DEFAULT_MAX_FRAME_SIZE = 256 * 1024 * 1024; /// Use default buffer sizes. - TFramedTransport() - : transport_(), + TFramedTransport(std::shared_ptr config = nullptr) + : TVirtualTransport(config), + transport_(), rBufSize_(0), wBufSize_(DEFAULT_BUFFER_SIZE), rBuf_(), @@ -319,54 +327,57 @@ class TFramedTransport : public TVirtualTransport initPointers(); } - TFramedTransport(stdcxx::shared_ptr transport) - : transport_(transport), + TFramedTransport(std::shared_ptr transport, std::shared_ptr config = nullptr) + : TVirtualTransport(config), + transport_(transport), rBufSize_(0), wBufSize_(DEFAULT_BUFFER_SIZE), rBuf_(), wBuf_(new uint8_t[wBufSize_]), bufReclaimThresh_((std::numeric_limits::max)()), - maxFrameSize_(DEFAULT_MAX_FRAME_SIZE) { + maxFrameSize_(configuration_->getMaxFrameSize()) { initPointers(); } - TFramedTransport(stdcxx::shared_ptr transport, + TFramedTransport(std::shared_ptr transport, uint32_t sz, - uint32_t bufReclaimThresh = (std::numeric_limits::max)()) - : transport_(transport), + uint32_t bufReclaimThresh = (std::numeric_limits::max)(), + std::shared_ptr config = nullptr) + : TVirtualTransport(config), + transport_(transport), rBufSize_(0), wBufSize_(sz), rBuf_(), wBuf_(new uint8_t[wBufSize_]), bufReclaimThresh_(bufReclaimThresh), - maxFrameSize_(DEFAULT_MAX_FRAME_SIZE) { + maxFrameSize_(configuration_->getMaxFrameSize()) { initPointers(); } - void open() { transport_->open(); } + void open() override { transport_->open(); } - bool isOpen() { return transport_->isOpen(); } + bool isOpen() const override { return transport_->isOpen(); } - bool peek() { return (rBase_ < rBound_) || transport_->peek(); } + bool peek() override { return (rBase_ < rBound_) || transport_->peek(); } - void close() { + void close() override { flush(); transport_->close(); } - virtual uint32_t readSlow(uint8_t* buf, uint32_t len); + uint32_t readSlow(uint8_t* buf, uint32_t len) override; - virtual void writeSlow(const uint8_t* buf, uint32_t len); + void writeSlow(const uint8_t* buf, uint32_t len) override; - virtual void flush(); + void flush() override; - uint32_t readEnd(); + uint32_t readEnd() override; - uint32_t writeEnd(); + uint32_t writeEnd() override; - const uint8_t* borrowSlow(uint8_t* buf, uint32_t* len); + const uint8_t* borrowSlow(uint8_t* buf, uint32_t* len) override; - stdcxx::shared_ptr getUnderlyingTransport() { return transport_; } + std::shared_ptr getUnderlyingTransport() { return transport_; } /* * TVirtualTransport provides a default implementation of readAll(). @@ -377,7 +388,7 @@ class TFramedTransport : public TVirtualTransport /** * Returns the origin of the underlying transport */ - virtual const std::string getOrigin() { return transport_->getOrigin(); } + const std::string getOrigin() const override { return transport_->getOrigin(); } /** * Set the maximum size of the frame at read @@ -399,7 +410,7 @@ class TFramedTransport : public TVirtualTransport virtual bool readFrame(); void initPointers() { - setReadBuffer(NULL, 0); + setReadBuffer(nullptr, 0); setWriteBuffer(wBuf_.get(), wBufSize_); // Pad the buffer so we can insert the size later. @@ -407,7 +418,7 @@ class TFramedTransport : public TVirtualTransport this->write((uint8_t*)&pad, sizeof(pad)); } - stdcxx::shared_ptr transport_; + std::shared_ptr transport_; uint32_t rBufSize_; uint32_t wBufSize_; @@ -423,15 +434,15 @@ class TFramedTransport : public TVirtualTransport */ class TFramedTransportFactory : public TTransportFactory { public: - TFramedTransportFactory() {} + TFramedTransportFactory() = default; - virtual ~TFramedTransportFactory() {} + ~TFramedTransportFactory() override = default; /** * Wraps the transport into a framed one. */ - virtual stdcxx::shared_ptr getTransport(stdcxx::shared_ptr trans) { - return stdcxx::shared_ptr(new TFramedTransport(trans)); + std::shared_ptr getTransport(std::shared_ptr trans) override { + return std::shared_ptr(new TFramedTransport(trans)); } }; @@ -451,10 +462,10 @@ class TMemoryBuffer : public TVirtualTransport { maxBufferSize_ = (std::numeric_limits::max)(); - if (buf == NULL && size != 0) { + if (buf == nullptr && size != 0) { assert(owner); buf = (uint8_t*)std::malloc(size); - if (buf == NULL) { + if (buf == nullptr) { throw std::bad_alloc(); } } @@ -503,7 +514,10 @@ class TMemoryBuffer : public TVirtualTransport { * Construct a TMemoryBuffer with a default-sized buffer, * owned by the TMemoryBuffer object. */ - TMemoryBuffer() { initCommon(NULL, defaultSize, true, 0); } + TMemoryBuffer(std::shared_ptr config = nullptr) + : TVirtualTransport(config) { + initCommon(nullptr, defaultSize, true, 0); + } /** * Construct a TMemoryBuffer with a buffer of a specified size, @@ -511,7 +525,10 @@ class TMemoryBuffer : public TVirtualTransport { * * @param sz The initial size of the buffer. */ - TMemoryBuffer(uint32_t sz) { initCommon(NULL, sz, true, 0); } + TMemoryBuffer(uint32_t sz, std::shared_ptr config = nullptr) + : TVirtualTransport(config) { + initCommon(nullptr, sz, true, 0); + } /** * Construct a TMemoryBuffer with buf as its initial contents. @@ -523,8 +540,9 @@ class TMemoryBuffer : public TVirtualTransport { * @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 == NULL && sz != 0) { + TMemoryBuffer(uint8_t* buf, uint32_t sz, MemoryPolicy policy = OBSERVE, std::shared_ptr config = nullptr) + : TVirtualTransport(config) { + if (buf == nullptr && sz != 0) { throw TTransportException(TTransportException::BAD_ARGS, "TMemoryBuffer given null buffer with non-zero size."); } @@ -535,7 +553,7 @@ class TMemoryBuffer : public TVirtualTransport { initCommon(buf, sz, policy == TAKE_OWNERSHIP, sz); break; case COPY: - initCommon(NULL, sz, true, 0); + initCommon(nullptr, sz, true, 0); this->write(buf, sz); break; default: @@ -544,19 +562,19 @@ class TMemoryBuffer : public TVirtualTransport { } } - ~TMemoryBuffer() { + ~TMemoryBuffer() override { if (owner_) { std::free(buffer_); } } - bool isOpen() { return true; } + bool isOpen() const override { return true; } - bool peek() { return (rBase_ < wBase_); } + bool peek() override { return (rBase_ < wBase_); } - void open() {} + void open() override {} - void close() {} + void close() override {} // TODO(dreiss): Make bufPtr const. void getBuffer(uint8_t** bufPtr, uint32_t* sz) { @@ -565,7 +583,7 @@ class TMemoryBuffer : public TVirtualTransport { } std::string getBufferAsString() { - if (buffer_ == NULL) { + if (buffer_ == nullptr) { return ""; } uint8_t* buf; @@ -575,7 +593,7 @@ class TMemoryBuffer : public TVirtualTransport { } void appendBufferToString(std::string& str) { - if (buffer_ == NULL) { + if (buffer_ == nullptr) { return; } uint8_t* buf; @@ -634,9 +652,9 @@ class TMemoryBuffer : public TVirtualTransport { uint32_t readAppendToString(std::string& str, uint32_t len); // return number of bytes read - uint32_t readEnd() { + uint32_t readEnd() override { // This cast should be safe, because buffer_'s size is a uint32_t - uint32_t bytes = static_cast(rBase_ - buffer_); + auto bytes = static_cast(rBase_ - buffer_); if (rBase_ == wBase_) { resetBuffer(); } @@ -644,7 +662,7 @@ class TMemoryBuffer : public TVirtualTransport { } // Return number of bytes written - uint32_t writeEnd() { + uint32_t writeEnd() override { // This cast should be safe, because buffer_'s size is a uint32_t return static_cast(wBase_ - buffer_); } @@ -719,11 +737,11 @@ class TMemoryBuffer : public TVirtualTransport { // 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); + uint32_t readSlow(uint8_t* buf, uint32_t len) override; - void writeSlow(const uint8_t* buf, uint32_t len); + void writeSlow(const uint8_t* buf, uint32_t len) override; - const uint8_t* borrowSlow(uint8_t* buf, uint32_t* len); + const uint8_t* borrowSlow(uint8_t* buf, uint32_t* len) override; // Data buffer uint8_t* buffer_; diff --git a/lib/cpp/src/thrift/transport/TFDTransport.cpp b/lib/cpp/src/thrift/transport/TFDTransport.cpp index 93dd1002105..fa7f0dabe2d 100644 --- a/lib/cpp/src/thrift/transport/TFDTransport.cpp +++ b/lib/cpp/src/thrift/transport/TFDTransport.cpp @@ -52,6 +52,7 @@ void TFDTransport::close() { } uint32_t TFDTransport::read(uint8_t* buf, uint32_t len) { + checkReadBytesAvailable(len); unsigned int maxRetries = 5; // same as the TSocket default unsigned int retries = 0; while (true) { diff --git a/lib/cpp/src/thrift/transport/TFDTransport.h b/lib/cpp/src/thrift/transport/TFDTransport.h index 5593d43dff0..fb84c9d8dbe 100644 --- a/lib/cpp/src/thrift/transport/TFDTransport.h +++ b/lib/cpp/src/thrift/transport/TFDTransport.h @@ -40,10 +40,12 @@ class TFDTransport : public TVirtualTransport { 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(int fd, ClosePolicy close_policy = NO_CLOSE_ON_DESTROY, + std::shared_ptr config = nullptr) + : TVirtualTransport(config), fd_(fd), close_policy_(close_policy) { + } - ~TFDTransport() { + ~TFDTransport() override { if (close_policy_ == CLOSE_ON_DESTROY) { try { close(); @@ -53,11 +55,11 @@ class TFDTransport : public TVirtualTransport { } } - bool isOpen() { return fd_ >= 0; } + bool isOpen() const override { return fd_ >= 0; } - void open() {} + void open() override {} - void close(); + void close() override; uint32_t read(uint8_t* buf, uint32_t len); diff --git a/lib/cpp/src/thrift/transport/TFileTransport.cpp b/lib/cpp/src/thrift/transport/TFileTransport.cpp index b08a5f16f2f..08372b3e2ad 100644 --- a/lib/cpp/src/thrift/transport/TFileTransport.cpp +++ b/lib/cpp/src/thrift/transport/TFileTransport.cpp @@ -25,13 +25,6 @@ #include #include -#if (BOOST_VERSION >= 105700) -#include -using boost::movelib::unique_ptr; -#else -#include -using boost::interprocess::unique_ptr; -#endif #ifdef HAVE_SYS_TIME_H #include @@ -49,6 +42,7 @@ using boost::interprocess::unique_ptr; #include #include #include +#include #ifdef HAVE_SYS_STAT_H #include #endif @@ -61,7 +55,7 @@ namespace apache { namespace thrift { namespace transport { -using stdcxx::shared_ptr; +using std::shared_ptr; using std::cerr; using std::cout; using std::endl; @@ -69,10 +63,11 @@ using std::string; using namespace apache::thrift::protocol; using namespace apache::thrift::concurrency; -TFileTransport::TFileTransport(string path, bool readOnly) - : readState_(), - readBuff_(NULL), - currentEvent_(NULL), +TFileTransport::TFileTransport(string path, bool readOnly, std::shared_ptr config) + : TTransport(config), + readState_(), + readBuff_(nullptr), + currentEvent_(nullptr), readBuffSize_(DEFAULT_READ_BUFF_SIZE), readTimeout_(NO_TAIL_READ_TIMEOUT), chunkSize_(DEFAULT_CHUNK_SIZE), @@ -84,8 +79,8 @@ TFileTransport::TFileTransport(string path, bool readOnly) eofSleepTime_(DEFAULT_EOF_SLEEP_TIME_US), corruptedEventSleepTime_(DEFAULT_CORRUPTED_SLEEP_TIME_US), writerThreadIOErrorSleepTime_(DEFAULT_WRITER_THREAD_SLEEP_TIME_US), - dequeueBuffer_(NULL), - enqueueBuffer_(NULL), + dequeueBuffer_(nullptr), + enqueueBuffer_(nullptr), notFull_(&mutex_), notEmpty_(&mutex_), closing_(false), @@ -147,22 +142,22 @@ TFileTransport::~TFileTransport() { if (dequeueBuffer_) { delete dequeueBuffer_; - dequeueBuffer_ = NULL; + dequeueBuffer_ = nullptr; } if (enqueueBuffer_) { delete enqueueBuffer_; - enqueueBuffer_ = NULL; + enqueueBuffer_ = nullptr; } if (readBuff_) { delete[] readBuff_; - readBuff_ = NULL; + readBuff_ = nullptr; } if (currentEvent_) { delete currentEvent_; - currentEvent_ = NULL; + currentEvent_ = nullptr; } // close logfile @@ -203,8 +198,6 @@ void TFileTransport::write(const uint8_t* buf, uint32_t len) { enqueueEvent(buf, len); } -// this is needed until boost 1.57 as the older unique_ptr implementation -// has no default deleter in interprocess template struct uniqueDeleter { @@ -228,7 +221,7 @@ void TFileTransport::enqueueEvent(const uint8_t* buf, uint32_t eventLen) { return; } - unique_ptr > toEnqueue(new eventInfo()); + std::unique_ptr > toEnqueue(new eventInfo()); toEnqueue->eventBuff_ = new uint8_t[(sizeof(uint8_t) * eventLen) + 4]; // first 4 bytes is the event length @@ -272,7 +265,7 @@ void TFileTransport::enqueueEvent(const uint8_t* buf, uint32_t eventLen) { // it is probably a non-factor for the time being } -bool TFileTransport::swapEventBuffers(struct timeval* deadline) { +bool TFileTransport::swapEventBuffers(const std::chrono::time_point *deadline) { bool swap; Guard g(mutex_); @@ -283,9 +276,9 @@ bool TFileTransport::swapEventBuffers(struct timeval* deadline) { // return immediately if the transport is closing swap = false; } else { - if (deadline != NULL) { + if (deadline != nullptr) { // if we were handed a deadline time struct, do a timed wait - notEmpty_.waitForTime(deadline); + notEmpty_.waitForTime(*deadline); } else { // just wait until the buffer gets an item notEmpty_.wait(); @@ -344,8 +337,7 @@ void TFileTransport::writerThread() { } // Figure out the next time by which a flush must take place - struct timeval ts_next_flush; - getNextFlushTime(&ts_next_flush); + auto ts_next_flush = getNextFlushTime(); uint32_t unflushed = 0; while (1) { @@ -371,7 +363,7 @@ void TFileTransport::writerThread() { if (swapEventBuffers(&ts_next_flush)) { eventInfo* outEvent; - while (NULL != (outEvent = dequeueBuffer_->getNext())) { + 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 @@ -431,12 +423,12 @@ void TFileTransport::writerThread() { if (chunk1 != chunk2) { // refetch the offset to keep in sync offset_ = THRIFT_LSEEK(fd_, 0, SEEK_CUR); - int32_t padding = (int32_t)((offset_ / chunkSize_ + 1) * chunkSize_ - offset_); + auto padding = (int32_t)((offset_ / chunkSize_ + 1) * chunkSize_ - offset_); - uint8_t* zeros = new uint8_t[padding]; + auto* zeros = new uint8_t[padding]; memset(zeros, '\0', padding); boost::scoped_array array(zeros); - if (-1 == ::write(fd_, zeros, padding)) { + if (-1 == ::THRIFT_WRITE(fd_, zeros, padding)) { int errno_copy = THRIFT_ERRNO; GlobalOutput.perror("TFileTransport: writerThread() error while padding zeros ", errno_copy); @@ -498,17 +490,13 @@ void TFileTransport::writerThread() { if (forced_flush || unflushed > flushMaxBytes_) { flush = true; } else { - struct timeval current_time; - THRIFT_GETTIMEOFDAY(¤t_time, NULL); - if (current_time.tv_sec > ts_next_flush.tv_sec - || (current_time.tv_sec == ts_next_flush.tv_sec - && current_time.tv_usec > ts_next_flush.tv_usec)) { + 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. - getNextFlushTime(&ts_next_flush); + ts_next_flush = getNextFlushTime(); } } } @@ -517,7 +505,7 @@ void TFileTransport::writerThread() { // sync (force flush) file to disk THRIFT_FSYNC(fd_); unflushed = 0; - getNextFlushTime(&ts_next_flush); + ts_next_flush = getNextFlushTime(); // notify anybody waiting for flush completion if (forced_flush) { @@ -532,6 +520,7 @@ void TFileTransport::writerThread() { } void TFileTransport::flush() { + resetConsumedMessageSize(); // file must be open for writing for any flushing to take place if (!writerThread_.get()) { return; @@ -550,6 +539,7 @@ void TFileTransport::flush() { } uint32_t TFileTransport::readAll(uint8_t* buf, uint32_t len) { + checkReadBytesAvailable(len); uint32_t have = 0; uint32_t get = 0; @@ -581,6 +571,7 @@ bool TFileTransport::peek() { } uint32_t TFileTransport::read(uint8_t* buf, uint32_t len) { + checkReadBytesAvailable(len); // check if there an event is ready to be read if (!currentEvent_) { currentEvent_ = readEvent(); @@ -600,7 +591,7 @@ uint32_t TFileTransport::read(uint8_t* buf, uint32_t len) { memcpy(buf, currentEvent_->eventBuff_ + currentEvent_->eventBuffPos_, remaining); } delete (currentEvent_); - currentEvent_ = NULL; + currentEvent_ = nullptr; return remaining; } @@ -643,12 +634,12 @@ eventInfo* TFileTransport::readEvent() { } else if (readTimeout_ == NO_TAIL_READ_TIMEOUT) { // reset state readState_.resetState(0); - return NULL; + return nullptr; } else if (readTimeout_ > 0) { // timeout already expired once if (readTries > 0) { readState_.resetState(0); - return NULL; + return nullptr; } else { THRIFT_SLEEP_USEC(readTimeout_ * 1000); readTries++; @@ -722,7 +713,7 @@ eventInfo* TFileTransport::readEvent() { eventInfo* completeEvent = readState_.event_; completeEvent->eventBuffPos_ = 0; - readState_.event_ = NULL; + readState_.event_ = nullptr; readState_.resetState(readState_.bufferPtr_); // exit criteria @@ -791,7 +782,7 @@ void TFileTransport::performRecovery() { // pretty hosed at this stage, rewind the file back to the last successful // point and punt on the error readState_.resetState(readState_.lastDispatchPtr_); - currentEvent_ = NULL; + currentEvent_ = nullptr; char errorMsg[1024]; sprintf(errorMsg, "TFileTransport: log file corrupted at offset: %lu", @@ -840,7 +831,7 @@ void TFileTransport::seekToChunk(int32_t chunk) { off_t newOffset = off_t(chunk) * chunkSize_; offset_ = ::THRIFT_LSEEK(fd_, newOffset, SEEK_SET); readState_.resetAllValues(); - currentEvent_ = NULL; + currentEvent_ = nullptr; if (offset_ == -1) { GlobalOutput("TFileTransport: lseek error in seekToChunk"); throw TTransportException("TFileTransport: lseek error in seekToChunk"); @@ -854,7 +845,7 @@ void TFileTransport::seekToChunk(int32_t chunk) { shared_ptr event; while ((offset_ + readState_.bufferPtr_) < minEndOffset) { event.reset(readEvent()); - if (event.get() == NULL) { + if (event.get() == nullptr) { break; } } @@ -916,15 +907,8 @@ void TFileTransport::openLogFile() { } } -void TFileTransport::getNextFlushTime(struct timeval* ts_next_flush) { - THRIFT_GETTIMEOFDAY(ts_next_flush, NULL); - - ts_next_flush->tv_usec += flushMaxUs_; - if (ts_next_flush->tv_usec > 1000000) { - long extra_secs = ts_next_flush->tv_usec / 1000000; - ts_next_flush->tv_usec %= 1000000; - ts_next_flush->tv_sec += extra_secs; - } +std::chrono::time_point TFileTransport::getNextFlushTime() { + return std::chrono::steady_clock::now() + std::chrono::microseconds(flushMaxUs_); } TFileTransportBuffer::TFileTransportBuffer(uint32_t size) @@ -938,7 +922,7 @@ TFileTransportBuffer::~TFileTransportBuffer() { delete buffer_[i]; } delete[] buffer_; - buffer_ = NULL; + buffer_ = nullptr; } } @@ -963,7 +947,7 @@ eventInfo* TFileTransportBuffer::getNext() { return buffer_[readPoint_++]; } else { // no more entries - return NULL; + return nullptr; } } @@ -997,7 +981,7 @@ TFileProcessor::TFileProcessor(shared_ptr processor, inputTransport_(inputTransport) { // default the output transport to a null transport (common case) - outputTransport_ = shared_ptr(new TNullTransport()); + outputTransport_ = std::make_shared(); } TFileProcessor::TFileProcessor(shared_ptr processor, @@ -1010,7 +994,7 @@ TFileProcessor::TFileProcessor(shared_ptr processor, inputTransport_(inputTransport) { // default the output transport to a null transport (common case) - outputTransport_ = shared_ptr(new TNullTransport()); + outputTransport_ = std::make_shared(); } TFileProcessor::TFileProcessor(shared_ptr processor, @@ -1040,7 +1024,7 @@ void TFileProcessor::process(uint32_t numEvents, bool tail) { // bad form to use exceptions for flow control but there is really // no other way around it try { - processor_->process(inputProtocol, outputProtocol, NULL); + processor_->process(inputProtocol, outputProtocol, nullptr); numProcessed++; if ((numEvents > 0) && (numProcessed == numEvents)) { return; @@ -1071,7 +1055,7 @@ void TFileProcessor::processChunk() { // bad form to use exceptions for flow control but there is really // no other way around it try { - processor_->process(inputProtocol, outputProtocol, NULL); + processor_->process(inputProtocol, outputProtocol, nullptr); if (curChunk != inputTransport_->getCurChunk()) { break; } diff --git a/lib/cpp/src/thrift/transport/TFileTransport.h b/lib/cpp/src/thrift/transport/TFileTransport.h index d6da4363e1b..608cff18411 100644 --- a/lib/cpp/src/thrift/transport/TFileTransport.h +++ b/lib/cpp/src/thrift/transport/TFileTransport.h @@ -24,15 +24,13 @@ #include #include +#include #include #include -#include -#include - #include #include -#include +#include #include namespace apache { @@ -50,7 +48,7 @@ typedef struct eventInfo { uint32_t eventSize_; uint32_t eventBuffPos_; - eventInfo() : eventBuff_(NULL), eventSize_(0), eventBuffPos_(0){}; + eventInfo() : eventBuff_(nullptr), eventSize_(0), eventBuffPos_(0){}; ~eventInfo() { if (eventBuff_) { delete[] eventBuff_; @@ -87,7 +85,7 @@ typedef struct readState { if (event_) { delete (event_); } - event_ = 0; + event_ = nullptr; } inline uint32_t getEventSize() { @@ -96,7 +94,7 @@ typedef struct readState { } readState() { - event_ = 0; + event_ = nullptr; resetAllValues(); } @@ -175,25 +173,25 @@ class TFileWriterTransport : virtual public TTransport { */ class TFileTransport : public TFileReaderTransport, public TFileWriterTransport { public: - TFileTransport(std::string path, bool readOnly = false); - ~TFileTransport(); + TFileTransport(std::string path, bool readOnly = false, std::shared_ptr config = nullptr); + ~TFileTransport() override; // TODO: what is the correct behaviour for this? // the log file is generally always open - bool isOpen() { return true; } + bool isOpen() const override { return true; } void write(const uint8_t* buf, uint32_t len); - void flush(); + void flush() override; uint32_t readAll(uint8_t* buf, uint32_t len); uint32_t read(uint8_t* buf, uint32_t len); - bool peek(); + bool peek() override; // log-file specific functions - void seekToChunk(int32_t chunk); - void seekToEnd(); - uint32_t getNumChunks(); - uint32_t getCurChunk(); + 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); @@ -208,15 +206,15 @@ class TFileTransport : public TFileReaderTransport, public TFileWriterTransport static const int32_t TAIL_READ_TIMEOUT = -1; static const int32_t NO_TAIL_READ_TIMEOUT = 0; - void setReadTimeout(int32_t readTimeout) { readTimeout_ = readTimeout; } - int32_t getReadTimeout() { return readTimeout_; } + void setReadTimeout(int32_t readTimeout) override { readTimeout_ = readTimeout; } + int32_t getReadTimeout() override { return readTimeout_; } - void setChunkSize(uint32_t chunkSize) { + void setChunkSize(uint32_t chunkSize) override { if (chunkSize) { chunkSize_ = chunkSize; } } - uint32_t getChunkSize() { return chunkSize_; } + uint32_t getChunkSize() override { return chunkSize_; } void setEventBufferSize(uint32_t bufferSize) { if (bufferAndThreadInitialized_) { @@ -262,20 +260,20 @@ class TFileTransport : public TFileReaderTransport, public TFileWriterTransport * We cannot use TVirtualTransport to provide these, since we need to inherit * virtually from TTransport. */ - virtual uint32_t read_virt(uint8_t* buf, uint32_t len) { return this->read(buf, len); } - virtual uint32_t readAll_virt(uint8_t* buf, uint32_t len) { return this->readAll(buf, len); } - virtual void write_virt(const uint8_t* buf, uint32_t len) { this->write(buf, len); } + 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(struct timeval* deadline); + bool swapEventBuffers(const std::chrono::time_point *deadline); bool initBufferAndWriteThread(); // control for writer thread static void* startWriterThread(void* ptr) { static_cast(ptr)->writerThread(); - return NULL; + return nullptr; } void writerThread(); @@ -288,7 +286,7 @@ class TFileTransport : public TFileReaderTransport, public TFileWriterTransport // Utility functions void openLogFile(); - void getNextFlushTime(struct timeval* ts_next_flush); + std::chrono::time_point getNextFlushTime(); // Class variables readState readState_; @@ -338,8 +336,8 @@ class TFileTransport : public TFileReaderTransport, public TFileWriterTransport static const uint32_t DEFAULT_WRITER_THREAD_SLEEP_TIME_US = 60 * 1000 * 1000; // writer thread - apache::thrift::concurrency::PlatformThreadFactory threadFactory_; - stdcxx::shared_ptr writerThread_; + apache::thrift::concurrency::ThreadFactory threadFactory_; + std::shared_ptr 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. @@ -348,11 +346,11 @@ class TFileTransport : public TFileReaderTransport, public TFileWriterTransport // conditions used to block when the buffer is full or empty Monitor notFull_, notEmpty_; - boost::atomic closing_; + std::atomic closing_; // To keep track of whether the buffer has been flushed Monitor flushed_; - boost::atomic forceFlush_; + std::atomic forceFlush_; // Mutex that is grabbed when enqueueing and swapping the read/write buffers Mutex mutex_; @@ -390,14 +388,14 @@ class TFileProcessor { * @param protocolFactory protocol factory * @param inputTransport file transport */ - TFileProcessor(stdcxx::shared_ptr processor, - stdcxx::shared_ptr protocolFactory, - stdcxx::shared_ptr inputTransport); + TFileProcessor(std::shared_ptr processor, + std::shared_ptr protocolFactory, + std::shared_ptr inputTransport); - TFileProcessor(stdcxx::shared_ptr processor, - stdcxx::shared_ptr inputProtocolFactory, - stdcxx::shared_ptr outputProtocolFactory, - stdcxx::shared_ptr inputTransport); + TFileProcessor(std::shared_ptr processor, + std::shared_ptr inputProtocolFactory, + std::shared_ptr outputProtocolFactory, + std::shared_ptr inputTransport); /** * Constructor @@ -407,10 +405,10 @@ class TFileProcessor { * @param inputTransport input file transport * @param output output transport */ - TFileProcessor(stdcxx::shared_ptr processor, - stdcxx::shared_ptr protocolFactory, - stdcxx::shared_ptr inputTransport, - stdcxx::shared_ptr outputTransport); + TFileProcessor(std::shared_ptr processor, + std::shared_ptr protocolFactory, + std::shared_ptr inputTransport, + std::shared_ptr outputTransport); /** * processes events from the file @@ -427,11 +425,11 @@ class TFileProcessor { void processChunk(); private: - stdcxx::shared_ptr processor_; - stdcxx::shared_ptr inputProtocolFactory_; - stdcxx::shared_ptr outputProtocolFactory_; - stdcxx::shared_ptr inputTransport_; - stdcxx::shared_ptr outputTransport_; + std::shared_ptr processor_; + std::shared_ptr inputProtocolFactory_; + std::shared_ptr outputProtocolFactory_; + std::shared_ptr inputTransport_; + std::shared_ptr outputTransport_; }; } } diff --git a/lib/cpp/src/thrift/transport/THeaderTransport.cpp b/lib/cpp/src/thrift/transport/THeaderTransport.cpp index ea16591a9c7..b3b8333893d 100644 --- a/lib/cpp/src/thrift/transport/THeaderTransport.cpp +++ b/lib/cpp/src/thrift/transport/THeaderTransport.cpp @@ -22,7 +22,6 @@ #include #include #include -#include #include #include @@ -37,7 +36,7 @@ using std::vector; namespace apache { namespace thrift { -using stdcxx::shared_ptr; +using std::shared_ptr; namespace transport { @@ -198,7 +197,7 @@ void THeaderTransport::readHeaderFormat(uint16_t headerSize, uint32_t sz) { readHeaders_.clear(); // Clear out any previous headers. // skip over already processed magic(4), seqId(4), headerSize(2) - uint8_t* ptr = reinterpret_cast(rBuf_.get() + 10); + auto* ptr = reinterpret_cast(rBuf_.get() + 10); // Catch integer overflow, check for reasonable header size if (headerSize >= 16384) { @@ -276,9 +275,9 @@ void THeaderTransport::untransform(uint8_t* ptr, uint32_t sz) { stream.avail_in = sz; // Setting these to 0 means use the default free/alloc functions - stream.zalloc = (alloc_func)0; - stream.zfree = (free_func)0; - stream.opaque = (voidpf)0; + 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, @@ -318,7 +317,7 @@ void THeaderTransport::untransform(uint8_t* ptr, uint32_t sz) { void THeaderTransport::resizeTransformBuffer(uint32_t additionalSize) { if (tBufSize_ < wBufSize_ + DEFAULT_BUFFER_SIZE) { uint32_t new_size = wBufSize_ + DEFAULT_BUFFER_SIZE + additionalSize; - uint8_t* new_buf = new uint8_t[new_size]; + auto* new_buf = new uint8_t[new_size]; tBuf_.reset(new_buf); tBufSize_ = new_size; } @@ -338,9 +337,9 @@ void THeaderTransport::transform(uint8_t* ptr, uint32_t sz) { stream.next_in = ptr; stream.avail_in = sz; - stream.zalloc = (alloc_func)0; - stream.zfree = (free_func)0; - stream.opaque = (voidpf)0; + 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, @@ -390,7 +389,7 @@ uint32_t THeaderTransport::getWriteBytes() { * Automatically advances ptr to after the written portion */ void THeaderTransport::writeString(uint8_t*& ptr, const string& str) { - int32_t strLen = safe_numeric_cast(str.length()); + auto strLen = safe_numeric_cast(str.length()); ptr += writeVarint32(strLen, ptr); memcpy(ptr, str.c_str(), strLen); // no need to write \0 ptr += strLen; @@ -416,6 +415,7 @@ void THeaderTransport::clearHeaders() { } void THeaderTransport::flush() { + resetConsumedMessageSize(); // Write out any data waiting in the write buffer. uint32_t haveBytes = getWriteBytes(); @@ -485,7 +485,7 @@ void THeaderTransport::flush() { // write info headers // for now only write kv-headers - int32_t headerCount = safe_numeric_cast(writeHeaders_.size()); + auto headerCount = safe_numeric_cast(writeHeaders_.size()); if (headerCount > 0) { pkt += writeVarint32(infoIdType::KEYVALUE, pkt); // Write key-value headers count @@ -527,7 +527,7 @@ void THeaderTransport::flush() { outTransport_->write(pktStart, szHbo - haveBytes + 4); outTransport_->write(wBuf_.get(), haveBytes); } else if (clientType == THRIFT_FRAMED_BINARY || clientType == THRIFT_FRAMED_COMPACT) { - uint32_t szHbo = (uint32_t)haveBytes; + auto szHbo = (uint32_t)haveBytes; uint32_t szNbo = htonl(szHbo); outTransport_->write(reinterpret_cast(&szNbo), 4); diff --git a/lib/cpp/src/thrift/transport/THeaderTransport.h b/lib/cpp/src/thrift/transport/THeaderTransport.h index 1a2c8e04900..63a4ac88001 100644 --- a/lib/cpp/src/thrift/transport/THeaderTransport.h +++ b/lib/cpp/src/thrift/transport/THeaderTransport.h @@ -34,7 +34,6 @@ #endif #include -#include #include #include @@ -75,36 +74,38 @@ class THeaderTransport : public TVirtualTransport& transport) - : TVirtualTransport(transport), + explicit THeaderTransport(const std::shared_ptr& transport, + std::shared_ptr config = nullptr) + : TVirtualTransport(transport, config), outTransport_(transport), protoId(T_COMPACT_PROTOCOL), clientType(THRIFT_HEADER_CLIENT_TYPE), seqId(0), flags(0), tBufSize_(0), - tBuf_(NULL) { + tBuf_(nullptr) { if (!transport_) throw std::invalid_argument("transport is empty"); initBuffers(); } - THeaderTransport(const stdcxx::shared_ptr inTransport, - const stdcxx::shared_ptr outTransport) - : TVirtualTransport(inTransport), + THeaderTransport(const std::shared_ptr inTransport, + const std::shared_ptr outTransport, + std::shared_ptr config = nullptr) + : TVirtualTransport(inTransport, config), outTransport_(outTransport), protoId(T_COMPACT_PROTOCOL), clientType(THRIFT_HEADER_CLIENT_TYPE), seqId(0), flags(0), tBufSize_(0), - tBuf_(NULL) { + tBuf_(nullptr) { if (!transport_) throw std::invalid_argument("inTransport is empty"); if (!outTransport_) throw std::invalid_argument("outTransport is empty"); initBuffers(); } - virtual uint32_t readSlow(uint8_t* buf, uint32_t len); - virtual void flush(); + uint32_t readSlow(uint8_t* buf, uint32_t len) override; + void flush() override; void resizeTransformBuffer(uint32_t additionalSize = 0); @@ -176,17 +177,17 @@ class THeaderTransport : public TVirtualTransport outTransport_; + std::shared_ptr outTransport_; // 0 and 16th bits must be 0 to differentiate from framed & unframed static const uint32_t HEADER_MAGIC = 0x0FFF0000; @@ -258,15 +259,15 @@ class THeaderTransport : public TVirtualTransport getTransport(stdcxx::shared_ptr trans) { - return stdcxx::shared_ptr(new THeaderTransport(trans)); + std::shared_ptr getTransport(std::shared_ptr trans) override { + return std::shared_ptr(new THeaderTransport(trans)); } }; } diff --git a/lib/cpp/src/thrift/transport/THttpClient.cpp b/lib/cpp/src/thrift/transport/THttpClient.cpp index afd02a83dc0..ea2eb99af6e 100644 --- a/lib/cpp/src/thrift/transport/THttpClient.cpp +++ b/lib/cpp/src/thrift/transport/THttpClient.cpp @@ -32,24 +32,27 @@ namespace apache { namespace thrift { namespace transport { -THttpClient::THttpClient(stdcxx::shared_ptr transport, +THttpClient::THttpClient(std::shared_ptr transport, std::string host, - std::string path) - : THttpTransport(transport), host_(host), path_(path) { + std::string path, + std::shared_ptr config) + : THttpTransport(transport, config), + host_(host), + path_(path) { } -THttpClient::THttpClient(string host, int port, string path) - : THttpTransport(stdcxx::shared_ptr(new TSocket(host, port))), +THttpClient::THttpClient(string host, int port, string path, + std::shared_ptr config) + : THttpTransport(std::shared_ptr(new TSocket(host, port)), config), host_(host), path_(path) { } -THttpClient::~THttpClient() { -} +THttpClient::~THttpClient() = default; void THttpClient::parseHeader(char* header) { char* colon = strchr(header, ':'); - if (colon == NULL) { + if (colon == nullptr) { return; } char* value = colon + 1; @@ -68,7 +71,7 @@ bool THttpClient::parseStatusLine(char* status) { char* http = status; char* code = strchr(http, ' '); - if (code == NULL) { + if (code == nullptr) { throw TTransportException(string("Bad Status: ") + status); } @@ -77,7 +80,7 @@ bool THttpClient::parseStatusLine(char* status) { }; char* msg = strchr(code, ' '); - if (msg == NULL) { + if (msg == nullptr) { throw TTransportException(string("Bad Status: ") + status); } *msg = '\0'; @@ -94,6 +97,7 @@ bool THttpClient::parseStatusLine(char* status) { } void THttpClient::flush() { + resetConsumedMessageSize(); // Fetch the contents of the write buffer uint8_t* buf; uint32_t len; @@ -118,6 +122,10 @@ void THttpClient::flush() { writeBuffer_.resetBuffer(); readHeaders_ = true; } + +void THttpClient::setPath(std::string path) { + path_ = path; +} } } } // apache::thrift::transport diff --git a/lib/cpp/src/thrift/transport/THttpClient.h b/lib/cpp/src/thrift/transport/THttpClient.h index 96fd5b8460b..f0d7e8b2706 100644 --- a/lib/cpp/src/thrift/transport/THttpClient.h +++ b/lib/cpp/src/thrift/transport/THttpClient.h @@ -26,22 +26,43 @@ namespace apache { namespace thrift { namespace transport { +/** + * @brief Client transport using HTTP. The path is an optional field that is + * not required by Thrift HTTP server or client. It can be used i.e. with HTTP + * redirection, load balancing or forwarding on the server. + */ class THttpClient : public THttpTransport { public: - THttpClient(stdcxx::shared_ptr transport, std::string host, std::string path = ""); + /** + * @brief Constructor that wraps an existing transport, but also sets the + * host and path. The host and path are not used for the connection but are + * set in the HTTP header of the transport. + */ + THttpClient(std::shared_ptr transport, + std::string host = "localhost", + std::string path = "/service", + std::shared_ptr config = nullptr); + + /** + * @brief Constructor that will create a new socket transport using the host + * and port. + */ + THttpClient(std::string host, int port, + std::string path = "", + std::shared_ptr config = nullptr); - THttpClient(std::string host, int port, std::string path = ""); + ~THttpClient() override; - virtual ~THttpClient(); + void flush() override; - virtual void flush(); + void setPath(std::string path); protected: std::string host_; std::string path_; - virtual void parseHeader(char* header); - virtual bool parseStatusLine(char* status); + void parseHeader(char* header) override; + bool parseStatusLine(char* status) override; }; } } diff --git a/lib/cpp/src/thrift/transport/THttpServer.cpp b/lib/cpp/src/thrift/transport/THttpServer.cpp index 2f48cf6dd93..91a1c39afcd 100644 --- a/lib/cpp/src/thrift/transport/THttpServer.cpp +++ b/lib/cpp/src/thrift/transport/THttpServer.cpp @@ -34,12 +34,13 @@ namespace apache { namespace thrift { namespace transport { -THttpServer::THttpServer(stdcxx::shared_ptr transport) : THttpTransport(transport) { -} +THttpServer::THttpServer(std::shared_ptr transport, std::shared_ptr config) + : THttpTransport(transport, config) { -THttpServer::~THttpServer() { } +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) @@ -52,14 +53,14 @@ THttpServer::~THttpServer() { void THttpServer::parseHeader(char* header) { char* colon = strchr(header, ':'); - if (colon == NULL) { + 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") != NULL) { + if (THRIFT_strcasestr(value, "chunked") != nullptr) { chunked_ = true; } } else if (THRIFT_strncasecmp(header, "Content-length", sz) == 0) { @@ -74,7 +75,7 @@ bool THttpServer::parseStatusLine(char* status) { char* method = status; char* path = strchr(method, ' '); - if (path == NULL) { + if (path == nullptr) { throw TTransportException(string("Bad Status: ") + status); } @@ -83,7 +84,7 @@ bool THttpServer::parseStatusLine(char* status) { }; char* http = strchr(path, ' '); - if (http == NULL) { + if (http == nullptr) { throw TTransportException(string("Bad Status: ") + status); } *http = '\0'; @@ -119,18 +120,14 @@ bool THttpServer::parseStatusLine(char* status) { } void THttpServer::flush() { + resetConsumedMessageSize(); // 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(); + string header = getHeader(len); // Write the header, then the data, then flush // cast should be fine, because none of "header" is under attacker control @@ -143,13 +140,22 @@ void THttpServer::flush() { readHeaders_ = true; } +std::string THttpServer::getHeader(uint32_t len) { + 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; + return h.str(); +} + 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(NULL); + time_t t = time(nullptr); struct tm tmb; THRIFT_GMTIME(tmb, t); diff --git a/lib/cpp/src/thrift/transport/THttpServer.h b/lib/cpp/src/thrift/transport/THttpServer.h index 086dd6f5ff9..bc98986d7cf 100644 --- a/lib/cpp/src/thrift/transport/THttpServer.h +++ b/lib/cpp/src/thrift/transport/THttpServer.h @@ -28,16 +28,17 @@ namespace transport { class THttpServer : public THttpTransport { public: - THttpServer(stdcxx::shared_ptr transport); + THttpServer(std::shared_ptr transport, std::shared_ptr config = nullptr); - virtual ~THttpServer(); + ~THttpServer() override; - virtual void flush(); + void flush() override; protected: + virtual std::string getHeader(uint32_t len); void readHeaders(); - virtual void parseHeader(char* header); - virtual bool parseStatusLine(char* status); + void parseHeader(char* header) override; + bool parseStatusLine(char* status) override; std::string getTimeRFC1123(); }; @@ -46,15 +47,15 @@ class THttpServer : public THttpTransport { */ class THttpServerTransportFactory : public TTransportFactory { public: - THttpServerTransportFactory() {} + THttpServerTransportFactory() = default; - virtual ~THttpServerTransportFactory() {} + ~THttpServerTransportFactory() override = default; /** * Wraps the transport into a buffered one. */ - virtual stdcxx::shared_ptr getTransport(stdcxx::shared_ptr trans) { - return stdcxx::shared_ptr(new THttpServer(trans)); + std::shared_ptr getTransport(std::shared_ptr trans) override { + return std::shared_ptr(new THttpServer(trans)); } }; } diff --git a/lib/cpp/src/thrift/transport/THttpTransport.cpp b/lib/cpp/src/thrift/transport/THttpTransport.cpp index 4ccb4d35b03..305221e9d00 100644 --- a/lib/cpp/src/thrift/transport/THttpTransport.cpp +++ b/lib/cpp/src/thrift/transport/THttpTransport.cpp @@ -31,15 +31,16 @@ namespace transport { const char* THttpTransport::CRLF = "\r\n"; const int THttpTransport::CRLF_LEN = 2; -THttpTransport::THttpTransport(stdcxx::shared_ptr transport) - : transport_(transport), +THttpTransport::THttpTransport(std::shared_ptr transport, std::shared_ptr config) + : TVirtualTransport(config), + transport_(transport), origin_(""), readHeaders_(true), chunked_(false), chunkedDone_(false), chunkSize_(0), contentLength_(0), - httpBuf_(NULL), + httpBuf_(nullptr), httpPos_(0), httpBufLen_(0), httpBufSize_(1024) { @@ -48,19 +49,20 @@ THttpTransport::THttpTransport(stdcxx::shared_ptr transport) void THttpTransport::init() { httpBuf_ = (char*)std::malloc(httpBufSize_ + 1); - if (httpBuf_ == NULL) { + if (httpBuf_ == nullptr) { throw std::bad_alloc(); } httpBuf_[httpBufLen_] = '\0'; } THttpTransport::~THttpTransport() { - if (httpBuf_ != NULL) { + if (httpBuf_ != nullptr) { std::free(httpBuf_); } } uint32_t THttpTransport::read(uint8_t* buf, uint32_t len) { + checkReadBytesAvailable(len); if (readBuffer_.available_read() == 0) { readBuffer_.resetBuffer(); uint32_t got = readMoreData(); @@ -132,7 +134,7 @@ void THttpTransport::readChunkedFooters() { uint32_t THttpTransport::parseChunkSize(char* line) { char* semi = strchr(line, ';'); - if (semi != NULL) { + if (semi != nullptr) { *semi = '\0'; } uint32_t size = 0; @@ -166,12 +168,12 @@ uint32_t THttpTransport::readContent(uint32_t size) { char* THttpTransport::readLine() { while (true) { - char* eol = NULL; + char* eol = nullptr; eol = strstr(httpBuf_ + httpPos_, CRLF); // No CRLF yet? - if (eol == NULL) { + if (eol == nullptr) { // Shift whatever we have now to front and refill shift(); refill(); @@ -203,7 +205,7 @@ void THttpTransport::refill() { if (avail <= (httpBufSize_ / 4)) { httpBufSize_ *= 2; char* tmpBuf = (char*)std::realloc(httpBuf_, httpBufSize_ + 1); - if (tmpBuf == NULL) { + if (tmpBuf == nullptr) { throw std::bad_alloc(); } httpBuf_ = tmpBuf; @@ -257,7 +259,7 @@ void THttpTransport::write(const uint8_t* buf, uint32_t len) { writeBuffer_.write(buf, len); } -const std::string THttpTransport::getOrigin() { +const std::string THttpTransport::getOrigin() const { std::ostringstream oss; if (!origin_.empty()) { oss << origin_ << ", "; diff --git a/lib/cpp/src/thrift/transport/THttpTransport.h b/lib/cpp/src/thrift/transport/THttpTransport.h index 3fa80f81dae..5d2bd37fe29 100644 --- a/lib/cpp/src/thrift/transport/THttpTransport.h +++ b/lib/cpp/src/thrift/transport/THttpTransport.h @@ -36,30 +36,32 @@ namespace transport { */ class THttpTransport : public TVirtualTransport { public: - THttpTransport(stdcxx::shared_ptr transport); + THttpTransport(std::shared_ptr transport, std::shared_ptr config = nullptr); - virtual ~THttpTransport(); + ~THttpTransport() override; - void open() { transport_->open(); } + void open() override { transport_->open(); } - bool isOpen() { return transport_->isOpen(); } + bool isOpen() const override { return transport_->isOpen(); } - bool peek() { return transport_->peek(); } + bool peek() override { return transport_->peek(); } - void close() { transport_->close(); } + void close() override { transport_->close(); } uint32_t read(uint8_t* buf, uint32_t len); - uint32_t readEnd(); + uint32_t readEnd() override; void write(const uint8_t* buf, uint32_t len); - virtual void flush() = 0; + void flush() override { + resetConsumedMessageSize(); + }; - virtual const std::string getOrigin(); + const std::string getOrigin() const override; protected: - stdcxx::shared_ptr transport_; + std::shared_ptr transport_; std::string origin_; TMemoryBuffer writeBuffer_; diff --git a/lib/cpp/src/thrift/transport/TNonblockingSSLServerSocket.cpp b/lib/cpp/src/thrift/transport/TNonblockingSSLServerSocket.cpp index da83bea144e..adec5d0f96f 100644 --- a/lib/cpp/src/thrift/transport/TNonblockingSSLServerSocket.cpp +++ b/lib/cpp/src/thrift/transport/TNonblockingSSLServerSocket.cpp @@ -27,14 +27,14 @@ namespace transport { /** * Nonblocking SSL server socket implementation. */ -TNonblockingSSLServerSocket::TNonblockingSSLServerSocket(int port, stdcxx::shared_ptr factory) +TNonblockingSSLServerSocket::TNonblockingSSLServerSocket(int port, std::shared_ptr factory) : TNonblockingServerSocket(port), factory_(factory) { factory_->server(true); } TNonblockingSSLServerSocket::TNonblockingSSLServerSocket(const std::string& address, int port, - stdcxx::shared_ptr factory) + std::shared_ptr factory) : TNonblockingServerSocket(address, port), factory_(factory) { factory_->server(true); } @@ -42,13 +42,13 @@ TNonblockingSSLServerSocket::TNonblockingSSLServerSocket(const std::string& addr TNonblockingSSLServerSocket::TNonblockingSSLServerSocket(int port, int sendTimeout, int recvTimeout, - stdcxx::shared_ptr factory) + std::shared_ptr factory) : TNonblockingServerSocket(port, sendTimeout, recvTimeout), factory_(factory) { factory_->server(true); } -stdcxx::shared_ptr TNonblockingSSLServerSocket::createSocket(THRIFT_SOCKET client) { - stdcxx::shared_ptr tSSLSocket; +std::shared_ptr TNonblockingSSLServerSocket::createSocket(THRIFT_SOCKET client) { + std::shared_ptr tSSLSocket; tSSLSocket = factory_->createSocket(client); tSSLSocket->setLibeventSafe(); return tSSLSocket; diff --git a/lib/cpp/src/thrift/transport/TNonblockingSSLServerSocket.h b/lib/cpp/src/thrift/transport/TNonblockingSSLServerSocket.h index 7aaff53df48..a38bf126631 100644 --- a/lib/cpp/src/thrift/transport/TNonblockingSSLServerSocket.h +++ b/lib/cpp/src/thrift/transport/TNonblockingSSLServerSocket.h @@ -21,7 +21,6 @@ #define _THRIFT_TRANSPORT_TNONBLOCKINGSSLSERVERSOCKET_H_ 1 #include -#include namespace apache { namespace thrift { @@ -40,7 +39,7 @@ class TNonblockingSSLServerSocket : public TNonblockingServerSocket { * @param port Listening port * @param factory SSL socket factory implementation */ - TNonblockingSSLServerSocket(int port, stdcxx::shared_ptr factory); + TNonblockingSSLServerSocket(int port, std::shared_ptr factory); /** * Constructor. Binds to the specified address. @@ -51,7 +50,7 @@ class TNonblockingSSLServerSocket : public TNonblockingServerSocket { */ TNonblockingSSLServerSocket(const std::string& address, int port, - stdcxx::shared_ptr factory); + std::shared_ptr factory); /** * Constructor. Binds to all interfaces. @@ -64,11 +63,11 @@ class TNonblockingSSLServerSocket : public TNonblockingServerSocket { TNonblockingSSLServerSocket(int port, int sendTimeout, int recvTimeout, - stdcxx::shared_ptr factory); + std::shared_ptr factory); protected: - stdcxx::shared_ptr createSocket(THRIFT_SOCKET socket); - stdcxx::shared_ptr factory_; + std::shared_ptr createSocket(THRIFT_SOCKET socket) override; + std::shared_ptr factory_; }; } } diff --git a/lib/cpp/src/thrift/transport/TNonblockingServerSocket.cpp b/lib/cpp/src/thrift/transport/TNonblockingServerSocket.cpp index cca52a4ec3f..5ef083533c2 100644 --- a/lib/cpp/src/thrift/transport/TNonblockingServerSocket.cpp +++ b/lib/cpp/src/thrift/transport/TNonblockingServerSocket.cpp @@ -20,6 +20,7 @@ #include #include +#include #include #include #ifdef HAVE_SYS_SOCKET_H @@ -43,9 +44,11 @@ #include #endif -#include -#include #include +#include +#include +#include +#include #ifndef AF_LOCAL #define AF_LOCAL AF_UNIX @@ -73,8 +76,8 @@ namespace apache { namespace thrift { namespace transport { +using std::shared_ptr; using std::string; -using stdcxx::shared_ptr; TNonblockingServerSocket::TNonblockingServerSocket(int port) : port_(port), @@ -170,60 +173,7 @@ 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() ? NULL : &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 == NULL) - break; - } - - if (!path_.empty()) { - serverSocket_ = socket(PF_UNIX, SOCK_STREAM, IPPROTO_IP); - } else if (res != NULL) { - 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); - } +void TNonblockingServerSocket::_setup_sockopts() { // Set THRIFT_NO_SOCKET_CACHING to prevent 2MSL delay on accept int one = 1; @@ -277,19 +227,6 @@ void TNonblockingServerSocket::listen() { } } -#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))) { @@ -309,24 +246,6 @@ void TNonblockingServerSocket::listen() { 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) { @@ -347,6 +266,26 @@ void TNonblockingServerSocket::listen() { errno_copy); } +} // _setup_sockopts() + +void TNonblockingServerSocket::_setup_tcp_sockopts() { + int one = 1; + + // Set TCP nodelay if available, MAC OS X Hack + // See http://lists.danga.com/pipermail/memcached/2005-March/001240.html +#ifndef TCP_NOPUSH + // 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 + #ifdef TCP_LOW_MIN_RTO if (TSocket::getUseLowMinRto()) { if (-1 == setsockopt(s, IPPROTO_TCP, TCP_LOW_MIN_RTO, const_cast_sockopt(&one), sizeof(one))) { @@ -360,42 +299,66 @@ void TNonblockingServerSocket::listen() { } #endif - // prepare the port information +} // _setup_tcp_sockopts() + +void TNonblockingServerSocket::listen() { + listening_ = true; +#ifdef _WIN32 + TWinsockSingleton::create(); +#endif // _WIN32 + + // tcp == false means Unix Domain socket + bool tcp = (path_.empty()); + + // Validate port number + if (port_ < 0 || port_ > 0xFFFF) { + throw TTransportException(TTransportException::BAD_ARGS, "Specified port is invalid"); + } + + // Resolve host:port strings into an iterable of struct addrinfo* + AddressResolutionHelper resolved_addresses; + if (tcp) { + try { + resolved_addresses.resolve(address_, std::to_string(port_), SOCK_STREAM, + AI_PASSIVE | AI_V4MAPPED); + } catch (const std::system_error& e) { + GlobalOutput.printf("getaddrinfo() -> %d. %s", e.code().value(), e.what()); + close(); + throw TTransportException(TTransportException::NOT_OPEN, + "Could not resolve host for server socket."); + } + } + // 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()) { + if (!tcp) { + // -- Unix Domain Socket -- // -#ifndef _WIN32 + serverSocket_ = socket(PF_UNIX, SOCK_STREAM, IPPROTO_IP); - // Unix Domain Socket - size_t len = path_.size() + 1; - if (len > sizeof(((sockaddr_un*)NULL)->sun_path)) { - errno_copy = THRIFT_GET_SOCKET_ERROR; - GlobalOutput.perror("TSocket::listen() Unix Domain socket path too long", errno_copy); + 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, - "Unix Domain socket path too long", + "Could not create server socket.", errno_copy); } - struct sockaddr_un address; - address.sun_family = AF_UNIX; - memcpy(address.sun_path, path_.c_str(), len); + _setup_sockopts(); + //_setup_unixdomain_sockopts(); - socklen_t structlen = static_cast(sizeof(address)); +/* + * TODO: seems that windows now support unix sockets, + * see: https://devblogs.microsoft.com/commandline/af_unix-comes-to-windows/ + */ +#ifndef _WIN32 - 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 - } + struct sockaddr_un address; + socklen_t structlen = fillUnixSocketAddr(address, path_); do { if (0 == ::bind(serverSocket_, (struct sockaddr*)&address, structlen)) { @@ -410,11 +373,48 @@ void TNonblockingServerSocket::listen() { " Unix Domain socket path not supported"); #endif } else { + + // -- TCP socket -- // + + auto addr_iter = AddressResolutionHelper::Iter{}; + + // Via DNS or somehow else, single hostname can resolve into many addresses. + // Results may contain perhaps a mix of IPv4 and IPv6. Here, we iterate + // over what system gave us, picking the first address that works. do { - if (0 == ::bind(serverSocket_, res->ai_addr, static_cast(res->ai_addrlen))) { + if (!addr_iter) { + // init + recycle over many retries + addr_iter = resolved_addresses.iterate(); + } + auto trybind = *addr_iter++; + + serverSocket_ = socket(trybind->ai_family, trybind->ai_socktype, trybind->ai_protocol); + if (serverSocket_ == -1) { + errno_copy = THRIFT_GET_SOCKET_ERROR; + continue; + } + + _setup_sockopts(); + _setup_tcp_sockopts(); + +#ifdef IPV6_V6ONLY + if (trybind->ai_family == AF_INET6) { + 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 + + if (0 == ::bind(serverSocket_, trybind->ai_addr, static_cast(trybind->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)); @@ -428,20 +428,29 @@ void TNonblockingServerSocket::listen() { GlobalOutput.perror("TNonblockingServerSocket::getPort() getsockname() ", errno_copy); } else { if (sa.ss_family == AF_INET6) { - const struct sockaddr_in6* sin = reinterpret_cast(&sa); + const auto* sin = reinterpret_cast(&sa); listenPort_ = ntohs(sin->sin6_port); } else { - const struct sockaddr_in* sin = reinterpret_cast(&sa); + const auto* sin = reinterpret_cast(&sa); listenPort_ = ntohs(sin->sin_port); } } } + } // TCP socket // + + // throw error if socket still wasn't created successfully + if (serverSocket_ == THRIFT_INVALID_SOCKET) { + GlobalOutput.perror("TServerSocket::listen() socket() ", errno_copy); + close(); + throw TTransportException(TTransportException::NOT_OPEN, + "Could not create server socket.", + errno_copy); } // throw an error if we failed to bind properly if (retries > retryLimit_) { char errbuf[1024]; - if (!path_.empty()) { + if (!tcp) { THRIFT_SNPRINTF(errbuf, sizeof(errbuf), "TNonblockingServerSocket::listen() PATH %s", path_.c_str()); } else { THRIFT_SNPRINTF(errbuf, sizeof(errbuf), "TNonblockingServerSocket::listen() BIND %d", port_); @@ -477,9 +486,10 @@ int TNonblockingServerSocket::getListenPort() { shared_ptr TNonblockingServerSocket::acceptImpl() { if (serverSocket_ == THRIFT_INVALID_SOCKET) { - throw TTransportException(TTransportException::NOT_OPEN, "TNonblockingServerSocket not listening"); + throw TTransportException(TTransportException::NOT_OPEN, + "TNonblockingServerSocket not listening"); } - + struct sockaddr_storage clientAddress; int size = sizeof(clientAddress); THRIFT_SOCKET clientSocket @@ -532,7 +542,7 @@ shared_ptr TNonblockingServerSocket::acceptImpl() { } shared_ptr TNonblockingServerSocket::createSocket(THRIFT_SOCKET clientSocket) { - return shared_ptr(new TSocket(clientSocket)); + return std::make_shared(clientSocket); } void TNonblockingServerSocket::close() { @@ -543,6 +553,6 @@ void TNonblockingServerSocket::close() { serverSocket_ = THRIFT_INVALID_SOCKET; listening_ = false; } -} -} -} // apache::thrift::transport +} // namespace transport +} // namespace thrift +} // namespace apache diff --git a/lib/cpp/src/thrift/transport/TNonblockingServerSocket.h b/lib/cpp/src/thrift/transport/TNonblockingServerSocket.h index 1d33239ffae..1ed2b07f9c3 100644 --- a/lib/cpp/src/thrift/transport/TNonblockingServerSocket.h +++ b/lib/cpp/src/thrift/transport/TNonblockingServerSocket.h @@ -22,7 +22,6 @@ #include #include -#include namespace apache { namespace thrift { @@ -37,7 +36,7 @@ class TSocket; */ class TNonblockingServerSocket : public TNonblockingServerTransport { public: - typedef apache::thrift::stdcxx::function socket_func_t; + typedef std::function socket_func_t; const static int DEFAULT_BACKLOG = 1024; @@ -72,7 +71,7 @@ class TNonblockingServerSocket : public TNonblockingServerTransport { */ TNonblockingServerSocket(const std::string& path); - virtual ~TNonblockingServerSocket(); + ~TNonblockingServerSocket() override; void setSendTimeout(int sendTimeout); void setRecvTimeout(int recvTimeout); @@ -98,18 +97,18 @@ class TNonblockingServerSocket : public TNonblockingServerTransport { // socket, this is the place to do it. void setAcceptCallback(const socket_func_t& acceptCallback) { acceptCallback_ = acceptCallback; } - THRIFT_SOCKET getSocketFD() { return serverSocket_; } + THRIFT_SOCKET getSocketFD() override { return serverSocket_; } - int getPort(); - - int getListenPort(); + int getPort() override; - void listen(); - void close(); + int getListenPort() override; + + void listen() override; + void close() override; protected: - apache::thrift::stdcxx::shared_ptr acceptImpl(); - virtual apache::thrift::stdcxx::shared_ptr createSocket(THRIFT_SOCKET client); + std::shared_ptr acceptImpl() override; + virtual std::shared_ptr createSocket(THRIFT_SOCKET client); private: int port_; @@ -129,6 +128,9 @@ class TNonblockingServerSocket : public TNonblockingServerTransport { socket_func_t listenCallback_; socket_func_t acceptCallback_; + + void _setup_sockopts(); + void _setup_tcp_sockopts(); }; } } diff --git a/lib/cpp/src/thrift/transport/TNonblockingServerTransport.h b/lib/cpp/src/thrift/transport/TNonblockingServerTransport.h index c32a051d90c..e8997d7849f 100644 --- a/lib/cpp/src/thrift/transport/TNonblockingServerTransport.h +++ b/lib/cpp/src/thrift/transport/TNonblockingServerTransport.h @@ -22,7 +22,6 @@ #include #include -#include namespace apache { namespace thrift { @@ -36,7 +35,7 @@ namespace transport { */ class TNonblockingServerTransport { public: - virtual ~TNonblockingServerTransport() {} + virtual ~TNonblockingServerTransport() = default; /** * Starts the server transport listening for new connections. Prior to this @@ -50,16 +49,16 @@ class TNonblockingServerTransport { * 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 + * opened state. nullptr should never be returned, instead an Exception should * always be thrown. * * @return A new TTransport object * @throws TTransportException if there is an error */ - stdcxx::shared_ptr accept() { - stdcxx::shared_ptr result = acceptImpl(); + std::shared_ptr accept() { + std::shared_ptr result = acceptImpl(); if (!result) { - throw TTransportException("accept() may not return NULL"); + throw TTransportException("accept() may not return nullptr"); } return result; } @@ -83,7 +82,7 @@ class TNonblockingServerTransport { virtual void close() = 0; protected: - TNonblockingServerTransport() {} + TNonblockingServerTransport() = default; /** * Subclasses should implement this function for accept. @@ -91,7 +90,7 @@ class TNonblockingServerTransport { * @return A newly allocated TTransport object * @throw TTransportException If an error occurs */ - virtual stdcxx::shared_ptr acceptImpl() = 0; + virtual std::shared_ptr acceptImpl() = 0; }; } diff --git a/lib/cpp/src/thrift/transport/TPipe.cpp b/lib/cpp/src/thrift/transport/TPipe.cpp index 8a84457db0a..953cec16711 100644 --- a/lib/cpp/src/thrift/transport/TPipe.cpp +++ b/lib/cpp/src/thrift/transport/TPipe.cpp @@ -183,7 +183,7 @@ void pseudo_sync_write(HANDLE pipe, HANDLE event, const uint8_t* buf, uint32_t l uint32_t written = 0; while (written < len) { - BOOL result = ::WriteFile(pipe, buf + written, len - written, NULL, &tempOverlap); + BOOL result = ::WriteFile(pipe, buf + written, len - written, nullptr, &tempOverlap); if (result == FALSE && ::GetLastError() != ERROR_IO_PENDING) { GlobalOutput.perror("TPipe ::WriteFile errored GLE=", ::GetLastError()); @@ -205,7 +205,7 @@ uint32_t pseudo_sync_read(HANDLE pipe, HANDLE event, uint8_t* buf, uint32_t len) memset(&tempOverlap, 0, sizeof(tempOverlap)); tempOverlap.hEvent = event; - BOOL result = ::ReadFile(pipe, buf, len, NULL, &tempOverlap); + BOOL result = ::ReadFile(pipe, buf, len, nullptr, &tempOverlap); if (result == FALSE && ::GetLastError() != ERROR_IO_PENDING) { GlobalOutput.perror("TPipe ::ReadFile errored GLE=", ::GetLastError()); @@ -222,30 +222,35 @@ uint32_t pseudo_sync_read(HANDLE pipe, HANDLE event, uint8_t* buf, uint32_t len) } //---- Constructors ---- -TPipe::TPipe(TAutoHandle &Pipe) - : impl_(new TWaitableNamedPipeImpl(Pipe)), TimeoutSeconds_(3), isAnonymous_(false) { +TPipe::TPipe(TAutoHandle &Pipe, std::shared_ptr config) + : impl_(new TWaitableNamedPipeImpl(Pipe)), TimeoutSeconds_(3), + isAnonymous_(false), TVirtualTransport(config) { } -TPipe::TPipe(HANDLE Pipe) - : TimeoutSeconds_(3), isAnonymous_(false) +TPipe::TPipe(HANDLE Pipe, std::shared_ptr config) + : TimeoutSeconds_(3), isAnonymous_(false), TVirtualTransport(config) { TAutoHandle pipeHandle(Pipe); impl_.reset(new TWaitableNamedPipeImpl(pipeHandle)); } -TPipe::TPipe(const char* pipename) : TimeoutSeconds_(3), isAnonymous_(false) { +TPipe::TPipe(const char* pipename, std::shared_ptr config) : TimeoutSeconds_(3), + isAnonymous_(false), TVirtualTransport(config) { setPipename(pipename); } -TPipe::TPipe(const std::string& pipename) : TimeoutSeconds_(3), isAnonymous_(false) { +TPipe::TPipe(const std::string& pipename, std::shared_ptr config) : TimeoutSeconds_(3), + isAnonymous_(false), TVirtualTransport(config) { setPipename(pipename); } -TPipe::TPipe(HANDLE PipeRd, HANDLE PipeWrt) - : impl_(new TAnonPipeImpl(PipeRd, PipeWrt)), TimeoutSeconds_(3), isAnonymous_(true) { +TPipe::TPipe(HANDLE PipeRd, HANDLE PipeWrt, std::shared_ptr config) + : impl_(new TAnonPipeImpl(PipeRd, PipeWrt)), TimeoutSeconds_(3), isAnonymous_(true), + TVirtualTransport(config) { } -TPipe::TPipe() : TimeoutSeconds_(3), isAnonymous_(false) { +TPipe::TPipe(std::shared_ptr config) : TimeoutSeconds_(3), isAnonymous_(false), + TVirtualTransport(config) { } TPipe::~TPipe() { @@ -254,8 +259,8 @@ TPipe::~TPipe() { //--------------------------------------------------------- // Transport callbacks //--------------------------------------------------------- -bool TPipe::isOpen() { - return impl_.get() != NULL; +bool TPipe::isOpen() const { + return impl_.get() != nullptr; } bool TPipe::peek() { @@ -272,10 +277,10 @@ void TPipe::open() { hPipe.reset(CreateFileA(pipename_.c_str(), GENERIC_READ | GENERIC_WRITE, 0, // no sharing - NULL, // default security attributes + nullptr, // default security attributes OPEN_EXISTING, // opens existing pipe flags, - NULL)); // no template file + nullptr)); // no template file if (hPipe.h != INVALID_HANDLE_VALUE) break; // success! @@ -299,6 +304,7 @@ void TPipe::close() { } uint32_t TPipe::read(uint8_t* buf, uint32_t len) { + checkReadBytesAvailable(len); if (!isOpen()) throw TTransportException(TTransportException::NOT_OPEN, "Called read on non-open pipe"); return impl_->read(buf, len); @@ -310,7 +316,7 @@ uint32_t pipe_read(HANDLE pipe, uint8_t* buf, uint32_t len) { buf, // buffer to receive reply len, // size of buffer &cbRead, // number of bytes read - NULL); // not overlapped + nullptr); // not overlapped if (!fSuccess && GetLastError() != ERROR_MORE_DATA) return 0; // No more data, possibly because client disconnected. @@ -330,7 +336,7 @@ void pipe_write(HANDLE pipe, const uint8_t* buf, uint32_t len) { buf, // message len, // message length &cbWritten, // bytes written - NULL); // not overlapped + nullptr); // not overlapped if (!fSuccess) throw TTransportException(TTransportException::NOT_OPEN, "Write to pipe failed"); diff --git a/lib/cpp/src/thrift/transport/TPipe.h b/lib/cpp/src/thrift/transport/TPipe.h index dfc5f2c2fab..7795151a61a 100644 --- a/lib/cpp/src/thrift/transport/TPipe.h +++ b/lib/cpp/src/thrift/transport/TPipe.h @@ -49,30 +49,30 @@ class TPipeImpl; class TPipe : public TVirtualTransport { public: // Constructs a new pipe object. - TPipe(); + TPipe(std::shared_ptr config = nullptr); // Named pipe constructors - - explicit TPipe(HANDLE Pipe); // HANDLE is a void* - explicit TPipe(TAutoHandle& Pipe); // this ctor will clear out / move from Pipe + explicit TPipe(HANDLE Pipe, std::shared_ptr config = nullptr); // HANDLE is a void* + explicit TPipe(TAutoHandle& Pipe, std::shared_ptr config = nullptr); // 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); + explicit TPipe(const char* pipename, std::shared_ptr config = nullptr); + explicit TPipe(const std::string& pipename, std::shared_ptr config = nullptr); // Anonymous pipe - - TPipe(HANDLE PipeRd, HANDLE PipeWrt); + TPipe(HANDLE PipeRd, HANDLE PipeWrt, std::shared_ptr config = nullptr); // Destroys the pipe object, closing it if necessary. virtual ~TPipe(); // Returns whether the pipe is open & valid. - virtual bool isOpen(); + bool isOpen() const override; // Checks whether more data is available in the pipe. - virtual bool peek(); + bool peek() override; // Creates and opens the named/anonymous pipe. - virtual void open(); + void open() override; // Shuts down communications on the pipe. - virtual void close(); + void close() override; // Reads from the pipe. virtual uint32_t read(uint8_t* buf, uint32_t len); @@ -95,7 +95,7 @@ class TPipe : public TVirtualTransport { HANDLE getNativeWaitHandle(); private: - stdcxx::shared_ptr impl_; + std::shared_ptr impl_; std::string pipename_; diff --git a/lib/cpp/src/thrift/transport/TPipeServer.cpp b/lib/cpp/src/thrift/transport/TPipeServer.cpp index 5923a6236a8..27635513c98 100644 --- a/lib/cpp/src/thrift/transport/TPipeServer.cpp +++ b/lib/cpp/src/thrift/transport/TPipeServer.cpp @@ -22,13 +22,13 @@ #include #include -#include #include #ifdef _WIN32 #include #include #include +#include #endif //_WIN32 namespace apache { @@ -37,20 +37,20 @@ namespace transport { #ifdef _WIN32 -using stdcxx::shared_ptr; +using std::shared_ptr; class TPipeServerImpl : boost::noncopyable { public: TPipeServerImpl() {} virtual ~TPipeServerImpl() {} virtual void interrupt() = 0; - virtual stdcxx::shared_ptr acceptImpl() = 0; + virtual std::shared_ptr acceptImpl() = 0; virtual HANDLE getPipeHandle() = 0; virtual HANDLE getWrtPipeHandle() = 0; virtual HANDLE getClientRdPipeHandle() = 0; virtual HANDLE getClientWrtPipeHandle() = 0; - virtual HANDLE getNativeWaitHandle() { return NULL; } + virtual HANDLE getNativeWaitHandle() { return nullptr; } }; class TAnonPipeServer : public TPipeServerImpl { @@ -75,7 +75,7 @@ class TAnonPipeServer : public TPipeServerImpl { virtual void interrupt() {} // not currently implemented - virtual stdcxx::shared_ptr acceptImpl(); + virtual std::shared_ptr acceptImpl(); virtual HANDLE getPipeHandle() { return PipeR_.h; } virtual HANDLE getWrtPipeHandle() { return PipeW_.h; } @@ -96,9 +96,15 @@ class TAnonPipeServer : public TPipeServerImpl { class TNamedPipeServer : public TPipeServerImpl { public: - TNamedPipeServer(const std::string& pipename, uint32_t bufsize, uint32_t maxconnections) - : stopping_(false), pipename_(pipename), bufsize_(bufsize), maxconns_(maxconnections) - { + TNamedPipeServer(const std::string& pipename, + uint32_t bufsize, + uint32_t maxconnections, + const std::string& securityDescriptor) + : stopping_(false), + pipename_(pipename), + bufsize_(bufsize), + maxconns_(maxconnections), + securityDescriptor_(securityDescriptor) { connectOverlap_.action = TOverlappedWorkItem::CONNECT; cancelOverlap_.action = TOverlappedWorkItem::CANCELIO; TAutoCrit lock(pipe_protect_); @@ -117,7 +123,7 @@ class TNamedPipeServer : public TPipeServerImpl { } } - virtual stdcxx::shared_ptr acceptImpl(); + virtual std::shared_ptr acceptImpl(); virtual HANDLE getPipeHandle() { return Pipe_.h; } virtual HANDLE getWrtPipeHandle() { return INVALID_HANDLE_VALUE; } @@ -135,20 +141,21 @@ class TNamedPipeServer : public TPipeServerImpl { bool stopping_; std::string pipename_; + std::string securityDescriptor_; uint32_t bufsize_; uint32_t maxconns_; TManualResetEvent listen_event_; TCriticalSection pipe_protect_; // only read or write these variables underneath a locked pipe_protect_ - stdcxx::shared_ptr cached_client_; + std::shared_ptr cached_client_; TAutoHandle Pipe_; }; HANDLE TPipeServer::getNativeWaitHandle() { if (impl_) return impl_->getNativeWaitHandle(); - return NULL; + return nullptr; } //---- Constructors ---- @@ -156,17 +163,30 @@ TPipeServer::TPipeServer(const std::string& pipename, uint32_t bufsize) : bufsize_(bufsize), isAnonymous_(false) { setMaxConnections(TPIPE_SERVER_MAX_CONNS_DEFAULT); setPipename(pipename); + setSecurityDescriptor(DEFAULT_PIPE_SECURITY); } TPipeServer::TPipeServer(const std::string& pipename, uint32_t bufsize, uint32_t maxconnections) : bufsize_(bufsize), isAnonymous_(false) { setMaxConnections(maxconnections); setPipename(pipename); + setSecurityDescriptor(DEFAULT_PIPE_SECURITY); +} + +TPipeServer::TPipeServer(const std::string& pipename, + uint32_t bufsize, + uint32_t maxconnections, + const std::string& securityDescriptor) + : bufsize_(bufsize), isAnonymous_(false) { + setMaxConnections(maxconnections); + setPipename(pipename); + setSecurityDescriptor(securityDescriptor); } TPipeServer::TPipeServer(const std::string& pipename) : bufsize_(1024), isAnonymous_(false) { setMaxConnections(TPIPE_SERVER_MAX_CONNS_DEFAULT); setPipename(pipename); + setSecurityDescriptor(DEFAULT_PIPE_SECURITY); } TPipeServer::TPipeServer(int bufsize) : bufsize_(bufsize), isAnonymous_(true) { @@ -182,13 +202,17 @@ TPipeServer::TPipeServer() : bufsize_(1024), isAnonymous_(true) { //---- Destructor ---- TPipeServer::~TPipeServer() {} +bool TPipeServer::isOpen() const { + return (impl_->getPipeHandle() != INVALID_HANDLE_VALUE); +} + //--------------------------------------------------------- // Transport callbacks //--------------------------------------------------------- void TPipeServer::listen() { if (isAnonymous_) return; - impl_.reset(new TNamedPipeServer(pipename_, bufsize_, maxconns_)); + impl_.reset(new TNamedPipeServer(pipename_, bufsize_, maxconns_, securityDescriptor_)); } shared_ptr TPipeServer::acceptImpl() { @@ -203,7 +227,7 @@ shared_ptr TAnonPipeServer::acceptImpl() { &buf, // buffer to receive reply 0, // size of buffer &br, // number of bytes read - NULL); // not overlapped + nullptr); // not overlapped if (!fSuccess && GetLastError() != ERROR_MORE_DATA) { GlobalOutput.perror("TPipeServer unable to initiate pipe comms, GLE=", GetLastError()); @@ -224,7 +248,7 @@ void TNamedPipeServer::initiateNamedConnect(const TAutoCrit &lockProof) { // The prior connection has been handled, so close the gate ResetEvent(listen_event_.h); - connectOverlap_.reset(NULL, 0, listen_event_.h); + connectOverlap_.reset(nullptr, 0, listen_event_.h); connectOverlap_.h = Pipe_.h; thread_->addWorkItem(&connectOverlap_); @@ -259,7 +283,7 @@ void TNamedPipeServer::initiateNamedConnect(const TAutoCrit &lockProof) { shared_ptr TNamedPipeServer::acceptImpl() { { TAutoCrit lock(pipe_protect_); - if (cached_client_.get() != NULL) { + if (cached_client_.get() != nullptr) { shared_ptr client; // zero out cached_client, since we are about to return it. client.swap(cached_client_); @@ -287,8 +311,20 @@ shared_ptr TNamedPipeServer::acceptImpl() { // concurrently with interrupt, and that should be fine. if (GetOverlappedResult(Pipe_.h, &connectOverlap_.overlap, &dwDummy, TRUE)) { TAutoCrit lock(pipe_protect_); + shared_ptr client; + try { + client.reset(new TPipe(Pipe_)); + } catch (TTransportException& ttx) { + if (ttx.getType() == TTransportException::INTERRUPTED) { + throw; + } + + GlobalOutput.perror("Client connection failed. TTransportExceptionType=", ttx.getType()); + // kick off the next connection before throwing + initiateNamedConnect(lock); + throw TTransportException(TTransportException::CLIENT_DISCONNECT, ttx.what()); + } GlobalOutput.printf("Client connected."); - shared_ptr client(new TPipe(Pipe_)); // kick off the next connection before returning initiateNamedConnect(lock); return client; // success! @@ -312,60 +348,47 @@ 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); +bool TNamedPipeServer::createNamedPipe(const TAutoCrit& /*lockProof*/) { - 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(everyone_sid); + PSECURITY_DESCRIPTOR psd = nullptr; + ULONG size = 0; - PACL acl = NULL; - SetEntriesInAcl(1, &ea, NULL, &acl); - - PSECURITY_DESCRIPTOR sd = (PSECURITY_DESCRIPTOR)LocalAlloc(LPTR, SECURITY_DESCRIPTOR_MIN_LENGTH); - InitializeSecurityDescriptor(sd, SECURITY_DESCRIPTOR_REVISION); - SetSecurityDescriptorDacl(sd, TRUE, acl, FALSE); + if (!ConvertStringSecurityDescriptorToSecurityDescriptorA(securityDescriptor_.c_str(), + SDDL_REVISION_1, &psd, &size)) { + DWORD lastError = GetLastError(); + GlobalOutput.perror("TPipeServer::ConvertStringSecurityDescriptorToSecurityDescriptorA() GLE=", + lastError); + throw TTransportException( + TTransportException::NOT_OPEN, + "TPipeServer::ConvertStringSecurityDescriptorToSecurityDescriptorA() failed", lastError); + } SECURITY_ATTRIBUTES sa; sa.nLength = sizeof(SECURITY_ATTRIBUTES); - sa.lpSecurityDescriptor = sd; + sa.lpSecurityDescriptor = psd; 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 - - DWORD lastError = GetLastError(); - LocalFree(sd); - LocalFree(acl); - FreeSid(everyone_sid); + 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(); + if (psd) + LocalFree(psd); if (hPipe.h == INVALID_HANDLE_VALUE) { Pipe_.reset(); GlobalOutput.perror("TPipeServer::TCreateNamedPipe() GLE=", lastError); - throw TTransportException(TTransportException::NOT_OPEN, - "TCreateNamedPipe() failed", - lastError); - return false; + throw TTransportException(TTransportException::NOT_OPEN, "TCreateNamedPipe() failed", + lastError); } Pipe_.reset(hPipe.release()); @@ -376,8 +399,16 @@ bool TAnonPipeServer::createAnonPipe() { SECURITY_ATTRIBUTES sa; SECURITY_DESCRIPTOR sd; // security information for pipes - InitializeSecurityDescriptor(&sd, SECURITY_DESCRIPTOR_REVISION); - SetSecurityDescriptorDacl(&sd, true, NULL, false); + if (!InitializeSecurityDescriptor(&sd, SECURITY_DESCRIPTOR_REVISION)) { + GlobalOutput.perror("TPipeServer InitializeSecurityDescriptor (anon) failed, GLE=", + GetLastError()); + return false; + } + if (!SetSecurityDescriptorDacl(&sd, true, nullptr, 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 @@ -445,6 +476,10 @@ void TPipeServer::setAnonymous(bool anon) { isAnonymous_ = anon; } +void TPipeServer::setSecurityDescriptor(const std::string& securityDescriptor) { + securityDescriptor_ = securityDescriptor; +} + void TPipeServer::setMaxConnections(uint32_t maxconnections) { if (maxconnections == 0) maxconns_ = 1; diff --git a/lib/cpp/src/thrift/transport/TPipeServer.h b/lib/cpp/src/thrift/transport/TPipeServer.h index 117773c2266..67c5d51a761 100644 --- a/lib/cpp/src/thrift/transport/TPipeServer.h +++ b/lib/cpp/src/thrift/transport/TPipeServer.h @@ -20,8 +20,8 @@ #ifndef _THRIFT_TRANSPORT_TSERVERWINPIPES_H_ #define _THRIFT_TRANSPORT_TSERVERWINPIPES_H_ 1 +#include #include -#include #ifndef _WIN32 #include #endif @@ -31,6 +31,11 @@ #define TPIPE_SERVER_MAX_CONNS_DEFAULT PIPE_UNLIMITED_INSTANCES +// Windows - set security to allow non-elevated apps +// to access pipes created by elevated apps. +// Full access to everyone +const std::string DEFAULT_PIPE_SECURITY{"D:(A;;FA;;;WD)"}; + namespace apache { namespace thrift { namespace transport { @@ -51,6 +56,10 @@ class TPipeServer : public TServerTransport { // 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, + uint32_t bufsize, + uint32_t maxconnections, + const std::string& securityDescriptor); TPipeServer(const std::string& pipename); // Anonymous pipe - TPipeServer(int bufsize); @@ -59,10 +68,12 @@ class TPipeServer : public TServerTransport { // Destructor virtual ~TPipeServer(); + bool isOpen() const override; + // Standard transport callbacks - virtual void interrupt(); - virtual void close(); - virtual void listen(); + void interrupt() override; + void close() override; + void listen() override; // Accessors std::string getPipename(); @@ -76,18 +87,20 @@ class TPipeServer : public TServerTransport { bool getAnonymous(); void setAnonymous(bool anon); void setMaxConnections(uint32_t maxconnections); + void setSecurityDescriptor(const std::string& securityDescriptor); // 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 stdcxx::shared_ptr acceptImpl(); + virtual std::shared_ptr acceptImpl(); private: - stdcxx::shared_ptr impl_; + std::shared_ptr impl_; std::string pipename_; + std::string securityDescriptor_; uint32_t bufsize_; uint32_t maxconns_; bool isAnonymous_; diff --git a/lib/cpp/src/thrift/transport/TSSLServerSocket.cpp b/lib/cpp/src/thrift/transport/TSSLServerSocket.cpp index 8e81ad7431e..b20c174087d 100644 --- a/lib/cpp/src/thrift/transport/TSSLServerSocket.cpp +++ b/lib/cpp/src/thrift/transport/TSSLServerSocket.cpp @@ -17,6 +17,7 @@ * under the License. */ +#include #include #include @@ -27,14 +28,14 @@ namespace transport { /** * SSL server socket implementation. */ -TSSLServerSocket::TSSLServerSocket(int port, stdcxx::shared_ptr factory) +TSSLServerSocket::TSSLServerSocket(int port, std::shared_ptr factory) : TServerSocket(port), factory_(factory) { factory_->server(true); } TSSLServerSocket::TSSLServerSocket(const std::string& address, int port, - stdcxx::shared_ptr factory) + std::shared_ptr factory) : TServerSocket(address, port), factory_(factory) { factory_->server(true); } @@ -42,12 +43,12 @@ TSSLServerSocket::TSSLServerSocket(const std::string& address, TSSLServerSocket::TSSLServerSocket(int port, int sendTimeout, int recvTimeout, - stdcxx::shared_ptr factory) + std::shared_ptr factory) : TServerSocket(port, sendTimeout, recvTimeout), factory_(factory) { factory_->server(true); } -stdcxx::shared_ptr TSSLServerSocket::createSocket(THRIFT_SOCKET client) { +std::shared_ptr TSSLServerSocket::createSocket(THRIFT_SOCKET client) { if (interruptableChildren_) { return factory_->createSocket(client, pChildInterruptSockReader_); diff --git a/lib/cpp/src/thrift/transport/TSSLServerSocket.h b/lib/cpp/src/thrift/transport/TSSLServerSocket.h index dda9af41767..44df4327687 100644 --- a/lib/cpp/src/thrift/transport/TSSLServerSocket.h +++ b/lib/cpp/src/thrift/transport/TSSLServerSocket.h @@ -20,7 +20,6 @@ #ifndef _THRIFT_TRANSPORT_TSSLSERVERSOCKET_H_ #define _THRIFT_TRANSPORT_TSSLSERVERSOCKET_H_ 1 -#include #include namespace apache { @@ -40,7 +39,7 @@ class TSSLServerSocket : public TServerSocket { * @param port Listening port * @param factory SSL socket factory implementation */ - TSSLServerSocket(int port, stdcxx::shared_ptr factory); + TSSLServerSocket(int port, std::shared_ptr factory); /** * Constructor. Binds to the specified address. @@ -51,7 +50,7 @@ class TSSLServerSocket : public TServerSocket { */ TSSLServerSocket(const std::string& address, int port, - stdcxx::shared_ptr factory); + std::shared_ptr factory); /** * Constructor. Binds to all interfaces. @@ -64,11 +63,11 @@ class TSSLServerSocket : public TServerSocket { TSSLServerSocket(int port, int sendTimeout, int recvTimeout, - stdcxx::shared_ptr factory); + std::shared_ptr factory); protected: - stdcxx::shared_ptr createSocket(THRIFT_SOCKET socket); - stdcxx::shared_ptr factory_; + std::shared_ptr createSocket(THRIFT_SOCKET socket) override; + std::shared_ptr factory_; }; } } diff --git a/lib/cpp/src/thrift/transport/TSSLSocket.cpp b/lib/cpp/src/thrift/transport/TSSLSocket.cpp index 251ef2f5bdc..9efc5fc50d1 100644 --- a/lib/cpp/src/thrift/transport/TSSLSocket.cpp +++ b/lib/cpp/src/thrift/transport/TSSLSocket.cpp @@ -19,9 +19,10 @@ #include +#include #include +#include #include -#include #ifdef HAVE_ARPA_INET_H #include #endif @@ -95,7 +96,7 @@ static CRYPTO_dynlock_value* dyn_create(const char*, int) { } static void dyn_lock(int mode, struct CRYPTO_dynlock_value* lock, const char*, int) { - if (lock != NULL) { + if (lock != nullptr) { if (mode & CRYPTO_LOCK) { lock->mutex.lock(); } else { @@ -180,7 +181,7 @@ SSLContext::SSLContext(const SSLProtocol& protocol) { throw TSSLException("SSL_CTX_new: Unknown protocol"); } - if (ctx_ == NULL) { + if (ctx_ == nullptr) { string errors; buildErrors(errors); throw TSSLException("SSL_CTX_new: " + errors); @@ -196,15 +197,15 @@ SSLContext::SSLContext(const SSLProtocol& protocol) { } SSLContext::~SSLContext() { - if (ctx_ != NULL) { + if (ctx_ != nullptr) { SSL_CTX_free(ctx_); - ctx_ = NULL; + ctx_ = nullptr; } } SSL* SSLContext::createSSL() { SSL* ssl = SSL_new(ctx_); - if (ssl == NULL) { + if (ssl == nullptr) { string errors; buildErrors(errors); throw TSSLException("SSL_new: " + errors); @@ -213,34 +214,37 @@ SSL* SSLContext::createSSL() { } // TSSLSocket implementation -TSSLSocket::TSSLSocket(stdcxx::shared_ptr ctx) - : TSocket(), server_(false), ssl_(NULL), ctx_(ctx) { +TSSLSocket::TSSLSocket(std::shared_ptr ctx, std::shared_ptr config) + : TSocket(config), server_(false), ssl_(nullptr), ctx_(ctx) { init(); } -TSSLSocket::TSSLSocket(stdcxx::shared_ptr ctx, stdcxx::shared_ptr interruptListener) - : TSocket(), server_(false), ssl_(NULL), ctx_(ctx) { +TSSLSocket::TSSLSocket(std::shared_ptr ctx, std::shared_ptr interruptListener, + std::shared_ptr config) + : TSocket(config), server_(false), ssl_(nullptr), ctx_(ctx) { init(); interruptListener_ = interruptListener; } -TSSLSocket::TSSLSocket(stdcxx::shared_ptr ctx, THRIFT_SOCKET socket) - : TSocket(socket), server_(false), ssl_(NULL), ctx_(ctx) { +TSSLSocket::TSSLSocket(std::shared_ptr ctx, THRIFT_SOCKET socket, std::shared_ptr config) + : TSocket(socket, config), server_(false), ssl_(nullptr), ctx_(ctx) { init(); } -TSSLSocket::TSSLSocket(stdcxx::shared_ptr ctx, THRIFT_SOCKET socket, stdcxx::shared_ptr interruptListener) - : TSocket(socket, interruptListener), server_(false), ssl_(NULL), ctx_(ctx) { +TSSLSocket::TSSLSocket(std::shared_ptr ctx, THRIFT_SOCKET socket, std::shared_ptr interruptListener, + std::shared_ptr config) + : TSocket(socket, interruptListener, config), server_(false), ssl_(nullptr), ctx_(ctx) { init(); } -TSSLSocket::TSSLSocket(stdcxx::shared_ptr ctx, string host, int port) - : TSocket(host, port), server_(false), ssl_(NULL), ctx_(ctx) { +TSSLSocket::TSSLSocket(std::shared_ptr ctx, string host, int port, std::shared_ptr config) + : TSocket(host, port, config), server_(false), ssl_(nullptr), ctx_(ctx) { init(); } -TSSLSocket::TSSLSocket(stdcxx::shared_ptr ctx, string host, int port, stdcxx::shared_ptr interruptListener) - : TSocket(host, port), server_(false), ssl_(NULL), ctx_(ctx) { +TSSLSocket::TSSLSocket(std::shared_ptr ctx, string host, int port, std::shared_ptr interruptListener, + std::shared_ptr config) + : TSocket(host, port, config), server_(false), ssl_(nullptr), ctx_(ctx) { init(); interruptListener_ = interruptListener; } @@ -266,8 +270,8 @@ void TSSLSocket::init() { eventSafe_ = false; } -bool TSSLSocket::isOpen() { - if (ssl_ == NULL || !TSocket::isOpen()) { +bool TSSLSocket::isOpen() const { + if (ssl_ == nullptr || !TSocket::isOpen()) { return false; } int shutdown = SSL_get_shutdown(ssl_); @@ -291,11 +295,10 @@ bool TSSLSocket::peek() { if (!checkHandshake()) throw TSSLException("SSL_peek: Handshake is not completed"); int rc; - uint8_t byte; 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) { @@ -318,6 +321,8 @@ bool TSSLSocket::peek() { } else if (rc == 0) { ERR_clear_error(); break; + } else { + break; } } while (true); return (rc > 0); @@ -334,7 +339,7 @@ void TSSLSocket::open() { * Note: This method is not libevent safe. */ void TSSLSocket::close() { - if (ssl_ != NULL) { + if (ssl_ != nullptr) { try { int rc; int errno_copy = 0; @@ -375,7 +380,7 @@ void TSSLSocket::close() { GlobalOutput.printf("SSL_shutdown: %s", te.what()); } SSL_free(ssl_); - ssl_ = NULL; + ssl_ = nullptr; handshakeCompleted_ = false; ERR_remove_state(0); } @@ -389,6 +394,7 @@ void TSSLSocket::close() { * exception incase of failure. */ uint32_t TSSLSocket::read(uint8_t* buf, uint32_t len) { + checkReadBytesAvailable(len); initializeHandshake(); if (!checkHandshake()) throw TTransportException(TTransportException::UNKNOWN, "retry again"); @@ -551,16 +557,17 @@ uint32_t TSSLSocket::write_partial(const uint8_t* buf, uint32_t len) { } void TSSLSocket::flush() { + resetConsumedMessageSize(); // Don't throw exception if not open. Thrift servers close socket twice. - if (ssl_ == NULL) { + if (ssl_ == nullptr) { return; } initializeHandshake(); if (!checkHandshake()) throw TSSLException("BIO_flush: Handshake is not completed"); BIO* bio = SSL_get_wbio(ssl_); - if (bio == NULL) { - throw TSSLException("SSL_get_wbio returns NULL"); + if (bio == nullptr) { + throw TSSLException("SSL_get_wbio returns nullptr"); } if (BIO_flush(bio) != 1) { int errno_copy = THRIFT_GET_SOCKET_ERROR; @@ -597,7 +604,7 @@ void TSSLSocket::initializeHandshake() { return; } - if (ssl_ == NULL) { + if (ssl_ == nullptr) { initializeHandshakeParams(); } @@ -683,19 +690,19 @@ void TSSLSocket::authorize() { } X509* cert = SSL_get_peer_certificate(ssl_); - if (cert == NULL) { + 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_ != NULL) { + if (server() && access_ != nullptr) { throw TSSLException("authorize: certificate required for authorization"); } return; } // certificate is present - if (access_ == NULL) { + if (access_ == nullptr) { X509_free(cert); return; } @@ -720,13 +727,13 @@ void TSSLSocket::authorize() { } // extract subjectAlternativeName - STACK_OF(GENERAL_NAME)* alternatives - = (STACK_OF(GENERAL_NAME)*)X509_get_ext_d2i(cert, NID_subject_alt_name, NULL, NULL); - if (alternatives != NULL) { + 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 == NULL) { + if (name == nullptr) { continue; } char* data = (char*)ASN1_STRING_data(name->d.ia5); @@ -756,7 +763,7 @@ void TSSLSocket::authorize() { // extract commonName X509_NAME* name = X509_get_subject_name(cert); - if (name != NULL) { + if (name != nullptr) { X509_NAME_ENTRY* entry; unsigned char* utf8; int last = -1; @@ -765,7 +772,7 @@ void TSSLSocket::authorize() { if (last == -1) break; entry = X509_NAME_get_entry(name, last); - if (entry == NULL) + if (entry == nullptr) continue; ASN1_STRING* common = X509_NAME_ENTRY_get_data(entry); int size = ASN1_STRING_to_UTF8(&utf8, common); @@ -795,8 +802,8 @@ unsigned int TSSLSocket::waitForEvent(bool wantRead) { bio = SSL_get_wbio(ssl_); } - if (bio == NULL) { - throw TSSLException("SSL_get_?bio returned NULL"); + if (bio == nullptr) { + throw TSSLException("SSL_get_?bio returned nullptr"); } if (BIO_get_fd(bio, &fdSocket) <= 0) { @@ -857,7 +864,7 @@ TSSLSocketFactory::TSSLSocketFactory(SSLProtocol protocol) : server_(false) { randomize(); } count_++; - ctx_ = stdcxx::shared_ptr(new SSLContext(protocol)); + ctx_ = std::make_shared(protocol); } TSSLSocketFactory::~TSSLSocketFactory() { @@ -869,49 +876,49 @@ TSSLSocketFactory::~TSSLSocketFactory() { } } -stdcxx::shared_ptr TSSLSocketFactory::createSocket() { - stdcxx::shared_ptr ssl(new TSSLSocket(ctx_)); +std::shared_ptr TSSLSocketFactory::createSocket() { + std::shared_ptr ssl(new TSSLSocket(ctx_)); setup(ssl); return ssl; } -stdcxx::shared_ptr TSSLSocketFactory::createSocket(stdcxx::shared_ptr interruptListener) { - stdcxx::shared_ptr ssl(new TSSLSocket(ctx_, interruptListener)); +std::shared_ptr TSSLSocketFactory::createSocket(std::shared_ptr interruptListener) { + std::shared_ptr ssl(new TSSLSocket(ctx_, interruptListener)); setup(ssl); return ssl; } -stdcxx::shared_ptr TSSLSocketFactory::createSocket(THRIFT_SOCKET socket) { - stdcxx::shared_ptr ssl(new TSSLSocket(ctx_, socket)); +std::shared_ptr TSSLSocketFactory::createSocket(THRIFT_SOCKET socket) { + std::shared_ptr ssl(new TSSLSocket(ctx_, socket)); setup(ssl); return ssl; } -stdcxx::shared_ptr TSSLSocketFactory::createSocket(THRIFT_SOCKET socket, stdcxx::shared_ptr interruptListener) { - stdcxx::shared_ptr ssl(new TSSLSocket(ctx_, socket, interruptListener)); +std::shared_ptr TSSLSocketFactory::createSocket(THRIFT_SOCKET socket, std::shared_ptr interruptListener) { + std::shared_ptr ssl(new TSSLSocket(ctx_, socket, interruptListener)); setup(ssl); return ssl; } -stdcxx::shared_ptr TSSLSocketFactory::createSocket(const string& host, int port) { - stdcxx::shared_ptr ssl(new TSSLSocket(ctx_, host, port)); +std::shared_ptr TSSLSocketFactory::createSocket(const string& host, int port) { + std::shared_ptr ssl(new TSSLSocket(ctx_, host, port)); setup(ssl); return ssl; } -stdcxx::shared_ptr TSSLSocketFactory::createSocket(const string& host, int port, stdcxx::shared_ptr interruptListener) { - stdcxx::shared_ptr ssl(new TSSLSocket(ctx_, host, port, interruptListener)); +std::shared_ptr TSSLSocketFactory::createSocket(const string& host, int port, std::shared_ptr interruptListener) { + std::shared_ptr ssl(new TSSLSocket(ctx_, host, port, interruptListener)); setup(ssl); return ssl; } -void TSSLSocketFactory::setup(stdcxx::shared_ptr ssl) { +void TSSLSocketFactory::setup(std::shared_ptr ssl) { ssl->server(server()); - if (access_ == NULL && !server()) { - access_ = stdcxx::shared_ptr(new DefaultClientAccessManager); + if (access_ == nullptr && !server()) { + access_ = std::shared_ptr(new DefaultClientAccessManager); } - if (access_ != NULL) { + if (access_ != nullptr) { ssl->access(access_); } } @@ -935,13 +942,13 @@ void TSSLSocketFactory::authenticate(bool required) { } else { mode = SSL_VERIFY_NONE; } - SSL_CTX_set_verify(ctx_->get(), mode, NULL); + SSL_CTX_set_verify(ctx_->get(), mode, nullptr); } void TSSLSocketFactory::loadCertificate(const char* path, const char* format) { - if (path == NULL || format == NULL) { + if (path == nullptr || format == nullptr) { throw TTransportException(TTransportException::BAD_ARGS, - "loadCertificateChain: either or is NULL"); + "loadCertificateChain: either or is nullptr"); } if (strcmp(format, "PEM") == 0) { if (SSL_CTX_use_certificate_chain_file(ctx_->get(), path) == 0) { @@ -955,10 +962,32 @@ void TSSLSocketFactory::loadCertificate(const char* path, const char* format) { } } +void TSSLSocketFactory::loadCertificateFromBuffer(const char* aCertificate, const char* format) { + if (aCertificate == nullptr || format == nullptr) { + throw TTransportException(TTransportException::BAD_ARGS, + "loadCertificate: either or is nullptr"); + } + if (strcmp(format, "PEM") == 0) { + BIO* mem = BIO_new(BIO_s_mem()); + BIO_puts(mem, aCertificate); + X509* cert = PEM_read_bio_X509(mem, nullptr, 0, nullptr); + BIO_free(mem); + + if (SSL_CTX_use_certificate(ctx_->get(), cert) == 0) { + int errno_copy = THRIFT_GET_SOCKET_ERROR; + string errors; + buildErrors(errors, errno_copy); + throw TSSLException("SSL_CTX_use_certificate: " + errors); + } + } else { + throw TSSLException("Unsupported certificate format: " + string(format)); + } +} + void TSSLSocketFactory::loadPrivateKey(const char* path, const char* format) { - if (path == NULL || format == NULL) { + if (path == nullptr || format == nullptr) { throw TTransportException(TTransportException::BAD_ARGS, - "loadPrivateKey: either or is NULL"); + "loadPrivateKey: either or is nullptr"); } if (strcmp(format, "PEM") == 0) { if (SSL_CTX_use_PrivateKey_file(ctx_->get(), path, SSL_FILETYPE_PEM) == 0) { @@ -970,10 +999,32 @@ void TSSLSocketFactory::loadPrivateKey(const char* path, const char* format) { } } +void TSSLSocketFactory::loadPrivateKeyFromBuffer(const char* aPrivateKey, const char* format) { + if (aPrivateKey == nullptr || format == nullptr) { + throw TTransportException(TTransportException::BAD_ARGS, + "loadPrivateKey: either or is nullptr"); + } + if (strcmp(format, "PEM") == 0) { + BIO* mem = BIO_new(BIO_s_mem()); + BIO_puts(mem, aPrivateKey); + EVP_PKEY* cert = PEM_read_bio_PrivateKey(mem, nullptr, nullptr, nullptr); + + BIO_free(mem); + if (SSL_CTX_use_PrivateKey(ctx_->get(), cert) == 0) { + int errno_copy = THRIFT_GET_SOCKET_ERROR; + string errors; + buildErrors(errors, errno_copy); + throw TSSLException("SSL_CTX_use_PrivateKey: " + errors); + } + } else { + throw TSSLException("Unsupported certificate format: " + string(format)); + } +} + void TSSLSocketFactory::loadTrustedCertificates(const char* path, const char* capath) { - if (path == NULL) { + if (path == nullptr) { throw TTransportException(TTransportException::BAD_ARGS, - "loadTrustedCertificates: is NULL"); + "loadTrustedCertificates: is nullptr"); } if (SSL_CTX_load_verify_locations(ctx_->get(), path, capath) == 0) { int errno_copy = THRIFT_GET_SOCKET_ERROR; @@ -983,6 +1034,39 @@ void TSSLSocketFactory::loadTrustedCertificates(const char* path, const char* ca } } +void TSSLSocketFactory::loadTrustedCertificatesFromBuffer(const char* aCertificate, const char* aChain) { + if (aCertificate == nullptr) { + throw TTransportException(TTransportException::BAD_ARGS, + "loadTrustedCertificates: aCertificate is empty"); + } + X509_STORE* vX509Store = SSL_CTX_get_cert_store(ctx_->get()); + BIO* mem = BIO_new(BIO_s_mem()); + BIO_puts(mem, aCertificate); + X509* cert = PEM_read_bio_X509(mem, nullptr, 0, nullptr); + BIO_free(mem); + + if (X509_STORE_add_cert(vX509Store, cert) == 0) { + int errno_copy = THRIFT_GET_SOCKET_ERROR; + string errors; + buildErrors(errors, errno_copy); + throw TSSLException("X509_STORE_add_cert: " + errors); + } + + if (aChain) { + mem = BIO_new(BIO_s_mem()); + BIO_puts(mem, aChain); + cert = PEM_read_bio_X509(mem, nullptr, 0, nullptr); + BIO_free(mem); + + if (SSL_CTX_add_extra_chain_cert(ctx_->get(), cert) == 0) { + int errno_copy = THRIFT_GET_SOCKET_ERROR; + string errors; + buildErrors(errors, errno_copy); + throw TSSLException("X509_STORE_add_cert: " + errors); + } + } +} + void TSSLSocketFactory::randomize() { RAND_poll(); } @@ -993,7 +1077,7 @@ void TSSLSocketFactory::overrideDefaultPasswordCallback() { } int TSSLSocketFactory::passwordCallback(char* password, int size, int, void* data) { - TSSLSocketFactory* factory = (TSSLSocketFactory*)data; + auto* factory = (TSSLSocketFactory*)data; string userPassword; factory->getPassword(userPassword, size); int length = static_cast(userPassword.size()); @@ -1016,7 +1100,7 @@ void buildErrors(string& errors, int errno_copy, int sslerrno) { errors += "; "; } const char* reason = ERR_reason_error_string(errorCode); - if (reason == NULL) { + if (reason == nullptr) { THRIFT_SNPRINTF(message, sizeof(message) - 1, "SSL error # %lu", errorCode); reason = message; } @@ -1046,15 +1130,15 @@ void buildErrors(string& errors, int errno_copy, int sslerrno) { /** * Default implementation of AccessManager */ -Decision DefaultClientAccessManager::verify(const sockaddr_storage& sa) throw() { +Decision DefaultClientAccessManager::verify(const sockaddr_storage& sa) noexcept { (void)sa; return SKIP; } Decision DefaultClientAccessManager::verify(const string& host, const char* name, - int size) throw() { - if (host.empty() || name == NULL || size <= 0) { + int size) noexcept { + if (host.empty() || name == nullptr || size <= 0) { return SKIP; } return (matchName(host.c_str(), name, size) ? ALLOW : SKIP); @@ -1062,7 +1146,7 @@ Decision DefaultClientAccessManager::verify(const string& host, Decision DefaultClientAccessManager::verify(const sockaddr_storage& sa, const char* data, - int size) throw() { + 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); diff --git a/lib/cpp/src/thrift/transport/TSSLSocket.h b/lib/cpp/src/thrift/transport/TSSLSocket.h index ec30cc1491f..5afc571f991 100644 --- a/lib/cpp/src/thrift/transport/TSSLSocket.h +++ b/lib/cpp/src/thrift/transport/TSSLSocket.h @@ -26,7 +26,6 @@ #include #include #include -#include namespace apache { namespace thrift { @@ -70,19 +69,19 @@ void cleanupOpenSSL(); */ class TSSLSocket : public TSocket { public: - ~TSSLSocket(); + ~TSSLSocket() override; /** * TTransport interface. */ - bool isOpen(); - bool peek(); - void open(); - void close(); - bool hasPendingDataToRead(); - 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(); + 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. * @@ -98,7 +97,7 @@ class TSSLSocket : public TSocket { * * @param manager Instance of AccessManager */ - virtual void access(stdcxx::shared_ptr manager) { access_ = manager; } + virtual void access(std::shared_ptr manager) { access_ = manager; } /** * Set eventSafe flag if libevent is used. */ @@ -112,37 +111,40 @@ class TSSLSocket : public TSocket { /** * Constructor. */ - TSSLSocket(stdcxx::shared_ptr ctx); + TSSLSocket(std::shared_ptr ctx, std::shared_ptr config = nullptr); /** * Constructor with an interrupt signal. */ - TSSLSocket(stdcxx::shared_ptr ctx, stdcxx::shared_ptr interruptListener); + TSSLSocket(std::shared_ptr ctx, std::shared_ptr interruptListener, + std::shared_ptr config = nullptr); /** * Constructor, create an instance of TSSLSocket given an existing socket. * * @param socket An existing socket */ - TSSLSocket(stdcxx::shared_ptr ctx, THRIFT_SOCKET socket); + TSSLSocket(std::shared_ptr ctx, THRIFT_SOCKET socket, std::shared_ptr config = nullptr); /** * Constructor, create an instance of TSSLSocket given an existing socket that can be interrupted. * * @param socket An existing socket */ - TSSLSocket(stdcxx::shared_ptr ctx, THRIFT_SOCKET socket, stdcxx::shared_ptr interruptListener); + TSSLSocket(std::shared_ptr ctx, THRIFT_SOCKET socket, std::shared_ptr interruptListener, + std::shared_ptr config = nullptr); /** * Constructor. * * @param host Remote host name * @param port Remote port number */ - TSSLSocket(stdcxx::shared_ptr ctx, std::string host, int port); + TSSLSocket(std::shared_ptr ctx, std::string host, int port, std::shared_ptr config = nullptr); /** * Constructor with an interrupt signal. * * @param host Remote host name * @param port Remote port number */ - TSSLSocket(stdcxx::shared_ptr ctx, std::string host, int port, stdcxx::shared_ptr interruptListener); + TSSLSocket(std::shared_ptr ctx, std::string host, int port, std::shared_ptr interruptListener, + std::shared_ptr config = nullptr); /** * Authorize peer access after SSL handshake completes. */ @@ -171,8 +173,8 @@ class TSSLSocket : public TSocket { bool server_; SSL* ssl_; - stdcxx::shared_ptr ctx_; - stdcxx::shared_ptr access_; + std::shared_ptr ctx_; + std::shared_ptr access_; friend class TSSLSocketFactory; private: @@ -212,37 +214,37 @@ class TSSLSocketFactory { /** * Create an instance of TSSLSocket with a fresh new socket. */ - virtual stdcxx::shared_ptr createSocket(); + virtual std::shared_ptr createSocket(); /** * Create an instance of TSSLSocket with a fresh new socket, which is interruptable. */ - virtual stdcxx::shared_ptr createSocket(stdcxx::shared_ptr interruptListener); + virtual std::shared_ptr createSocket(std::shared_ptr interruptListener); /** * Create an instance of TSSLSocket with the given socket. * * @param socket An existing socket. */ - virtual stdcxx::shared_ptr createSocket(THRIFT_SOCKET socket); + virtual std::shared_ptr createSocket(THRIFT_SOCKET socket); /** * Create an instance of TSSLSocket with the given socket which is interruptable. * * @param socket An existing socket. */ - virtual stdcxx::shared_ptr createSocket(THRIFT_SOCKET socket, stdcxx::shared_ptr interruptListener); + virtual std::shared_ptr createSocket(THRIFT_SOCKET socket, std::shared_ptr interruptListener); /** * Create an instance of TSSLSocket. * * @param host Remote host to be connected to * @param port Remote port to be connected to */ - virtual stdcxx::shared_ptr createSocket(const std::string& host, int port); + virtual std::shared_ptr 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 stdcxx::shared_ptr createSocket(const std::string& host, int port, stdcxx::shared_ptr interruptListener); + virtual std::shared_ptr createSocket(const std::string& host, int port, std::shared_ptr interruptListener); /** * Set ciphers to be used in SSL handshake process. * @@ -262,6 +264,7 @@ class TSSLSocketFactory { * @param format Certificate file format */ virtual void loadCertificate(const char* path, const char* format = "PEM"); + virtual void loadCertificateFromBuffer(const char* aCertificate, const char* format = "PEM"); /** * Load private key. * @@ -269,12 +272,14 @@ class TSSLSocketFactory { * @param format Private key file format */ virtual void loadPrivateKey(const char* path, const char* format = "PEM"); + virtual void loadPrivateKeyFromBuffer(const char* aPrivateKey, 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 = NULL); + virtual void loadTrustedCertificates(const char* path, const char* capath = nullptr); + virtual void loadTrustedCertificatesFromBuffer(const char* aCertificate, const char* aChain = nullptr); /** * Default randomize method. */ @@ -300,13 +305,13 @@ class TSSLSocketFactory { * * @param manager The AccessManager instance */ - virtual void access(stdcxx::shared_ptr manager) { access_ = manager; } + virtual void access(std::shared_ptr manager) { access_ = manager; } static void setManualOpenSSLInitialization(bool manualOpenSSLInitialization) { manualOpenSSLInitialization_ = manualOpenSSLInitialization; } protected: - stdcxx::shared_ptr ctx_; + std::shared_ptr ctx_; /** * Override this method for custom password callback. It may be called @@ -319,11 +324,11 @@ class TSSLSocketFactory { private: bool server_; - stdcxx::shared_ptr access_; + std::shared_ptr access_; static concurrency::Mutex mutex_; static uint64_t count_; - static bool manualOpenSSLInitialization_; - void setup(stdcxx::shared_ptr ssl); + THRIFT_EXPORT static bool manualOpenSSLInitialization_; + void setup(std::shared_ptr ssl); static int passwordCallback(char* password, int size, int, void* data); }; @@ -335,7 +340,7 @@ class TSSLException : public TTransportException { TSSLException(const std::string& message) : TTransportException(TTransportException::INTERNAL_ERROR, message) {} - virtual const char* what() const throw() { + const char* what() const noexcept override { if (message_.empty()) { return "TSSLException"; } else { @@ -374,7 +379,7 @@ class AccessManager { /** * Destructor */ - virtual ~AccessManager() {} + 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 @@ -386,7 +391,7 @@ class AccessManager { * @param sa Peer IP address * @return True if the peer is trusted, false otherwise */ - virtual Decision verify(const sockaddr_storage& /* sa */) throw() { return DENY; } + 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 @@ -402,7 +407,7 @@ class AccessManager { */ virtual Decision verify(const std::string& /* host */, const char* /* name */, - int /* size */) throw() { + int /* size */) noexcept { return DENY; } /** @@ -416,7 +421,7 @@ class AccessManager { */ virtual Decision verify(const sockaddr_storage& /* sa */, const char* /* data */, - int /* size */) throw() { + int /* size */) noexcept { return DENY; } }; @@ -426,9 +431,9 @@ typedef AccessManager::Decision Decision; class DefaultClientAccessManager : public AccessManager { public: // AccessManager interface - Decision verify(const sockaddr_storage& sa) throw(); - Decision verify(const std::string& host, const char* name, int size) throw(); - Decision verify(const sockaddr_storage& sa, const char* data, int size) throw(); + 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; }; } } diff --git a/lib/cpp/src/thrift/transport/TServerSocket.cpp b/lib/cpp/src/thrift/transport/TServerSocket.cpp index 3f11a59e39e..0c32b106e8f 100644 --- a/lib/cpp/src/thrift/transport/TServerSocket.cpp +++ b/lib/cpp/src/thrift/transport/TServerSocket.cpp @@ -20,6 +20,7 @@ #include #include +#include #include #include #ifdef HAVE_SYS_SOCKET_H @@ -42,11 +43,15 @@ #ifdef HAVE_UNISTD_H #include #endif +#ifdef HAVE_SYS_STAT_H +#include +#endif -#include -#include #include -#include +#include +#include +#include +#include #ifndef AF_LOCAL #define AF_LOCAL AF_UNIX @@ -60,6 +65,16 @@ #endif // _WIN32 #endif +#ifdef _WIN32 +// Including Windows.h can conflict with Winsock2 usage, and also +// adds problematic macros like min() and max(). Try to work around: +#define NOMINMAX +#define WIN32_LEAN_AND_MEAN +#include +#undef NOMINMAX +#undef WIN32_LEAN_AND_MEAN +#endif + template inline const SOCKOPT_CAST_T* const_cast_sockopt(const T* v) { return reinterpret_cast(v); @@ -81,27 +96,7 @@ namespace apache { namespace thrift { namespace transport { -using stdcxx::shared_ptr; - -TGetAddrInfoWrapper::TGetAddrInfoWrapper(const char* node, - const char* service, - const struct addrinfo* hints) - : node_(node), service_(service), hints_(hints), res_(NULL) {} - -TGetAddrInfoWrapper::~TGetAddrInfoWrapper() { - if (this->res_ != NULL) - freeaddrinfo(this->res_); -} - -int TGetAddrInfoWrapper::init() { - if (this->res_ == NULL) - return getaddrinfo(this->node_, this->service_, this->hints_, &(this->res_)); - return 0; -} - -const struct addrinfo* TGetAddrInfoWrapper::res() { - return this->res_; -} +using std::shared_ptr; TServerSocket::TServerSocket(int port) : interruptableChildren_(true), @@ -185,6 +180,35 @@ TServerSocket::~TServerSocket() { close(); } +bool TServerSocket::isOpen() const { + if (serverSocket_ == THRIFT_INVALID_SOCKET) + return false; + + if (!listening_) + return false; + + if (!path_.empty() && (path_[0] != '\0')) { + // On some platforms the domain socket file may not be instantly + // available yet, i.e. the Windows file system can be slow. Therefore + // we should check that the domain socket file actually exists. +#ifdef _MSC_VER + // Currently there is a bug in ClangCl on Windows so the stat() call + // does not work. Workaround is a Windows-specific call if file exists: + DWORD const f_attrib = GetFileAttributesA(path_.c_str()); + if (f_attrib == INVALID_FILE_ATTRIBUTES) { +#else + struct THRIFT_STAT path_info; + if (::THRIFT_STAT(path_.c_str(), &path_info) < 0) { +#endif + const std::string vError = "TServerSocket::isOpen(): The domain socket path '" + path_ + "' does not exist (yet)."; + GlobalOutput.perror(vError.c_str(), THRIFT_GET_SOCKET_ERROR); + return false; + } + } + + return true; +} + void TServerSocket::setSendTimeout(int sendTimeout) { sendTimeout_ = sendTimeout; } @@ -224,82 +248,7 @@ void TServerSocket::setInterruptableChildren(bool enable) { 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_ - = stdcxx::shared_ptr(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() ? NULL : &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 == NULL) - break; - } - - if (!path_.empty()) { - serverSocket_ = socket(PF_UNIX, SOCK_STREAM, IPPROTO_IP); - } else if (res != NULL) { - 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); - } +void TServerSocket::_setup_sockopts() { // Set THRIFT_NO_SOCKET_CACHING to prevent 2MSL delay on accept int one = 1; @@ -353,33 +302,6 @@ void TServerSocket::listen() { } } -// 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))) { @@ -389,20 +311,6 @@ void TServerSocket::listen() { 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) { @@ -413,7 +321,6 @@ void TServerSocket::listen() { "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); @@ -422,43 +329,120 @@ void TServerSocket::listen() { "THRIFT_FCNTL() THRIFT_F_SETFL THRIFT_O_NONBLOCK failed", errno_copy); } +} + +void TServerSocket::_setup_unixdomain_sockopts() { +} + +void TServerSocket::_setup_tcp_sockopts() { + int one = 1; + + // 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 + + // 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); + } +} + +void TServerSocket::listen() { +#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(new THRIFT_SOCKET(sv[0]), destroyer_of_fine_sockets); + } + + // tcp == false means Unix Domain socket + bool tcp = (path_.empty()); + + // Validate port number + if (port_ < 0 || port_ > 0xFFFF) { + throw TTransportException(TTransportException::BAD_ARGS, "Specified port is invalid"); + } + + // Resolve host:port strings into an iterable of struct addrinfo* + AddressResolutionHelper resolved_addresses; + if (tcp) { + try { + resolved_addresses.resolve(address_, std::to_string(port_), SOCK_STREAM, + AI_PASSIVE | AI_V4MAPPED); + } catch (const std::system_error& e) { + GlobalOutput.printf("getaddrinfo() -> %d; %s", e.code().value(), e.what()); + close(); + throw TTransportException(TTransportException::NOT_OPEN, + "Could not resolve host for server socket."); + } + } - // 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()) { + if (!tcp) { + // -- Unix Domain Socket -- // -#ifndef _WIN32 + serverSocket_ = socket(PF_UNIX, SOCK_STREAM, IPPROTO_IP); - // Unix Domain Socket - size_t len = path_.size() + 1; - if (len > sizeof(((sockaddr_un*)NULL)->sun_path)) { - errno_copy = THRIFT_GET_SOCKET_ERROR; - GlobalOutput.perror("TSocket::listen() Unix Domain socket path too long", errno_copy); + 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, - "Unix Domain socket path too long", + "Could not create server socket.", errno_copy); } - struct sockaddr_un address; - address.sun_family = AF_UNIX; - memcpy(address.sun_path, path_.c_str(), len); + _setup_sockopts(); + _setup_unixdomain_sockopts(); - socklen_t structlen = static_cast(sizeof(address)); +/* + * TODO: seems that windows now support unix sockets, + * see: https://devblogs.microsoft.com/commandline/af_unix-comes-to-windows/ + */ +#ifndef _WIN32 - 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 - } + struct sockaddr_un address; + socklen_t structlen = fillUnixSocketAddr(address, path_); do { if (0 == ::bind(serverSocket_, (struct sockaddr*)&address, structlen)) { @@ -473,11 +457,48 @@ void TServerSocket::listen() { " Unix Domain socket path not supported"); #endif } else { + + // -- TCP socket -- // + + auto addr_iter = AddressResolutionHelper::Iter{}; + + // Via DNS or somehow else, single hostname can resolve into many addresses. + // Results may contain perhaps a mix of IPv4 and IPv6. Here, we iterate + // over what system gave us, picking the first address that works. do { - if (0 == ::bind(serverSocket_, res->ai_addr, static_cast(res->ai_addrlen))) { + if (!addr_iter) { + // init + recycle over many retries + addr_iter = resolved_addresses.iterate(); + } + auto trybind = *addr_iter++; + + serverSocket_ = socket(trybind->ai_family, trybind->ai_socktype, trybind->ai_protocol); + if (serverSocket_ == -1) { + errno_copy = THRIFT_GET_SOCKET_ERROR; + continue; + } + + _setup_sockopts(); + _setup_tcp_sockopts(); + +#ifdef IPV6_V6ONLY + if (trybind->ai_family == AF_INET6) { + 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 + + if (0 == ::bind(serverSocket_, trybind->ai_addr, static_cast(trybind->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)); @@ -491,20 +512,29 @@ void TServerSocket::listen() { GlobalOutput.perror("TServerSocket::getPort() getsockname() ", errno_copy); } else { if (sa.ss_family == AF_INET6) { - const struct sockaddr_in6* sin = reinterpret_cast(&sa); + const auto* sin = reinterpret_cast(&sa); port_ = ntohs(sin->sin6_port); } else { - const struct sockaddr_in* sin = reinterpret_cast(&sa); + const auto* sin = reinterpret_cast(&sa); port_ = ntohs(sin->sin_port); } } } + } // TCP socket // + + // throw error if socket still wasn't created successfully + if (serverSocket_ == THRIFT_INVALID_SOCKET) { + GlobalOutput.perror("TServerSocket::listen() socket() ", errno_copy); + close(); + throw TTransportException(TTransportException::NOT_OPEN, + "Could not create server socket.", + errno_copy); } // throw an error if we failed to bind properly if (retries > retryLimit_) { char errbuf[1024]; - if (!path_.empty()) { + if (!tcp) { THRIFT_SNPRINTF(errbuf, sizeof(errbuf), "TServerSocket::listen() PATH %s", path_.c_str()); } else { THRIFT_SNPRINTF(errbuf, sizeof(errbuf), "TServerSocket::listen() BIND %d", port_); @@ -528,6 +558,7 @@ void TServerSocket::listen() { } // The socket is now listening! + listening_ = true; } int TServerSocket::getPort() { @@ -642,9 +673,9 @@ shared_ptr TServerSocket::acceptImpl() { shared_ptr TServerSocket::createSocket(THRIFT_SOCKET clientSocket) { if (interruptableChildren_) { - return shared_ptr(new TSocket(clientSocket, pChildInterruptSockReader_)); + return std::make_shared(clientSocket, pChildInterruptSockReader_); } else { - return shared_ptr(new TSocket(clientSocket)); + return std::make_shared(clientSocket); } } @@ -693,6 +724,6 @@ void TServerSocket::close() { pChildInterruptSockReader_.reset(); listening_ = false; } -} -} -} // apache::thrift::transport +} // namespace transport +} // namespace thrift +} // namespace apache diff --git a/lib/cpp/src/thrift/transport/TServerSocket.h b/lib/cpp/src/thrift/transport/TServerSocket.h index 1daaa8299b1..e4659a0363d 100644 --- a/lib/cpp/src/thrift/transport/TServerSocket.h +++ b/lib/cpp/src/thrift/transport/TServerSocket.h @@ -21,7 +21,6 @@ #define _THRIFT_TRANSPORT_TSERVERSOCKET_H_ 1 #include -#include #include #include @@ -39,22 +38,6 @@ 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. @@ -62,7 +45,7 @@ class TGetAddrInfoWrapper { */ class TServerSocket : public TServerTransport { public: - typedef apache::thrift::stdcxx::function socket_func_t; + typedef std::function socket_func_t; const static int DEFAULT_BACKLOG = 1024; @@ -97,7 +80,10 @@ class TServerSocket : public TServerTransport { */ TServerSocket(const std::string& path); - virtual ~TServerSocket(); + ~TServerSocket() override; + + + bool isOpen() const override; void setSendTimeout(int sendTimeout); void setRecvTimeout(int recvTimeout); @@ -137,23 +123,26 @@ class TServerSocket : public TServerTransport { // \throws std::logic_error if listen() has been called void setInterruptableChildren(bool enable); - THRIFT_SOCKET getSocketFD() { return serverSocket_; } + THRIFT_SOCKET getSocketFD() override { return serverSocket_; } int getPort(); - void listen(); - void interrupt(); - void interruptChildren(); - void close(); + void listen() override; + void interrupt() override; + void interruptChildren() override; + void close() override; protected: - stdcxx::shared_ptr acceptImpl(); - virtual stdcxx::shared_ptr createSocket(THRIFT_SOCKET client); + std::shared_ptr acceptImpl() override; + virtual std::shared_ptr createSocket(THRIFT_SOCKET client); bool interruptableChildren_; - stdcxx::shared_ptr pChildInterruptSockReader_; // if interruptableChildren_ this is shared with child TSockets + std::shared_ptr pChildInterruptSockReader_; // if interruptableChildren_ this is shared with child TSockets private: void notify(THRIFT_SOCKET notifySock); + void _setup_sockopts(); + void _setup_unixdomain_sockopts(); + void _setup_tcp_sockopts(); int port_; std::string address_; diff --git a/lib/cpp/src/thrift/transport/TServerTransport.h b/lib/cpp/src/thrift/transport/TServerTransport.h index 9d5a3d556e7..0c566092998 100644 --- a/lib/cpp/src/thrift/transport/TServerTransport.h +++ b/lib/cpp/src/thrift/transport/TServerTransport.h @@ -22,7 +22,6 @@ #include #include -#include namespace apache { namespace thrift { @@ -36,7 +35,12 @@ namespace transport { */ class TServerTransport { public: - virtual ~TServerTransport() {} + virtual ~TServerTransport() = default; + + /** + * Whether this transport is open. + */ + virtual bool isOpen() const { return false; } /** * Starts the server transport listening for new connections. Prior to this @@ -50,16 +54,16 @@ class TServerTransport { * 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 + * opened state. nullptr should never be returned, instead an Exception should * always be thrown. * * @return A new TTransport object * @throws TTransportException if there is an error */ - stdcxx::shared_ptr accept() { - stdcxx::shared_ptr result = acceptImpl(); + std::shared_ptr accept() { + std::shared_ptr result = acceptImpl(); if (!result) { - throw TTransportException("accept() may not return NULL"); + throw TTransportException("accept() may not return nullptr"); } return result; } @@ -97,7 +101,7 @@ class TServerTransport { virtual void close() = 0; protected: - TServerTransport() {} + TServerTransport() = default; /** * Subclasses should implement this function for accept. @@ -105,7 +109,7 @@ class TServerTransport { * @return A newly allocated TTransport object * @throw TTransportException If an error occurs */ - virtual stdcxx::shared_ptr acceptImpl() = 0; + virtual std::shared_ptr acceptImpl() = 0; }; } } diff --git a/lib/cpp/src/thrift/transport/TShortReadTransport.h b/lib/cpp/src/thrift/transport/TShortReadTransport.h index 550b6ba4b01..c99e6a72bfa 100644 --- a/lib/cpp/src/thrift/transport/TShortReadTransport.h +++ b/lib/cpp/src/thrift/transport/TShortReadTransport.h @@ -38,18 +38,21 @@ namespace test { */ class TShortReadTransport : public TVirtualTransport { public: - TShortReadTransport(stdcxx::shared_ptr transport, double full_prob) - : transport_(transport), fullProb_(full_prob) {} + TShortReadTransport(std::shared_ptr transport, double full_prob, + std::shared_ptr config = nullptr) + : TVirtualTransport(config), transport_(transport), fullProb_(full_prob) { + } - bool isOpen() { return transport_->isOpen(); } + bool isOpen() const override { return transport_->isOpen(); } - bool peek() { return transport_->peek(); } + bool peek() override { return transport_->peek(); } - void open() { transport_->open(); } + void open() override { transport_->open(); } - void close() { transport_->close(); } + void close() override { transport_->close(); } uint32_t read(uint8_t* buf, uint32_t len) { + checkReadBytesAvailable(len); if (len == 0) { return 0; } @@ -62,16 +65,22 @@ class TShortReadTransport : public TVirtualTransport { void write(const uint8_t* buf, uint32_t len) { transport_->write(buf, len); } - void flush() { transport_->flush(); } + void flush() override { + resetConsumedMessageSize(); + 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); } + void consume(uint32_t len) { + countConsumedMessageBytes(len); + return transport_->consume(len); + } - stdcxx::shared_ptr getUnderlyingTransport() { return transport_; } + std::shared_ptr getUnderlyingTransport() { return transport_; } protected: - stdcxx::shared_ptr transport_; + std::shared_ptr transport_; double fullProb_; }; } diff --git a/lib/cpp/src/thrift/transport/TSimpleFileTransport.cpp b/lib/cpp/src/thrift/transport/TSimpleFileTransport.cpp index 4b1399e140b..c41affb79b0 100644 --- a/lib/cpp/src/thrift/transport/TSimpleFileTransport.cpp +++ b/lib/cpp/src/thrift/transport/TSimpleFileTransport.cpp @@ -35,8 +35,8 @@ namespace apache { namespace thrift { namespace transport { -TSimpleFileTransport::TSimpleFileTransport(const std::string& path, bool read, bool write) - : TFDTransport(-1, TFDTransport::CLOSE_ON_DESTROY) { +TSimpleFileTransport::TSimpleFileTransport(const std::string& path, bool read, bool write, std::shared_ptr config) + : TFDTransport(-1, TFDTransport::CLOSE_ON_DESTROY, config) { int flags = 0; if (read && write) { flags = O_RDWR; diff --git a/lib/cpp/src/thrift/transport/TSimpleFileTransport.h b/lib/cpp/src/thrift/transport/TSimpleFileTransport.h index 32e18974dd9..24741b0f349 100644 --- a/lib/cpp/src/thrift/transport/TSimpleFileTransport.h +++ b/lib/cpp/src/thrift/transport/TSimpleFileTransport.h @@ -33,7 +33,8 @@ namespace transport { */ class TSimpleFileTransport : public TFDTransport { public: - TSimpleFileTransport(const std::string& path, bool read = true, bool write = false); + TSimpleFileTransport(const std::string& path, bool read = true, bool write = false, + std::shared_ptr config = nullptr); }; } } diff --git a/lib/cpp/src/thrift/transport/TSocket.cpp b/lib/cpp/src/thrift/transport/TSocket.cpp index 18cadbc0611..046c2e6859b 100644 --- a/lib/cpp/src/thrift/transport/TSocket.cpp +++ b/lib/cpp/src/thrift/transport/TSocket.cpp @@ -47,6 +47,7 @@ #include #include #include +#include #ifndef SOCKOPT_CAST_T #ifndef _WIN32 @@ -77,8 +78,9 @@ namespace transport { * */ -TSocket::TSocket(const string& host, int port) - : host_(host), +TSocket::TSocket(const string& host, int port, std::shared_ptr config) + : TVirtualTransport(config), + host_(host), port_(port), socket_(THRIFT_INVALID_SOCKET), peerPort_(0), @@ -92,8 +94,9 @@ TSocket::TSocket(const string& host, int port) maxRecvRetries_(5) { } -TSocket::TSocket(const string& path) - : port_(0), +TSocket::TSocket(const string& path, std::shared_ptr config) + : TVirtualTransport(config), + port_(0), path_(path), socket_(THRIFT_INVALID_SOCKET), peerPort_(0), @@ -108,8 +111,9 @@ TSocket::TSocket(const string& path) cachedPeerAddr_.ipv4.sin_family = AF_UNSPEC; } -TSocket::TSocket() - : port_(0), +TSocket::TSocket(std::shared_ptr config) + : TVirtualTransport(config), + port_(0), socket_(THRIFT_INVALID_SOCKET), peerPort_(0), connTimeout_(0), @@ -123,8 +127,9 @@ TSocket::TSocket() cachedPeerAddr_.ipv4.sin_family = AF_UNSPEC; } -TSocket::TSocket(THRIFT_SOCKET socket) - : port_(0), +TSocket::TSocket(THRIFT_SOCKET socket, std::shared_ptr config) + : TVirtualTransport(config), + port_(0), socket_(socket), peerPort_(0), connTimeout_(0), @@ -144,8 +149,10 @@ TSocket::TSocket(THRIFT_SOCKET socket) #endif } -TSocket::TSocket(THRIFT_SOCKET socket, stdcxx::shared_ptr interruptListener) - : port_(0), +TSocket::TSocket(THRIFT_SOCKET socket, std::shared_ptr interruptListener, + std::shared_ptr config) + : TVirtualTransport(config), + port_(0), socket_(socket), peerPort_(0), interruptListener_(interruptListener), @@ -190,7 +197,7 @@ bool TSocket::hasPendingDataToRead() { return numBytesAvailable > 0; } -bool TSocket::isOpen() { +bool TSocket::isOpen() const { return (socket_ != THRIFT_INVALID_SOCKET); } @@ -322,30 +329,14 @@ void TSocket::openConnection(struct addrinfo* res) { int ret; if (!path_.empty()) { +/* + * TODO: seems that windows now support unix sockets, + * see: https://devblogs.microsoft.com/commandline/af_unix-comes-to-windows/ + */ #ifndef _WIN32 - size_t len = path_.size() + 1; - if (len > sizeof(((sockaddr_un*)NULL)->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); - - socklen_t structlen = static_cast(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 - } + socklen_t structlen = fillUnixSocketAddr(address, path_); ret = connect(socket_, (struct sockaddr*)&address, structlen); #else @@ -432,8 +423,8 @@ void TSocket::open() { void TSocket::unix_open() { if (!path_.empty()) { - // Unix Domain SOcket does not need addrinfo struct, so we pass NULL - openConnection(NULL); + // Unix Domain Socket does not need addrinfo struct, so we pass NULL + openConnection(nullptr); } } @@ -453,8 +444,8 @@ void TSocket::local_open() { } struct addrinfo hints, *res, *res0; - res = NULL; - res0 = NULL; + res = nullptr; + res0 = nullptr; int error; char port[sizeof("65535")]; std::memset(&hints, 0, sizeof(hints)); @@ -465,12 +456,16 @@ void TSocket::local_open() { error = getaddrinfo(host_.c_str(), port, &hints, &res0); + if ( #ifdef _WIN32 - if (error == WSANO_DATA) { + error == WSANO_DATA +#else + error == EAI_NODATA +#endif + ) { hints.ai_flags &= ~AI_ADDRCONFIG; error = getaddrinfo(host_.c_str(), port, &hints, &res0); } -#endif if (error) { string errStr = "TSocket::open() getaddrinfo() " + getSocketInfo() @@ -518,6 +513,7 @@ void TSocket::setSocketFD(THRIFT_SOCKET socket) { } uint32_t TSocket::read(uint8_t* buf, uint32_t len) { + checkReadBytesAvailable(len); if (socket_ == THRIFT_INVALID_SOCKET) { throw TTransportException(TTransportException::NOT_OPEN, "Called read on non-open socket"); } @@ -540,7 +536,7 @@ uint32_t TSocket::read(uint8_t* buf, uint32_t len) { // Read from the socket struct timeval begin; if (recvTimeout_ > 0) { - THRIFT_GETTIMEOFDAY(&begin, NULL); + 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. @@ -592,8 +588,8 @@ uint32_t TSocket::read(uint8_t* buf, uint32_t len) { } // check if this is the lack of resources or timeout case struct timeval end; - THRIFT_GETTIMEOFDAY(&end, NULL); - uint32_t readElapsedMicros = static_cast(((end.tv_sec - begin.tv_sec) * 1000 * 1000) + THRIFT_GETTIMEOFDAY(&end, nullptr); + auto readElapsedMicros = static_cast(((end.tv_sec - begin.tv_sec) * 1000 * 1000) + (end.tv_usec - begin.tv_usec)); if (!eagainThresholdMicros || (readElapsedMicros < eagainThresholdMicros)) { @@ -700,6 +696,10 @@ int TSocket::getPort() { return port_; } +std::string TSocket::getPath() { + return path_; +} + void TSocket::setHost(string host) { host_ = host; } @@ -708,6 +708,10 @@ void TSocket::setPort(int port) { port_ = port; } +void TSocket::setPath(std::string path) { + path_ = path; +} + void TSocket::setLinger(bool on, int linger) { lingerOn_ = on; lingerVal_ = linger; @@ -807,7 +811,7 @@ void TSocket::setMaxRecvRetries(int maxRecvRetries) { maxRecvRetries_ = maxRecvRetries; } -string TSocket::getSocketInfo() { +string TSocket::getSocketInfo() const { std::ostringstream oss; if (path_.empty()) { if (host_.empty() || port_ == 0) { @@ -817,12 +821,16 @@ string TSocket::getSocketInfo() { oss << ""; } } else { - oss << ""; + std::string fmt_path_ = path_; + // Handle printing abstract sockets (first character is a '\0' char): + if (!fmt_path_.empty() && fmt_path_[0] == '\0') + fmt_path_[0] = '@'; + oss << ""; } return oss.str(); } -std::string TSocket::getPeerHost() { +std::string TSocket::getPeerHost() const { if (peerHost_.empty() && path_.empty()) { struct sockaddr_storage addr; struct sockaddr* addrPtr; @@ -834,14 +842,14 @@ std::string TSocket::getPeerHost() { addrPtr = getCachedAddress(&addrLen); - if (addrPtr == NULL) { + if (addrPtr == nullptr) { addrLen = sizeof(addr); if (getpeername(socket_, (sockaddr*)&addr, &addrLen) != 0) { return peerHost_; } addrPtr = (sockaddr*)&addr; - setCachedAddress(addrPtr, addrLen); + const_cast(*this).setCachedAddress(addrPtr, addrLen); } char clienthost[NI_MAXHOST]; @@ -860,7 +868,7 @@ std::string TSocket::getPeerHost() { return peerHost_; } -std::string TSocket::getPeerAddress() { +std::string TSocket::getPeerAddress() const { if (peerAddress_.empty() && path_.empty()) { struct sockaddr_storage addr; struct sockaddr* addrPtr; @@ -872,14 +880,14 @@ std::string TSocket::getPeerAddress() { addrPtr = getCachedAddress(&addrLen); - if (addrPtr == NULL) { + if (addrPtr == nullptr) { addrLen = sizeof(addr); if (getpeername(socket_, (sockaddr*)&addr, &addrLen) != 0) { return peerAddress_; } addrPtr = (sockaddr*)&addr; - setCachedAddress(addrPtr, addrLen); + const_cast(*this).setCachedAddress(addrPtr, addrLen); } char clienthost[NI_MAXHOST]; @@ -899,7 +907,7 @@ std::string TSocket::getPeerAddress() { return peerAddress_; } -int TSocket::getPeerPort() { +int TSocket::getPeerPort() const { getPeerAddress(); return peerPort_; } @@ -937,7 +945,7 @@ sockaddr* TSocket::getCachedAddress(socklen_t* len) const { return (sockaddr*)&cachedPeerAddr_.ipv6; default: - return NULL; + return nullptr; } } @@ -949,7 +957,7 @@ bool TSocket::getUseLowMinRto() { return useLowMinRto_; } -const std::string TSocket::getOrigin() { +const std::string TSocket::getOrigin() const { std::ostringstream oss; oss << getPeerHost() << ":" << getPeerPort(); return oss.str(); diff --git a/lib/cpp/src/thrift/transport/TSocket.h b/lib/cpp/src/thrift/transport/TSocket.h index 66d9e6cd37d..8a224f2de72 100644 --- a/lib/cpp/src/thrift/transport/TSocket.h +++ b/lib/cpp/src/thrift/transport/TSocket.h @@ -52,7 +52,7 @@ class TSocket : public TVirtualTransport { * socket. * */ - TSocket(); + TSocket(std::shared_ptr config = nullptr); /** * Constructs a new socket. Note that this does NOT actually connect the @@ -61,46 +61,47 @@ class TSocket : public TVirtualTransport { * @param host An IP address or hostname to connect to * @param port The port to connect on */ - TSocket(const std::string& host, int port); + TSocket(const std::string& host, int port, std::shared_ptr config = nullptr); /** * 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" + * or a zero-prefixed string to create an abstract domain socket on Linux. */ - TSocket(const std::string& path); + TSocket(const std::string& path, std::shared_ptr config = nullptr); /** * Destroyes the socket object, closing it if necessary. */ - virtual ~TSocket(); + ~TSocket() override; /** * Whether the socket is alive. * * @return Is the socket alive? */ - virtual bool isOpen(); + 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. */ - virtual bool peek(); + bool peek() override; /** * Creates and opens the UNIX socket. * * @throws TTransportException If the socket could not connect */ - virtual void open(); + void open() override; /** * Shuts down communications on the socket. */ - virtual void close(); + void close() override; /** * Determines whether there is pending data to read or not. @@ -149,6 +150,13 @@ class TSocket : public TVirtualTransport { */ int getPort(); + /** + * Get the Unix domain socket path that the socket is connected to + * + * @return std::string path + */ + std::string getPath(); + /** * Set the host that socket will connect to * @@ -163,6 +171,13 @@ class TSocket : public TVirtualTransport { */ void setPort(int port); + /** + * Set the Unix domain socket path for the socket + * + * @param path std::string path + */ + void setPath(std::string path); + /** * Controls whether the linger option is set on the socket. * @@ -208,22 +223,22 @@ class TSocket : public TVirtualTransport { /** * Get socket information formatted as a string */ - std::string getSocketInfo(); + std::string getSocketInfo() const; /** * Returns the DNS name of the host to which the socket is connected */ - std::string getPeerHost(); + std::string getPeerHost() const; /** * Returns the address of the host to which the socket is connected */ - std::string getPeerAddress(); + std::string getPeerAddress() const; /** * Returns the port of the host to which the socket is connected **/ - int getPeerPort(); + int getPeerPort() const; /** * Returns the underlying socket file descriptor. @@ -259,18 +274,19 @@ class TSocket : public TVirtualTransport { * * @return string peer host identifier and port */ - virtual const std::string getOrigin(); + const std::string getOrigin() const override; /** * Constructor to create socket from file descriptor. */ - TSocket(THRIFT_SOCKET socket); + TSocket(THRIFT_SOCKET socket, std::shared_ptr config = nullptr); /** * Constructor to create socket from file descriptor that * can be interrupted safely. */ - TSocket(THRIFT_SOCKET socket, stdcxx::shared_ptr interruptListener); + TSocket(THRIFT_SOCKET socket, std::shared_ptr interruptListener, + std::shared_ptr config = nullptr); /** * Set a cache of the peer address (used when trivially available: e.g. @@ -295,19 +311,19 @@ class TSocket : public TVirtualTransport { THRIFT_SOCKET socket_; /** Peer hostname */ - std::string peerHost_; + mutable std::string peerHost_; /** Peer address */ - std::string peerAddress_; + mutable std::string peerAddress_; /** Peer port */ - int peerPort_; + mutable int peerPort_; /** * A shared socket pointer that will interrupt a blocking read if data * becomes available on it */ - stdcxx::shared_ptr interruptListener_; + std::shared_ptr interruptListener_; /** Connect timeout in ms */ int connTimeout_; diff --git a/lib/cpp/src/thrift/transport/TSocketPool.cpp b/lib/cpp/src/thrift/transport/TSocketPool.cpp index a34d1359914..f56c76ee458 100644 --- a/lib/cpp/src/thrift/transport/TSocketPool.cpp +++ b/lib/cpp/src/thrift/transport/TSocketPool.cpp @@ -21,6 +21,7 @@ #include #include +#include #if __cplusplus >= 201703L #include #endif @@ -35,7 +36,7 @@ namespace apache { namespace thrift { namespace transport { -using stdcxx::shared_ptr; +using std::shared_ptr; /** * TSocketPoolServer implementation @@ -94,8 +95,8 @@ TSocketPool::TSocketPool(const vector >& servers) maxConsecutiveFailures_(1), randomize_(true), alwaysTryLast_(true) { - for (unsigned i = 0; i < servers.size(); ++i) { - addServer(servers[i].first, servers[i].second); + for (const auto & server : servers) { + addServer(server.first, server.second); } } @@ -129,7 +130,7 @@ TSocketPool::~TSocketPool() { } void TSocketPool::addServer(const string& host, int port) { - servers_.push_back(shared_ptr(new TSocketPoolServer(host, port))); + servers_.push_back(std::make_shared(host, port)); } void TSocketPool::addServer(shared_ptr& server) { @@ -177,7 +178,7 @@ void TSocketPool::setCurrentServer(const shared_ptr& server) * 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 */ +/* TODO: without apcu we ignore a lot of functionality from the php version */ void TSocketPool::open() { size_t numServers = servers_.size(); @@ -216,7 +217,7 @@ void TSocketPool::open() { if (server->lastFailTime_ > 0) { // The server was marked as down, so check if enough time has elapsed to retry - time_t elapsedTime = time(NULL) - server->lastFailTime_; + time_t elapsedTime = time(nullptr) - server->lastFailTime_; if (elapsedTime > retryInterval_) { retryIntervalPassed = true; } @@ -245,7 +246,7 @@ void TSocketPool::open() { if (server->consecutiveFailures_ > maxConsecutiveFailures_) { // Mark server as down server->consecutiveFailures_ = 0; - server->lastFailTime_ = time(NULL); + server->lastFailTime_ = time(nullptr); } } } diff --git a/lib/cpp/src/thrift/transport/TSocketPool.h b/lib/cpp/src/thrift/transport/TSocketPool.h index bd49e5574b1..97a2b906332 100644 --- a/lib/cpp/src/thrift/transport/TSocketPool.h +++ b/lib/cpp/src/thrift/transport/TSocketPool.h @@ -92,7 +92,7 @@ class TSocketPool : public TSocket { * * @param servers list of TSocketPoolServers */ - TSocketPool(const std::vector >& servers); + TSocketPool(const std::vector >& servers); /** * Socket pool constructor @@ -105,7 +105,7 @@ class TSocketPool : public TSocket { /** * Destroyes the socket object, closing it if necessary. */ - virtual ~TSocketPool(); + ~TSocketPool() override; /** * Add a server to the pool @@ -115,17 +115,17 @@ class TSocketPool : public TSocket { /** * Add a server to the pool */ - void addServer(stdcxx::shared_ptr& server); + void addServer(std::shared_ptr& server); /** * Set list of servers in this pool */ - void setServers(const std::vector >& servers); + void setServers(const std::vector >& servers); /** * Get list of servers in this pool */ - void getServers(std::vector >& servers); + void getServers(std::vector >& servers); /** * Sets how many times to keep retrying a host in the connect function. @@ -155,21 +155,21 @@ class TSocketPool : public TSocket { /** * Creates and opens the UNIX socket. */ - void open(); + void open() override; /* * Closes the UNIX socket */ - void close(); + void close() override; protected: - void setCurrentServer(const stdcxx::shared_ptr& server); + void setCurrentServer(const std::shared_ptr& server); /** List of servers to connect to */ - std::vector > servers_; + std::vector > servers_; /** Current server */ - stdcxx::shared_ptr currentServer_; + std::shared_ptr currentServer_; /** How many times to retry each host in connect */ int numRetries_; diff --git a/lib/cpp/src/thrift/transport/TSocketUtils.h b/lib/cpp/src/thrift/transport/TSocketUtils.h new file mode 100644 index 00000000000..c9e0e57b882 --- /dev/null +++ b/lib/cpp/src/thrift/transport/TSocketUtils.h @@ -0,0 +1,163 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#ifndef _THRIFT_TRANSPORT_SOCKETUTILS_H_ +#define _THRIFT_TRANSPORT_SOCKETUTILS_H_ 1 + +#include +#include +#include +#include + +#include +#ifdef HAVE_SYS_SOCKET_H +#include +#endif +#ifdef HAVE_NETDB_H +#include +#endif + +#include + +namespace apache { +namespace thrift { + +/** + * A helper to resolve hostnames to struct addrinfo's -- and not leak memory. + * + * Use like this: + * + * apache::thrift::AddressResolutionHelper addresses("localhost", "80"); + * + * for (auto addr : addresses.iterate()) { + * connect(sock, addr->ai_addr, addr->ai_addrlen); + * // ... + * } + */ +struct AddressResolutionHelper { + +private: + struct addrinfo_deleter { + void operator()(addrinfo* addr) { + ::freeaddrinfo(addr); // frees the whole list + } + }; + +public: + using PtrOwnedList = std::unique_ptr; + + struct Iter : std::iterator { + value_type ptr = nullptr; + + Iter() = default; + Iter(const addrinfo* head) : ptr(head) {} + + value_type operator*() const { return ptr; } + + bool operator==(const Iter& other) { return this->ptr == other.ptr; } + bool operator!=(const Iter& other) { return this->ptr != other.ptr; } + + operator bool() { return this->ptr != nullptr; } + bool operator!() { return this->ptr == nullptr; } + + Iter& operator++() { + if (ptr == nullptr) { + throw std::out_of_range("won't go pass end of linked list"); + } + ptr = ptr->ai_next; + return *this; + } + Iter operator++(int) { + Iter tmp(*this); + ++(*this); + return tmp; + } + }; + + struct gai_error : std::error_category { + virtual const char* name() const noexcept override { return "getaddrinfo"; } + virtual std::string message(int code) const override { return THRIFT_GAI_STRERROR(code); } + }; + +private: + PtrOwnedList gai_results; + + addrinfo* query(const std::string& host, const std::string& port, int socktype, int flags) { + addrinfo hints{}; + hints.ai_flags = flags; + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = socktype; + + addrinfo* head; + int ret = ::getaddrinfo(host.empty() ? NULL : host.c_str(), port.c_str(), &hints, &head); + if (ret == 0) { + return head; +#ifdef _WIN32 + } else { + throw std::system_error{THRIFT_GET_SOCKET_ERROR, std::system_category()}; +#else + } else if (ret == EAI_SYSTEM) { + throw std::system_error{THRIFT_GET_SOCKET_ERROR, std::system_category()}; + } else { + throw std::system_error{ret, gai_error()}; +#endif + } + } + +public: + /** + * Constructor. May block. Throws errors. + * + * @param port Port number, or service name, as a string. + * @param socktype Socket type, SOCK_STREAM or SOCK_DGRAM. + * @param flags Standard getaddrinfo() flags. + */ + AddressResolutionHelper(const std::string& host, + const std::string& port, // pass "25" or "smtp" for port 25 + int socktype = SOCK_STREAM, + int flags = AI_V4MAPPED | AI_ADDRCONFIG) + : gai_results(query(host, port, socktype, flags)) {} + + AddressResolutionHelper() = default; + + /** + * Manual query. May block. Throws errors. + * + * @param port Port number, or service name, as a string. + * @param socktype Socket type, SOCK_STREAM or SOCK_DGRAM. + * @param flags Standard getaddrinfo() flags. + */ + AddressResolutionHelper& resolve(const std::string& host, + const std::string& port, // pass "25" or "smtp" for port 25 + int socktype = SOCK_STREAM, + int flags = AI_V4MAPPED | AI_ADDRCONFIG) { + gai_results.reset(query(host, port, socktype, flags)); + return *this; + } + + /** + * Return ForwardIterator to struct addrinfo* results. + */ + Iter iterate() const { return Iter{gai_results.get()}; } +}; + +} // namespace thrift +} // namespace apache + +#endif diff --git a/lib/cpp/src/thrift/transport/TTransport.h b/lib/cpp/src/thrift/transport/TTransport.h index de032909d23..52b3a0a4d32 100644 --- a/lib/cpp/src/thrift/transport/TTransport.h +++ b/lib/cpp/src/thrift/transport/TTransport.h @@ -21,8 +21,9 @@ #define _THRIFT_TRANSPORT_TTRANSPORT_H_ 1 #include -#include +#include #include +#include #include namespace apache { @@ -55,15 +56,24 @@ uint32_t readAll(Transport_& trans, uint8_t* buf, uint32_t len) { */ class TTransport { public: + TTransport(std::shared_ptr config = nullptr) { + if(config == nullptr) { + configuration_ = std::shared_ptr (new TConfiguration()); + } else { + configuration_ = config; + } + resetConsumedMessageSize(); + } + /** * Virtual deconstructor. */ - virtual ~TTransport() {} + virtual ~TTransport() = default; /** * Whether this transport is open. */ - virtual bool isOpen() { return false; } + virtual bool isOpen() const { return false; } /** * Tests whether there is more data to read or if the remote side is @@ -191,7 +201,7 @@ class TTransport { * @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, + * nullptr 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. @@ -209,7 +219,7 @@ class TTransport { T_VIRTUAL_CALL(); return borrow_virt(buf, len); } - virtual const uint8_t* borrow_virt(uint8_t* /* buf */, uint32_t* /* len */) { return NULL; } + 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 @@ -236,13 +246,89 @@ class TTransport { * * The returned value can be used in a log message for example */ - virtual const std::string getOrigin() { return "Unknown"; } + virtual const std::string getOrigin() const { return "Unknown"; } + + std::shared_ptr getConfiguration() { return configuration_; } + + void setConfiguration(std::shared_ptr config) { + if (config != nullptr) configuration_ = config; + } + + /** + * Updates RemainingMessageSize to reflect then known real message size (e.g. framed transport). + * Will throw if we already consumed too many bytes or if the new size is larger than allowed. + * + * @param size real message size + */ + void updateKnownMessageSize(long int size) + { + long int consumed = knownMessageSize_ - remainingMessageSize_; + resetConsumedMessageSize(size); + countConsumedMessageBytes(consumed); + } + + /** + * Throws if there are not enough bytes in the input stream to satisfy a read of numBytes bytes of data + * + * @param numBytes numBytes bytes of data + */ + void checkReadBytesAvailable(long int numBytes) + { + if (remainingMessageSize_ < numBytes) + throw TTransportException(TTransportException::END_OF_FILE, "MaxMessageSize reached"); + } protected: + std::shared_ptr configuration_; + long int remainingMessageSize_; + long int knownMessageSize_; + + inline long int getRemainingMessageSize() { return remainingMessageSize_; } + inline void setRemainingMessageSize(long int remainingMessageSize) { remainingMessageSize_ = remainingMessageSize; } + inline int getMaxMessageSize() { return configuration_->getMaxMessageSize(); } + inline long int getKnownMessageSize() { return knownMessageSize_; } + void setKnownMessageSize(long int knownMessageSize) { knownMessageSize_ = knownMessageSize; } + + /** + * Resets RemainingMessageSize to the configured maximum + * + * @param newSize configured size + */ + void resetConsumedMessageSize(long newSize = -1) + { + // full reset + if (newSize < 0) + { + knownMessageSize_ = getMaxMessageSize(); + remainingMessageSize_ = getMaxMessageSize(); + return; + } + + // update only: message size can shrink, but not grow + if (newSize > knownMessageSize_) + throw TTransportException(TTransportException::END_OF_FILE, "MaxMessageSize reached"); + + knownMessageSize_ = newSize; + remainingMessageSize_ = newSize; + } + /** - * Simple constructor. + * Consumes numBytes from the RemainingMessageSize. + * + * @param numBytes Consumes numBytes */ - TTransport() {} + void countConsumedMessageBytes(long int numBytes) + { + if (remainingMessageSize_ >= numBytes) + { + remainingMessageSize_ -= numBytes; + } + else + { + remainingMessageSize_ = 0; + throw TTransportException(TTransportException::END_OF_FILE, "MaxMessageSize reached"); + } + } }; /** @@ -253,14 +339,14 @@ class TTransport { */ class TTransportFactory { public: - TTransportFactory() {} + TTransportFactory() = default; - virtual ~TTransportFactory() {} + virtual ~TTransportFactory() = default; /** * Default implementation does nothing, just returns the transport given. */ - virtual stdcxx::shared_ptr getTransport(stdcxx::shared_ptr trans) { + virtual std::shared_ptr getTransport(std::shared_ptr trans) { return trans; } }; diff --git a/lib/cpp/src/thrift/transport/TTransportException.cpp b/lib/cpp/src/thrift/transport/TTransportException.cpp index 9e15dbd2875..a527317e08b 100644 --- a/lib/cpp/src/thrift/transport/TTransportException.cpp +++ b/lib/cpp/src/thrift/transport/TTransportException.cpp @@ -28,7 +28,7 @@ namespace apache { namespace thrift { namespace transport { -const char* TTransportException::what() const throw() { +const char* TTransportException::what() const noexcept { if (message_.empty()) { switch (type_) { case UNKNOWN: diff --git a/lib/cpp/src/thrift/transport/TTransportException.h b/lib/cpp/src/thrift/transport/TTransportException.h index dbbb9711c5c..dd5f361f151 100644 --- a/lib/cpp/src/thrift/transport/TTransportException.h +++ b/lib/cpp/src/thrift/transport/TTransportException.h @@ -49,7 +49,8 @@ class TTransportException : public apache::thrift::TException { INTERRUPTED = 4, BAD_ARGS = 5, CORRUPTED_DATA = 6, - INTERNAL_ERROR = 7 + INTERNAL_ERROR = 7, + CLIENT_DISCONNECT = 8 }; TTransportException() : apache::thrift::TException(), type_(UNKNOWN) {} @@ -65,7 +66,7 @@ class TTransportException : public apache::thrift::TException { TTransportException(TTransportExceptionType type, const std::string& message, int errno_copy) : apache::thrift::TException(message + ": " + TOutput::strerror_s(errno_copy)), type_(type) {} - virtual ~TTransportException() throw() {} + ~TTransportException() noexcept override = default; /** * Returns an error code that provides information about the type of error @@ -73,9 +74,9 @@ class TTransportException : public apache::thrift::TException { * * @return Error code */ - TTransportExceptionType getType() const throw() { return type_; } + TTransportExceptionType getType() const noexcept { return type_; } - virtual const char* what() const throw(); + const char* what() const noexcept override; protected: /** Just like strerror_r but returns a C++ string object. */ diff --git a/lib/cpp/src/thrift/transport/TTransportUtils.cpp b/lib/cpp/src/thrift/transport/TTransportUtils.cpp index 5a236dc308c..427a2e7c18d 100644 --- a/lib/cpp/src/thrift/transport/TTransportUtils.cpp +++ b/lib/cpp/src/thrift/transport/TTransportUtils.cpp @@ -26,6 +26,7 @@ namespace thrift { namespace transport { uint32_t TPipedTransport::read(uint8_t* buf, uint32_t len) { + checkReadBytesAvailable(len); uint32_t need = len; // We don't have enough data yet @@ -41,8 +42,8 @@ uint32_t TPipedTransport::read(uint8_t* buf, uint32_t len) { // Double the size of the underlying buffer if it is full if (rLen_ == rBufSize_) { rBufSize_ *= 2; - uint8_t *tmpBuf = (uint8_t*)std::realloc(rBuf_, sizeof(uint8_t) * rBufSize_); - if (tmpBuf == NULL) { + auto *tmpBuf = (uint8_t*)std::realloc(rBuf_, sizeof(uint8_t) * rBufSize_); + if (tmpBuf == nullptr) { throw std::bad_alloc(); } rBuf_ = tmpBuf; @@ -77,8 +78,8 @@ void TPipedTransport::write(const uint8_t* buf, uint32_t len) { while ((len + wLen_) >= newBufSize) { newBufSize *= 2; } - uint8_t *tmpBuf= (uint8_t*)std::realloc(wBuf_, sizeof(uint8_t) * newBufSize); - if (tmpBuf == NULL) { + auto *tmpBuf= (uint8_t*)std::realloc(wBuf_, sizeof(uint8_t) * newBufSize); + if (tmpBuf == nullptr) { throw std::bad_alloc(); } wBuf_ = tmpBuf; @@ -103,15 +104,15 @@ void TPipedTransport::flush() { } TPipedFileReaderTransport::TPipedFileReaderTransport( - stdcxx::shared_ptr srcTrans, - stdcxx::shared_ptr dstTrans) - : TPipedTransport(srcTrans, dstTrans), srcTrans_(srcTrans) { + std::shared_ptr srcTrans, + std::shared_ptr dstTrans, + std::shared_ptr config) + : TPipedTransport(srcTrans, dstTrans, config), srcTrans_(srcTrans) { } -TPipedFileReaderTransport::~TPipedFileReaderTransport() { -} +TPipedFileReaderTransport::~TPipedFileReaderTransport() = default; -bool TPipedFileReaderTransport::isOpen() { +bool TPipedFileReaderTransport::isOpen() const { return TPipedTransport::isOpen(); } @@ -132,6 +133,7 @@ uint32_t TPipedFileReaderTransport::read(uint8_t* buf, uint32_t len) { } uint32_t TPipedFileReaderTransport::readAll(uint8_t* buf, uint32_t len) { + checkReadBytesAvailable(len); uint32_t have = 0; uint32_t get = 0; diff --git a/lib/cpp/src/thrift/transport/TTransportUtils.h b/lib/cpp/src/thrift/transport/TTransportUtils.h index f3b4c5acbf7..68c25f4c957 100644 --- a/lib/cpp/src/thrift/transport/TTransportUtils.h +++ b/lib/cpp/src/thrift/transport/TTransportUtils.h @@ -42,13 +42,13 @@ namespace transport { */ class TNullTransport : public TVirtualTransport { public: - TNullTransport() {} + TNullTransport() = default; - ~TNullTransport() {} + ~TNullTransport() override = default; - bool isOpen() { return true; } + bool isOpen() const override { return true; } - void open() {} + void open() override {} void write(const uint8_t* /* buf */, uint32_t /* len */) { return; } }; @@ -63,8 +63,10 @@ class TNullTransport : public TVirtualTransport { */ class TPipedTransport : virtual public TTransport { public: - TPipedTransport(stdcxx::shared_ptr srcTrans, stdcxx::shared_ptr dstTrans) - : srcTrans_(srcTrans), + TPipedTransport(std::shared_ptr srcTrans, std::shared_ptr dstTrans, + std::shared_ptr config = nullptr) + : TTransport(config), + srcTrans_(srcTrans), dstTrans_(dstTrans), rBufSize_(512), rPos_(0), @@ -77,19 +79,21 @@ class TPipedTransport : virtual public TTransport { pipeOnWrite_ = false; rBuf_ = (uint8_t*)std::malloc(sizeof(uint8_t) * rBufSize_); - if (rBuf_ == NULL) { + if (rBuf_ == nullptr) { throw std::bad_alloc(); } wBuf_ = (uint8_t*)std::malloc(sizeof(uint8_t) * wBufSize_); - if (wBuf_ == NULL) { + if (wBuf_ == nullptr) { throw std::bad_alloc(); } } - TPipedTransport(stdcxx::shared_ptr srcTrans, - stdcxx::shared_ptr dstTrans, - uint32_t sz) - : srcTrans_(srcTrans), + TPipedTransport(std::shared_ptr srcTrans, + std::shared_ptr dstTrans, + uint32_t sz, + std::shared_ptr config = nullptr) + : TTransport(config), + srcTrans_(srcTrans), dstTrans_(dstTrans), rBufSize_(512), rPos_(0), @@ -98,29 +102,29 @@ class TPipedTransport : virtual public TTransport { wLen_(0) { rBuf_ = (uint8_t*)std::malloc(sizeof(uint8_t) * rBufSize_); - if (rBuf_ == NULL) { + if (rBuf_ == nullptr) { throw std::bad_alloc(); } wBuf_ = (uint8_t*)std::malloc(sizeof(uint8_t) * wBufSize_); - if (wBuf_ == NULL) { + if (wBuf_ == nullptr) { throw std::bad_alloc(); } } - ~TPipedTransport() { + ~TPipedTransport() override { std::free(rBuf_); std::free(wBuf_); } - bool isOpen() { return srcTrans_->isOpen(); } + bool isOpen() const override { return srcTrans_->isOpen(); } - bool peek() { + bool peek() override { if (rPos_ >= rLen_) { // Double the size of the underlying buffer if it is full if (rLen_ == rBufSize_) { rBufSize_ *= 2; - uint8_t * tmpBuf = (uint8_t*)std::realloc(rBuf_, sizeof(uint8_t) * rBufSize_); - if (tmpBuf == NULL) { + auto * tmpBuf = (uint8_t*)std::realloc(rBuf_, sizeof(uint8_t) * rBufSize_); + if (tmpBuf == nullptr) { throw std::bad_alloc(); } rBuf_ = tmpBuf; @@ -132,9 +136,9 @@ class TPipedTransport : virtual public TTransport { return (rLen_ > rPos_); } - void open() { srcTrans_->open(); } + void open() override { srcTrans_->open(); } - void close() { srcTrans_->close(); } + void close() override { srcTrans_->close(); } void setPipeOnRead(bool pipeVal) { pipeOnRead_ = pipeVal; } @@ -142,7 +146,7 @@ class TPipedTransport : virtual public TTransport { uint32_t read(uint8_t* buf, uint32_t len); - uint32_t readEnd() { + uint32_t readEnd() override { if (pipeOnRead_) { dstTrans_->write(rBuf_, rPos_); @@ -164,7 +168,7 @@ class TPipedTransport : virtual public TTransport { void write(const uint8_t* buf, uint32_t len); - uint32_t writeEnd() { + uint32_t writeEnd() override { if (pipeOnWrite_) { dstTrans_->write(wBuf_, wLen_); dstTrans_->flush(); @@ -172,21 +176,21 @@ class TPipedTransport : virtual public TTransport { return wLen_; } - void flush(); + void flush() override; - stdcxx::shared_ptr getTargetTransport() { return dstTrans_; } + std::shared_ptr 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. */ - virtual uint32_t read_virt(uint8_t* buf, uint32_t len) { return this->read(buf, len); } - virtual void write_virt(const uint8_t* buf, uint32_t len) { this->write(buf, len); } + 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: - stdcxx::shared_ptr srcTrans_; - stdcxx::shared_ptr dstTrans_; + std::shared_ptr srcTrans_; + std::shared_ptr dstTrans_; uint8_t* rBuf_; uint32_t rBufSize_; @@ -207,21 +211,21 @@ class TPipedTransport : virtual public TTransport { */ class TPipedTransportFactory : public TTransportFactory { public: - TPipedTransportFactory() {} - TPipedTransportFactory(stdcxx::shared_ptr dstTrans) { + TPipedTransportFactory() = default; + TPipedTransportFactory(std::shared_ptr dstTrans) { initializeTargetTransport(dstTrans); } - virtual ~TPipedTransportFactory() {} + ~TPipedTransportFactory() override = default; /** * Wraps the base transport into a piped transport. */ - virtual stdcxx::shared_ptr getTransport(stdcxx::shared_ptr srcTrans) { - return stdcxx::shared_ptr(new TPipedTransport(srcTrans, dstTrans_)); + std::shared_ptr getTransport(std::shared_ptr srcTrans) override { + return std::shared_ptr(new TPipedTransport(srcTrans, dstTrans_)); } - virtual void initializeTargetTransport(stdcxx::shared_ptr dstTrans) { - if (dstTrans_.get() == NULL) { + virtual void initializeTargetTransport(std::shared_ptr dstTrans) { + if (dstTrans_.get() == nullptr) { dstTrans_ = dstTrans; } else { throw TException("Target transport already initialized"); @@ -229,7 +233,7 @@ class TPipedTransportFactory : public TTransportFactory { } protected: - stdcxx::shared_ptr dstTrans_; + std::shared_ptr dstTrans_; }; /** @@ -240,44 +244,45 @@ class TPipedTransportFactory : public TTransportFactory { */ class TPipedFileReaderTransport : public TPipedTransport, public TFileReaderTransport { public: - TPipedFileReaderTransport(stdcxx::shared_ptr srcTrans, - stdcxx::shared_ptr dstTrans); + TPipedFileReaderTransport(std::shared_ptr srcTrans, + std::shared_ptr dstTrans, + std::shared_ptr config = nullptr); - ~TPipedFileReaderTransport(); + ~TPipedFileReaderTransport() override; // TTransport functions - bool isOpen(); - bool peek(); - void open(); - void close(); + 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(); + uint32_t readEnd() override; void write(const uint8_t* buf, uint32_t len); - uint32_t writeEnd(); - void flush(); + uint32_t writeEnd() override; + void flush() override; // TFileReaderTransport functions - int32_t getReadTimeout(); - void setReadTimeout(int32_t readTimeout); - uint32_t getNumChunks(); - uint32_t getCurChunk(); - void seekToChunk(int32_t chunk); - void seekToEnd(); + 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. */ - virtual uint32_t read_virt(uint8_t* buf, uint32_t len) { return this->read(buf, len); } - virtual uint32_t readAll_virt(uint8_t* buf, uint32_t len) { return this->readAll(buf, len); } - virtual void write_virt(const uint8_t* buf, uint32_t len) { this->write(buf, len); } + 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(); - stdcxx::shared_ptr srcTrans_; + std::shared_ptr srcTrans_; }; /** @@ -286,24 +291,24 @@ class TPipedFileReaderTransport : public TPipedTransport, public TFileReaderTran */ class TPipedFileReaderTransportFactory : public TPipedTransportFactory { public: - TPipedFileReaderTransportFactory() {} - TPipedFileReaderTransportFactory(stdcxx::shared_ptr dstTrans) + TPipedFileReaderTransportFactory() = default; + TPipedFileReaderTransportFactory(std::shared_ptr dstTrans) : TPipedTransportFactory(dstTrans) {} - virtual ~TPipedFileReaderTransportFactory() {} + ~TPipedFileReaderTransportFactory() override = default; - stdcxx::shared_ptr getTransport(stdcxx::shared_ptr srcTrans) { - stdcxx::shared_ptr pFileReaderTransport - = stdcxx::dynamic_pointer_cast(srcTrans); - if (pFileReaderTransport.get() != NULL) { + std::shared_ptr getTransport(std::shared_ptr srcTrans) override { + std::shared_ptr pFileReaderTransport + = std::dynamic_pointer_cast(srcTrans); + if (pFileReaderTransport.get() != nullptr) { return getFileReaderTransport(pFileReaderTransport); } else { - return stdcxx::shared_ptr(); + return std::shared_ptr(); } } - stdcxx::shared_ptr getFileReaderTransport( - stdcxx::shared_ptr srcTrans) { - return stdcxx::shared_ptr( + std::shared_ptr getFileReaderTransport( + std::shared_ptr srcTrans) { + return std::shared_ptr( new TPipedFileReaderTransport(srcTrans, dstTrans_)); } }; diff --git a/lib/cpp/src/thrift/transport/TVirtualTransport.h b/lib/cpp/src/thrift/transport/TVirtualTransport.h index 0cacf61d0b7..44bfa1315e5 100644 --- a/lib/cpp/src/thrift/transport/TVirtualTransport.h +++ b/lib/cpp/src/thrift/transport/TVirtualTransport.h @@ -57,7 +57,7 @@ class TTransportDefaults : public TTransport { void consume(uint32_t len) { this->TTransport::consume_virt(len); } protected: - TTransportDefaults() {} + TTransportDefaults(std::shared_ptr config = nullptr) : TTransport(config) {} }; /** @@ -84,23 +84,23 @@ class TVirtualTransport : public Super_ { * Implementations of the *_virt() functions, to call the subclass's * non-virtual implementation function. */ - virtual uint32_t read_virt(uint8_t* buf, uint32_t len) { + uint32_t read_virt(uint8_t* buf, uint32_t len) override { return static_cast(this)->read(buf, len); } - virtual uint32_t readAll_virt(uint8_t* buf, uint32_t len) { + uint32_t readAll_virt(uint8_t* buf, uint32_t len) override { return static_cast(this)->readAll(buf, len); } - virtual void write_virt(const uint8_t* buf, uint32_t len) { + void write_virt(const uint8_t* buf, uint32_t len) override { static_cast(this)->write(buf, len); } - virtual const uint8_t* borrow_virt(uint8_t* buf, uint32_t* len) { + const uint8_t* borrow_virt(uint8_t* buf, uint32_t* len) override { return static_cast(this)->borrow(buf, len); } - virtual void consume_virt(uint32_t len) { static_cast(this)->consume(len); } + void consume_virt(uint32_t len) override { static_cast(this)->consume(len); } /* * Provide a default readAll() implementation that invokes @@ -113,12 +113,12 @@ class TVirtualTransport : public Super_ { * the correct parent implementation, if desired. */ uint32_t readAll(uint8_t* buf, uint32_t len) { - Transport_* trans = static_cast(this); + auto* trans = static_cast(this); return ::apache::thrift::transport::readAll(*trans, buf, len); } protected: - TVirtualTransport() {} + TVirtualTransport() : Super_() {} /* * Templatized constructors, to allow arguments to be passed to the Super_ diff --git a/lib/cpp/src/thrift/concurrency/PlatformThreadFactory.h b/lib/cpp/src/thrift/transport/TWebSocketServer.cpp similarity index 51% rename from lib/cpp/src/thrift/concurrency/PlatformThreadFactory.h rename to lib/cpp/src/thrift/transport/TWebSocketServer.cpp index 545b5727060..9822a7faec8 100644 --- a/lib/cpp/src/thrift/concurrency/PlatformThreadFactory.h +++ b/lib/cpp/src/thrift/transport/TWebSocketServer.cpp @@ -17,36 +17,36 @@ * under the License. */ -#ifndef _THRIFT_CONCURRENCY_PLATFORMTHREADFACTORY_H_ -#define _THRIFT_CONCURRENCY_PLATFORMTHREADFACTORY_H_ 1 - -// clang-format off -#include -#if USE_BOOST_THREAD -# include -#elif USE_STD_THREAD -# include -#else -# include -#endif -// clang-format on +#include +#include +#include + +#include +#include + +#include + +using std::string; namespace apache { namespace thrift { -namespace concurrency { - -// clang-format off -#if USE_BOOST_THREAD - typedef BoostThreadFactory PlatformThreadFactory; -#elif USE_STD_THREAD - typedef StdThreadFactory PlatformThreadFactory; -#else - typedef PosixThreadFactory PlatformThreadFactory; -#endif -// clang-format on +namespace transport { -} -} -} // apache::thrift::concurrency +std::string base64Encode(unsigned char* data, int length) { + std::unique_ptr> base64(BIO_new(BIO_f_base64()), + [](BIO* b) { BIO_free_all(b); }); + BIO_set_flags(base64.get(), BIO_FLAGS_BASE64_NO_NL); + + BIO* dest = BIO_new(BIO_s_mem()); + BIO_push(base64.get(), dest); + BIO_write(base64.get(), data, length); + int ret = BIO_flush(base64.get()); + THRIFT_UNUSED_VARIABLE(ret); -#endif // #ifndef _THRIFT_CONCURRENCY_PLATFORMTHREADFACTORY_H_ + char* encoded; + length = BIO_get_mem_data(dest, &encoded); + return std::string(encoded, length); +} +} // namespace transport +} // namespace thrift +} // namespace apache diff --git a/lib/cpp/src/thrift/transport/TWebSocketServer.h b/lib/cpp/src/thrift/transport/TWebSocketServer.h new file mode 100644 index 00000000000..7f39f36b95e --- /dev/null +++ b/lib/cpp/src/thrift/transport/TWebSocketServer.h @@ -0,0 +1,416 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT 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_TWEBSOCKETSERVER_H_ +#define _THRIFT_TRANSPORT_TWEBSOCKETSERVER_H_ 1 + +#include +#include +#include + +#include + +#include +#include +#include +#include +#if defined(_MSC_VER) || defined(__MINGW32__) +#include +#define THRIFT_strncasecmp(str1, str2, len) _strnicmp(str1, str2, len) +#define THRIFT_strcasestr(haystack, needle) StrStrIA(haystack, needle) +#else +#define THRIFT_strncasecmp(str1, str2, len) strncasecmp(str1, str2, len) +#define THRIFT_strcasestr(haystack, needle) strcasestr(haystack, needle) +#endif +#if defined(__CYGWIN__) +#include +#endif + +using std::string; + +namespace apache { +namespace thrift { +namespace transport { + +std::string base64Encode(unsigned char* data, int length); + +template +class TWebSocketServer : public THttpServer { +public: + TWebSocketServer(std::shared_ptr transport, std::shared_ptr config = nullptr) + : THttpServer(transport, config) { + resetHandshake(); + } + + ~TWebSocketServer() override = default; + + uint32_t readAll_virt(uint8_t* buf, uint32_t len) override { + // If we do not have a good handshake, the client will attempt one. + if (!handshakeComplete()) { + resetHandshake(); + THttpServer::read(buf, len); + // If we did not get everything we expected, the handshake failed + // and we need to send a 400 response back. + if (!handshakeComplete()) { + sendBadRequest(); + return 0; + } + // Otherwise, send back the 101 response. + THttpServer::flush(); + } + + uint32_t want = len; + auto have = readBuffer_.available_read(); + + // 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 && have >= want) { + return readBuffer_.read(buf, want); + } + + // Read another frame. + if (!readFrame()) { + // EOF. No frame available. + return 0; + } + + // Hand over whatever we have. + uint32_t give = (std::min)(want, readBuffer_.available_read()); + return readBuffer_.read(buf, give); + } + + void flush() override { + resetConsumedMessageSize(); + writeFrameHeader(); + uint8_t* buffer; + uint32_t length; + writeBuffer_.getBuffer(&buffer, &length); + transport_->write(buffer, length); + transport_->flush(); + writeBuffer_.resetBuffer(); + } + +protected: + std::string getHeader(uint32_t len) override { + THRIFT_UNUSED_VARIABLE(len); + std::ostringstream h; + h << "HTTP/1.1 101 Switching Protocols" << CRLF << "Server: Thrift/" << PACKAGE_VERSION << CRLF + << "Upgrade: websocket" << CRLF << "Connection: Upgrade" << CRLF + << "Sec-WebSocket-Accept: " << acceptKey_ << CRLF << CRLF; + return h.str(); + } + + void parseHeader(char* header) override { + char* colon = strchr(header, ':'); + if (colon == nullptr) { + return; + } + size_t sz = colon - header; + char* value = colon + 1; + + if (THRIFT_strncasecmp(header, "Upgrade", sz) == 0) { + if (THRIFT_strcasestr(value, "websocket") != nullptr) { + upgrade_ = true; + } + } else if (THRIFT_strncasecmp(header, "Connection", sz) == 0) { + if (THRIFT_strcasestr(value, "Upgrade") != nullptr) { + connection_ = true; + } + } else if (THRIFT_strncasecmp(header, "Sec-WebSocket-Key", sz) == 0) { + std::string toHash = value + 1; + toHash += "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"; + unsigned char hash[20]; + SHA1((const unsigned char*)toHash.c_str(), toHash.length(), hash); + acceptKey_ = base64Encode(hash, 20); + secWebSocketKey_ = true; + } else if (THRIFT_strncasecmp(header, "Sec-WebSocket-Version", sz) == 0) { + if (THRIFT_strcasestr(value, "13") != nullptr) { + secWebSocketVersion_ = true; + } + } + } + + bool parseStatusLine(char* status) override { + 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, "GET") == 0) { + // GET method ok, looking for content. + return true; + } + throw TTransportException(string("Bad Status (unsupported method): ") + status); + } + +private: + enum class CloseCode : uint16_t { + NormalClosure = 1000, + GoingAway = 1001, + ProtocolError = 1002, + UnsupportedDataType = 1003, + NoStatusCode = 1005, + AbnormalClosure = 1006, + InvalidData = 1007, + PolicyViolation = 1008, + MessageTooBig = 1009, + ExtensionExpected = 1010, + UnexpectedError = 1011, + NotSecure = 1015 + }; + + enum class Opcode : uint8_t { + Continuation = 0x0, + Text = 0x1, + Binary = 0x2, + Close = 0x8, + Ping = 0x9, + Pong = 0xA + }; + + void failConnection(CloseCode reason) { + writeFrameHeader(Opcode::Close); + auto buffer = htons(static_cast(reason)); + transport_->write(reinterpret_cast(&buffer), 2); + transport_->flush(); + transport_->close(); + } + + bool handshakeComplete() { + return upgrade_ && connection_ && secWebSocketKey_ && secWebSocketVersion_; + } + + void pong() { + writeFrameHeader(Opcode::Pong); + uint8_t* buffer; + uint32_t size; + readBuffer_.getBuffer(&buffer, &size); + transport_->write(buffer, size); + transport_->flush(); + } + + bool readFrame() { + uint8_t headerBuffer[8]; + + auto read = transport_->read(headerBuffer, 2); + if (read < 2) { + return false; + } + // Since Thrift has its own message end marker and we read frame by frame, + // it doesn't really matter if the frame is marked as FIN. + // Capture it only for debugging only. + auto fin = (headerBuffer[0] & 0x80) != 0; + THRIFT_UNUSED_VARIABLE(fin); + + // RSV1, RSV2, RSV3 + if ((headerBuffer[0] & 0x70) != 0) { + failConnection(CloseCode::ProtocolError); + throw TTransportException(TTransportException::CORRUPTED_DATA, + "Reserved bits must be zeroes"); + } + + auto opcode = (Opcode)(headerBuffer[0] & 0x0F); + + // Mask + if ((headerBuffer[1] & 0x80) == 0) { + failConnection(CloseCode::ProtocolError); + throw TTransportException(TTransportException::CORRUPTED_DATA, + "Messages from the client must be masked"); + } + + // Read the length + uint64_t payloadLength = headerBuffer[1] & 0x7F; + if (payloadLength == 126) { + read = transport_->read(headerBuffer, 2); + if (read < 2) { + return false; + } + payloadLength = ntohs(*reinterpret_cast(headerBuffer)); + } else if (payloadLength == 127) { + read = transport_->read(headerBuffer, 8); + if (read < 8) { + return false; + } + payloadLength = THRIFT_ntohll(*reinterpret_cast(headerBuffer)); + if ((payloadLength & 0x8000000000000000) != 0) { + failConnection(CloseCode::ProtocolError); + throw TTransportException( + TTransportException::CORRUPTED_DATA, + "The most significant bit of the payload length must be zero"); + } + } + + // size_t is smaller than a ulong on a 32-bit system + if (payloadLength > UINT32_MAX) { + failConnection(CloseCode::MessageTooBig); + return false; + } + + auto length = static_cast(payloadLength); + + if (length > 0) { + // Read the masking key + read = transport_->read(headerBuffer, 4); + if (read < 4) { + return false; + } + + readBuffer_.resetBuffer(length); + uint8_t* buffer = readBuffer_.getWritePtr(length); + read = transport_->read(buffer, length); + readBuffer_.wroteBytes(read); + if (read < length) { + return false; + } + + // Unmask the data + for (size_t i = 0; i < length; i++) { + buffer[i] ^= headerBuffer[i % 4]; + } + + T_DEBUG("FIN=%d, Opcode=%X, length=%d, payload=%s", fin, opcode, length, + binary ? readBuffer_.toHexString() : cast(string) readBuffer_); + } + + switch (opcode) { + case Opcode::Close: + if (length >= 2) { + uint8_t buffer[2]; + readBuffer_.read(buffer, 2); + CloseCode closeCode = static_cast(ntohs(*reinterpret_cast(buffer))); + THRIFT_UNUSED_VARIABLE(closeCode); + string closeReason = readBuffer_.readAsString(length - 2); + T_DEBUG("Connection closed: %d %s", closeCode, closeReason); + } + transport_->close(); + return false; + case Opcode::Ping: + pong(); + return readFrame(); + default: + return true; + } + } + + void resetHandshake() { + connection_ = false; + secWebSocketKey_ = false; + secWebSocketVersion_ = false; + upgrade_ = false; + } + + void sendBadRequest() { + std::ostringstream h; + h << "HTTP/1.1 400 Bad Request" << CRLF << "Server: Thrift/" << PACKAGE_VERSION << CRLF << CRLF; + std::string header = h.str(); + transport_->write(reinterpret_cast(header.data()), static_cast(header.length())); + transport_->flush(); + transport_->close(); + } + + void writeFrameHeader(Opcode opcode = Opcode::Continuation) { + uint32_t headerSize = 1; + uint32_t length = writeBuffer_.available_read(); + if (length < 126) { + ++headerSize; + } else if (length < 65536) { + headerSize += 3; + } else { + headerSize += 9; + } + // The server does not mask the response + + uint8_t* header = static_cast(alloca(headerSize)); + if (opcode == Opcode::Continuation) { + opcode = binary ? Opcode::Binary : Opcode::Text; + } + header[0] = static_cast(opcode) | 0x80; + if (length < 126) { + header[1] = static_cast(length); + } else if (length < 65536) { + header[1] = 126; + *reinterpret_cast(header + 2) = htons(length); + } else { + header[1] = 127; + *reinterpret_cast(header + 2) = THRIFT_htonll(length); + } + + transport_->write(header, headerSize); + } + + // Add constant here to avoid a linker error on Windows + constexpr static const char* CRLF = "\r\n"; + std::string acceptKey_; + bool connection_; + bool secWebSocketKey_; + bool secWebSocketVersion_; + bool upgrade_; +}; + +/** + * Wraps a transport into binary WebSocket protocol + */ +class TBinaryWebSocketServerTransportFactory : public TTransportFactory { +public: + TBinaryWebSocketServerTransportFactory() = default; + + ~TBinaryWebSocketServerTransportFactory() override = default; + + /** + * Wraps the transport into a buffered one. + */ + std::shared_ptr getTransport(std::shared_ptr trans) override { + return std::shared_ptr(new TWebSocketServer(trans)); + } +}; + +/** + * Wraps a transport into text WebSocket protocol + */ +class TTextWebSocketServerTransportFactory : public TTransportFactory { +public: + TTextWebSocketServerTransportFactory() = default; + + ~TTextWebSocketServerTransportFactory() override = default; + + /** + * Wraps the transport into a buffered one. + */ + std::shared_ptr getTransport(std::shared_ptr trans) override { + return std::shared_ptr(new TWebSocketServer(trans)); + } +}; +} // namespace transport +} // namespace thrift +} // namespace apache +#endif diff --git a/lib/cpp/src/thrift/transport/TZlibTransport.cpp b/lib/cpp/src/thrift/transport/TZlibTransport.cpp index e426dc390c8..657ce52052d 100644 --- a/lib/cpp/src/thrift/transport/TZlibTransport.cpp +++ b/lib/cpp/src/thrift/transport/TZlibTransport.cpp @@ -110,7 +110,7 @@ TZlibTransport::~TZlibTransport() { delete wstream_; } -bool TZlibTransport::isOpen() { +bool TZlibTransport::isOpen() const { return (readAvail() > 0) || (rstream_->avail_in > 0) || transport_->isOpen(); } @@ -131,11 +131,12 @@ bool TZlibTransport::peek() { // 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() { +inline int TZlibTransport::readAvail() const { return urbuf_size_ - rstream_->avail_out - urpos_; } uint32_t TZlibTransport::read(uint8_t* buf, uint32_t len) { + checkReadBytesAvailable(len); uint32_t need = len; // TODO(dreiss): Skip urbuf on big reads. @@ -265,6 +266,7 @@ void TZlibTransport::flush() { } flushToTransport(Z_FULL_FLUSH); + resetConsumedMessageSize(); } void TZlibTransport::finish() { @@ -331,10 +333,11 @@ const uint8_t* TZlibTransport::borrow(uint8_t* buf, uint32_t* len) { *len = (uint32_t)readAvail(); return urbuf_ + urpos_; } - return NULL; + return nullptr; } void TZlibTransport::consume(uint32_t len) { + countConsumedMessageBytes(len); if (readAvail() >= (int)len) { urpos_ += len; } else { @@ -397,6 +400,18 @@ void TZlibTransport::verifyChecksum() { "verifyChecksum() called before end of " "zlib stream"); } + +TZlibTransportFactory::TZlibTransportFactory(std::shared_ptr transportFactory) + :transportFactory_(transportFactory) { +} + +std::shared_ptr TZlibTransportFactory::getTransport(std::shared_ptr trans) { + if (transportFactory_) { + return std::shared_ptr(new TZlibTransport(transportFactory_->getTransport(trans))); + } else { + return std::shared_ptr(new TZlibTransport(trans)); + } +} } } } // apache::thrift::transport diff --git a/lib/cpp/src/thrift/transport/TZlibTransport.h b/lib/cpp/src/thrift/transport/TZlibTransport.h index a0fb464d377..85765e6be74 100644 --- a/lib/cpp/src/thrift/transport/TZlibTransport.h +++ b/lib/cpp/src/thrift/transport/TZlibTransport.h @@ -36,9 +36,9 @@ class TZlibTransportException : public TTransportException { TZlibTransportException(int status, const char* msg) : TTransportException(TTransportException::INTERNAL_ERROR, errorMessage(status, msg)), zlib_status_(status), - zlib_msg_(msg == NULL ? "(null)" : msg) {} + zlib_msg_(msg == nullptr ? "(null)" : msg) {} - virtual ~TZlibTransportException() throw() {} + ~TZlibTransportException() noexcept override = default; int getZlibStatus() { return zlib_status_; } std::string getZlibMessage() { return zlib_msg_; } @@ -78,13 +78,15 @@ class TZlibTransport : public TVirtualTransport { * @param cwbuf_size Compressed buffer size for writing. * @param comp_level Compression level (0=none[fast], 6=default, 9=max[slow]). */ - TZlibTransport(stdcxx::shared_ptr transport, + TZlibTransport(std::shared_ptr 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), + int16_t comp_level = Z_DEFAULT_COMPRESSION, + std::shared_ptr config = nullptr) + : TVirtualTransport(config), + transport_(transport), urpos_(0), uwpos_(0), input_ended_(false), @@ -93,12 +95,12 @@ class TZlibTransport : public TVirtualTransport { crbuf_size_(crbuf_size), uwbuf_size_(uwbuf_size), cwbuf_size_(cwbuf_size), - urbuf_(NULL), - crbuf_(NULL), - uwbuf_(NULL), - cwbuf_(NULL), - rstream_(NULL), - wstream_(NULL), + 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. @@ -136,20 +138,20 @@ class TZlibTransport : public TVirtualTransport { * unflushed data. You must explicitly call flush() or finish() to ensure * that data is actually written and flushed to the underlying transport. */ - ~TZlibTransport(); + ~TZlibTransport() override; - bool isOpen(); - bool peek(); + bool isOpen() const override; + bool peek() override; - void open() { transport_->open(); } + void open() override { transport_->open(); } - void close() { transport_->close(); } + 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(); + void flush() override; /** * Finalize the zlib stream. @@ -180,12 +182,12 @@ class TZlibTransport : public TVirtualTransport { static const int DEFAULT_UWBUF_SIZE = 128; static const int DEFAULT_CWBUF_SIZE = 1024; - stdcxx::shared_ptr getUnderlyingTransport() const { return transport_; } + std::shared_ptr getUnderlyingTransport() const { return transport_; } protected: inline void checkZlibRv(int status, const char* msg); inline void checkZlibRvNothrow(int status, const char* msg); - inline int readAvail(); + inline int readAvail() const; void flushToTransport(int flush); void flushToZlib(const uint8_t* buf, int len, int flush); bool readFromZlib(); @@ -195,7 +197,7 @@ class TZlibTransport : public TVirtualTransport { // Larger (or equal) writes are dumped straight to zlib. static const uint32_t MIN_DIRECT_DEFLATE_SIZE = 32; - stdcxx::shared_ptr transport_; + std::shared_ptr transport_; int urpos_; int uwpos_; @@ -227,14 +229,21 @@ class TZlibTransport : public TVirtualTransport { */ class TZlibTransportFactory : public TTransportFactory { public: - TZlibTransportFactory() {} + TZlibTransportFactory() = default; - virtual ~TZlibTransportFactory() {} + /** + * Wraps a transport factory into a zlibbed one. + */ + TZlibTransportFactory(std::shared_ptr transportFactory); - virtual stdcxx::shared_ptr getTransport(stdcxx::shared_ptr trans) { - return stdcxx::shared_ptr(new TZlibTransport(trans)); - } + ~TZlibTransportFactory() override = default; + + std::shared_ptr getTransport(std::shared_ptr trans) override; + +protected: + std::shared_ptr transportFactory_; }; + } } } // apache::thrift::transport diff --git a/lib/cpp/src/thrift/windows/GetTimeOfDay.cpp b/lib/cpp/src/thrift/windows/GetTimeOfDay.cpp index 820828f32c7..ac24124b3af 100644 --- a/lib/cpp/src/thrift/windows/GetTimeOfDay.cpp +++ b/lib/cpp/src/thrift/windows/GetTimeOfDay.cpp @@ -25,12 +25,6 @@ #include #endif -#if defined(_MSC_VER) || defined(_MSC_EXTENSIONS) -#define DELTA_EPOCH_IN_MICROSECS 11644473600000000Ui64 -#else -#define DELTA_EPOCH_IN_MICROSECS 11644473600000000ULL -#endif - #if !defined(__MINGW32__) struct timezone { int tz_minuteswest; /* minutes W of Greenwich */ @@ -43,50 +37,45 @@ int thrift_gettimeofday(struct timeval* tv, struct timezone* tz) { return gettimeofday(tv,tz); } #else -int thrift_gettimeofday(struct timeval* tv, struct timezone* tz) { - FILETIME ft; - unsigned __int64 tmpres(0); - static int tzflag; +#define WIN32_LEAN_AND_MEAN +#include +#include +#include +#include - if (NULL != tv) { - GetSystemTimeAsFileTime(&ft); +// 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 == nullptr); - tmpres |= ft.dwHighDateTime; - tmpres <<= 32; - tmpres |= ft.dwLowDateTime; + // 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(116444736000000000ULL); - /*converting file time to unix epoch*/ - tmpres -= DELTA_EPOCH_IN_MICROSECS; - tmpres /= 10; /*convert into microseconds*/ - tv->tv_sec = (long)(tmpres / 1000000UL); - tv->tv_usec = (long)(tmpres % 1000000UL); - } + SYSTEMTIME system_time; + FILETIME file_time; + uint64_t time; - if (NULL != tz) { - if (!tzflag) { - _tzset(); - tzflag++; + 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(file_time.dwLowDateTime ) ; + time += static_cast(file_time.dwHighDateTime) << 32; - long time_zone(0); - errno_t err(_get_timezone(&time_zone)); - if (err == NO_ERROR) { - tz->tz_minuteswest = time_zone / 60; - } else { - return -1; - } - - int day_light(0); - err = (_get_daylight(&day_light)); - if (err == NO_ERROR) { - tz->tz_dsttime = day_light; - return 0; - } else { - return -1; - } - } - - return 0; + tp->tv_sec = static_cast((time - EPOCH) / 10000000L); + tp->tv_usec = static_cast(system_time.wMilliseconds * 1000); + return 0; } #endif diff --git a/lib/cpp/src/thrift/windows/SocketPair.cpp b/lib/cpp/src/thrift/windows/SocketPair.cpp index 7228832eef7..2650b37d475 100644 --- a/lib/cpp/src/thrift/windows/SocketPair.cpp +++ b/lib/cpp/src/thrift/windows/SocketPair.cpp @@ -77,12 +77,12 @@ int thrift_socketpair(int d, int type, int protocol, THRIFT_SOCKET sv[2]) { break; if (listen(listener, 1) == SOCKET_ERROR) break; - sv[0] = WSASocket(AF_INET, SOCK_STREAM, 0, NULL, 0, flags); + sv[0] = WSASocket(AF_INET, SOCK_STREAM, 0, nullptr, 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); + sv[1] = accept(listener, nullptr, nullptr); if (sv[1] == INVALID_SOCKET) break; diff --git a/lib/cpp/src/thrift/windows/Sync.h b/lib/cpp/src/thrift/windows/Sync.h index 5d321996b3d..a296d7ec4ae 100644 --- a/lib/cpp/src/thrift/windows/Sync.h +++ b/lib/cpp/src/thrift/windows/Sync.h @@ -55,8 +55,8 @@ struct TAutoResetEvent : boost::noncopyable { HANDLE h; TAutoResetEvent() { - h = CreateEvent(NULL, FALSE, FALSE, NULL); - if (h == NULL) { + h = CreateEvent(nullptr, FALSE, FALSE, nullptr); + if (h == nullptr) { GlobalOutput.perror("TAutoResetEvent unable to create event, GLE=", GetLastError()); throw apache::thrift::concurrency::SystemResourceException("CreateEvent failed"); } @@ -68,8 +68,8 @@ struct TManualResetEvent : boost::noncopyable { HANDLE h; TManualResetEvent() { - h = CreateEvent(NULL, TRUE, FALSE, NULL); - if (h == NULL) { + h = CreateEvent(nullptr, TRUE, FALSE, nullptr); + if (h == nullptr) { GlobalOutput.perror("TManualResetEvent unable to create event, GLE=", GetLastError()); throw apache::thrift::concurrency::SystemResourceException("CreateEvent failed"); } diff --git a/lib/cpp/src/thrift/windows/TWinsockSingleton.cpp b/lib/cpp/src/thrift/windows/TWinsockSingleton.cpp index 2e0ccf53a81..c3339f39efe 100644 --- a/lib/cpp/src/thrift/windows/TWinsockSingleton.cpp +++ b/lib/cpp/src/thrift/windows/TWinsockSingleton.cpp @@ -20,21 +20,14 @@ #include // boost -#include #include namespace apache { namespace thrift { namespace transport { -TWinsockSingleton::instance_ptr TWinsockSingleton::instance_ptr_(NULL); -#if USE_BOOST_THREAD -boost::once_flag TWinsockSingleton::flags_ = BOOST_ONCE_INIT; -#elif USE_STD_THREAD +TWinsockSingleton::instance_ptr TWinsockSingleton::instance_ptr_(nullptr); std::once_flag TWinsockSingleton::flags_; -#else -#error For windows you must choose USE_BOOST_THREAD or USE_STD_THREAD -#endif //------------------------------------------------------------------------------ TWinsockSingleton::TWinsockSingleton(void) { @@ -43,7 +36,6 @@ TWinsockSingleton::TWinsockSingleton(void) { int error(WSAStartup(version, &data)); if (error != 0) { - BOOST_ASSERT(false); throw std::runtime_error("Failed to initialise Winsock."); } } @@ -55,11 +47,7 @@ TWinsockSingleton::~TWinsockSingleton(void) { //------------------------------------------------------------------------------ void TWinsockSingleton::create(void) { -#if USE_BOOST_THREAD - boost::call_once(init, flags_); -#elif USE_STD_THREAD std::call_once(flags_, init); -#endif } //------------------------------------------------------------------------------ diff --git a/lib/cpp/src/thrift/windows/TWinsockSingleton.h b/lib/cpp/src/thrift/windows/TWinsockSingleton.h index 0eab6d4ec8a..a30806b98ce 100644 --- a/lib/cpp/src/thrift/windows/TWinsockSingleton.h +++ b/lib/cpp/src/thrift/windows/TWinsockSingleton.h @@ -33,15 +33,9 @@ // boost #include -#if USE_BOOST_THREAD -#include -#elif USE_STD_THREAD +#include #include -#else -#error For windows you must choose USE_BOOST_THREAD or USE_STD_THREAD -#endif -#include namespace apache { namespace thrift { @@ -54,7 +48,7 @@ namespace transport { class TWinsockSingleton : private boost::noncopyable { public: - typedef stdcxx::shared_ptr instance_ptr; + typedef std::shared_ptr instance_ptr; private: TWinsockSingleton(void); @@ -70,13 +64,7 @@ class TWinsockSingleton : private boost::noncopyable { private: static instance_ptr instance_ptr_; -#if USE_BOOST_THREAD - static boost::once_flag flags_; -#elif USE_STD_THREAD static std::once_flag flags_; -#else -#error Need a non-Boost non-C++11 way to track single initialization here. -#endif }; } } diff --git a/lib/cpp/src/thrift/windows/WinFcntl.cpp b/lib/cpp/src/thrift/windows/WinFcntl.cpp index c907e92f228..cec2f6745a5 100644 --- a/lib/cpp/src/thrift/windows/WinFcntl.cpp +++ b/lib/cpp/src/thrift/windows/WinFcntl.cpp @@ -42,57 +42,6 @@ int thrift_fcntl(THRIFT_SOCKET fd, int cmd, int flags) { 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()); diff --git a/lib/cpp/src/thrift/windows/WinFcntl.h b/lib/cpp/src/thrift/windows/WinFcntl.h index 6c6be97ef19..4816fc5ec37 100644 --- a/lib/cpp/src/thrift/windows/WinFcntl.h +++ b/lib/cpp/src/thrift/windows/WinFcntl.h @@ -36,17 +36,8 @@ #include #include -#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 diff --git a/lib/cpp/src/thrift/windows/config.h b/lib/cpp/src/thrift/windows/config.h index bc4aa42f8ad..a218d908e9a 100644 --- a/lib/cpp/src/thrift/windows/config.h +++ b/lib/cpp/src/thrift/windows/config.h @@ -28,35 +28,9 @@ #error "This is a Windows header only" #endif -// use std::thread in MSVC11 (2012) or newer and in MinGW -#if (_MSC_VER >= 1700) || defined(__MINGW32__) -#define USE_STD_THREAD 1 -#else -// otherwise use boost threads -#define USE_BOOST_THREAD 1 -#endif - // Something that defines PRId64 is required to build #define HAVE_INTTYPES_H 1 -// VS2010 or later has stdint.h as does MinGW -#if (_MSC_VER >= 1600) || defined(__MINGW32__) -#define HAVE_STDINT_H 1 -#endif - -#ifndef TARGET_WIN_XP -#define TARGET_WIN_XP 1 -#endif - -#if TARGET_WIN_XP -#ifndef WINVER -#define WINVER 0x0501 -#endif -#ifndef _WIN32_WINNT -#define _WIN32_WINNT 0x0501 -#endif -#endif - #ifndef _WIN32_WINNT #define _WIN32_WINNT 0x0601 #endif @@ -73,21 +47,7 @@ #define HAVE_GETTIMEOFDAY 1 #define HAVE_SYS_STAT_H 1 -// Must be using VS2010 or later, or boost, so that C99 types are defined in the global namespace -#ifdef HAVE_STDINT_H #include -#else -#include - -typedef boost::int64_t int64_t; -typedef boost::uint64_t uint64_t; -typedef boost::int32_t int32_t; -typedef boost::uint32_t uint32_t; -typedef boost::int16_t int16_t; -typedef boost::uint16_t uint16_t; -typedef boost::int8_t int8_t; -typedef boost::uint8_t uint8_t; -#endif #include #include @@ -104,6 +64,9 @@ typedef boost::uint8_t uint8_t; #pragma comment(lib, "Ws2.lib") #else #pragma comment(lib, "Ws2_32.lib") + #pragma comment(lib, "gdi32.lib") // For static OpenSSL + #pragma comment(lib, "crypt32.lib") // For static OpenSSL + #pragma comment(lib, "user32.lib") // For static OpenSSL #pragma comment(lib, "advapi32.lib") // For security APIs in TPipeServer #pragma comment(lib, "Shlwapi.lib") // For StrStrIA in TPipeServer #endif diff --git a/lib/cpp/test/AllProtocolTests.tcc b/lib/cpp/test/AllProtocolTests.tcc index b6df6563a8a..80a4ea097ab 100644 --- a/lib/cpp/test/AllProtocolTests.tcc +++ b/lib/cpp/test/AllProtocolTests.tcc @@ -28,7 +28,7 @@ #include "GenericHelpers.h" -using apache::thrift::stdcxx::shared_ptr; +using std::shared_ptr; using namespace apache::thrift; using namespace apache::thrift::protocol; using namespace apache::thrift::transport; @@ -216,7 +216,7 @@ void testProtocol(const char* protoname) { testMessage(); printf("%s => OK\n", protoname); - } catch (TException e) { + } catch (const TException &e) { THRIFT_SNPRINTF(errorMessage, ERR_LEN, "%s => Test FAILED: %s", protoname, e.what()); throw TException(errorMessage); } diff --git a/lib/cpp/test/Base64Test.cpp b/lib/cpp/test/Base64Test.cpp index 7686e4e7b72..843b236efd2 100644 --- a/lib/cpp/test/Base64Test.cpp +++ b/lib/cpp/test/Base64Test.cpp @@ -17,7 +17,7 @@ * under the License. */ -#include +#include #include using apache::thrift::protocol::base64_encode; diff --git a/lib/cpp/test/Benchmark.cpp b/lib/cpp/test/Benchmark.cpp index afde7d40278..56adac0b203 100644 --- a/lib/cpp/test/Benchmark.cpp +++ b/lib/cpp/test/Benchmark.cpp @@ -23,8 +23,8 @@ #include #define _USE_MATH_DEFINES #include +#include #include "thrift/protocol/TBinaryProtocol.h" -#include "thrift/stdcxx.h" #include "thrift/transport/TBufferTransports.h" #include "gen-cpp/DebugProtoTest_types.h" @@ -36,12 +36,12 @@ class Timer { public: timeval vStart; - Timer() { THRIFT_GETTIMEOFDAY(&vStart, 0); } - void start() { THRIFT_GETTIMEOFDAY(&vStart, 0); } + Timer() { THRIFT_GETTIMEOFDAY(&vStart, nullptr); } + void start() { THRIFT_GETTIMEOFDAY(&vStart, nullptr); } double frame() { timeval vEnd; - THRIFT_GETTIMEOFDAY(&vEnd, 0); + 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; @@ -68,9 +68,9 @@ int main() { ooe.base64 = "\1\2\3\255"; int num = 100000; - apache::thrift::stdcxx::shared_ptr buf(new TMemoryBuffer(num*1000)); + std::shared_ptr buf(new TMemoryBuffer(num*1000)); - uint8_t* data = NULL; + uint8_t* data = nullptr; uint32_t datasize = 0; { @@ -89,7 +89,7 @@ int main() { buf->getBuffer(&data, &datasize); { - apache::thrift::stdcxx::shared_ptr buf2(new TMemoryBuffer(data, datasize)); + std::shared_ptr buf2(new TMemoryBuffer(data, datasize)); TBinaryProtocolT prot(buf2); OneOfEach ooe2; double elapsed = 0.0; @@ -117,7 +117,7 @@ int main() { { OneOfEach ooe2; - apache::thrift::stdcxx::shared_ptr buf2(new TMemoryBuffer(data, datasize)); + std::shared_ptr buf2(new TMemoryBuffer(data, datasize)); TBinaryProtocolT prot(buf2); double elapsed = 0.0; Timer timer; @@ -143,7 +143,7 @@ int main() { } { - apache::thrift::stdcxx::shared_ptr buf2(new TMemoryBuffer(data, datasize)); + std::shared_ptr buf2(new TMemoryBuffer(data, datasize)); TBinaryProtocolT prot(buf2); OneOfEach ooe2; double elapsed = 0.0; @@ -157,7 +157,7 @@ int main() { } - data = NULL; + data = nullptr; datasize = 0; num = 10000000; @@ -182,7 +182,7 @@ int main() { buf->getBuffer(&data, &datasize); { - apache::thrift::stdcxx::shared_ptr buf2(new TMemoryBuffer(data, datasize)); + std::shared_ptr buf2(new TMemoryBuffer(data, datasize)); TBinaryProtocolT prot(buf2); ListDoublePerf listDoublePerf2; double elapsed = 0.0; @@ -206,7 +206,7 @@ int main() { { ListDoublePerf listDoublePerf2; - apache::thrift::stdcxx::shared_ptr buf2(new TMemoryBuffer(data, datasize)); + std::shared_ptr buf2(new TMemoryBuffer(data, datasize)); TBinaryProtocolT prot(buf2); double elapsed = 0.0; Timer timer; @@ -228,7 +228,7 @@ int main() { } { - apache::thrift::stdcxx::shared_ptr buf2(new TMemoryBuffer(data, datasize)); + std::shared_ptr buf2(new TMemoryBuffer(data, datasize)); TBinaryProtocolT prot(buf2); ListDoublePerf listDoublePerf2; double elapsed = 0.0; diff --git a/lib/cpp/test/CMakeLists.txt b/lib/cpp/test/CMakeLists.txt index 261382f6584..ced78a25738 100644 --- a/lib/cpp/test/CMakeLists.txt +++ b/lib/cpp/test/CMakeLists.txt @@ -17,11 +17,13 @@ # under the License. # -include_directories(SYSTEM "${Boost_INCLUDE_DIRS}") +# 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) -if (WITH_DYN_LINK_TEST) - add_definitions( -DBOOST_TEST_DYN_LINK ) -endif() +include(ThriftMacros) # Make sure gen-cpp files can be included include_directories("${CMAKE_CURRENT_BINARY_DIR}") @@ -40,7 +42,6 @@ set(testgencpp_SOURCES 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 @@ -80,13 +81,9 @@ set(UnitTest_SOURCES TypedefTest.cpp TServerSocketTest.cpp TServerTransportTest.cpp + ThrifttReadCheckTests.cpp ) -if(NOT WITH_BOOSTTHREADS AND NOT WITH_STDTHREADS AND NOT MSVC AND NOT MINGW) - list(APPEND UnitTest_SOURCES concurrency/MutexTest.cpp) - list(APPEND UnitTest_SOURCES concurrency/RWMutexStarveTest.cpp) -endif() - add_executable(UnitTests ${UnitTest_SOURCES}) target_link_libraries(UnitTests testgencpp ${Boost_LIBRARIES}) LINK_AGAINST_THRIFT_LIBRARY(UnitTests thrift) @@ -337,18 +334,17 @@ target_link_libraries(SecurityTest -lrt) endif () add_test(NAME SecurityTest COMMAND SecurityTest -- "${CMAKE_CURRENT_SOURCE_DIR}/../../../test/keys") -endif() - -if(WITH_QT4) -set(CMAKE_AUTOMOC ON) -find_package(Qt4 REQUIRED COMPONENTS QtTest) -set(TQTcpServerTest_SOURCES - qt/TQTcpServerTest.cpp +add_executable(SecurityFromBufferTest SecurityFromBufferTest.cpp) +target_link_libraries(SecurityFromBufferTest + testgencpp + ${Boost_LIBRARIES} ) -add_executable(TQTcpServerTest ${TQTcpServerTest_SOURCES}) -target_link_libraries(TQTcpServerTest testgencpp_cob thriftqt Qt4::QtTest) -LINK_AGAINST_THRIFT_LIBRARY(TQTcpServerTest thrift) -add_test(NAME TQTcpServerTest COMMAND TQTcpServerTest) +LINK_AGAINST_THRIFT_LIBRARY(SecurityFromBufferTest thrift) +if (NOT MSVC AND NOT ${CMAKE_SYSTEM_NAME} MATCHES "Darwin" AND NOT MINGW) +target_link_libraries(SecurityFromBufferTest -lrt) +endif () +add_test(NAME SecurityFromBufferTest COMMAND SecurityFromBufferTest -- "${CMAKE_CURRENT_SOURCE_DIR}/../../../test/keys") + endif() if(WITH_QT5) @@ -396,7 +392,7 @@ add_custom_command(OUTPUT gen-cpp/SecondService.cpp gen-cpp/ThriftTest_constants 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 +add_custom_command(OUTPUT gen-cpp/OneWayService.cpp gen-cpp/OneWayTest_types.h gen-cpp/OneWayService.h COMMAND ${THRIFT_COMPILER} --gen cpp ${CMAKE_CURRENT_SOURCE_DIR}/OneWayTest.thrift ) diff --git a/lib/cpp/test/DebugProtoTest.cpp b/lib/cpp/test/DebugProtoTest.cpp index e04600a32c2..060f3547dd7 100644 --- a/lib/cpp/test/DebugProtoTest.cpp +++ b/lib/cpp/test/DebugProtoTest.cpp @@ -21,14 +21,14 @@ #include #include "gen-cpp/DebugProtoTest_types.h" #include -#include +#include #define BOOST_TEST_MODULE DebugProtoTest #include using namespace thrift::test::debug; -static ::apache::thrift::stdcxx::shared_ptr ooe; +static ::std::shared_ptr ooe; void testCaseSetup_1() { ooe.reset(new OneOfEach); @@ -81,7 +81,7 @@ BOOST_AUTO_TEST_CASE(test_debug_proto_1) { "Expected:\n" << expected_result << "\nGotten:\n" << result); } -static ::apache::thrift::stdcxx::shared_ptr n; +static ::std::shared_ptr n; void testCaseSetup_2() { testCaseSetup_1(); @@ -149,7 +149,7 @@ BOOST_AUTO_TEST_CASE(test_debug_proto_2) { "Expected:\n" << expected_result << "\nGotten:\n" << result); } -static ::apache::thrift::stdcxx::shared_ptr hm; +static ::std::shared_ptr hm; void testCaseSetup_3() { testCaseSetup_2(); diff --git a/lib/cpp/test/EnumTest.cpp b/lib/cpp/test/EnumTest.cpp index c935bc41f44..388abb7e9ef 100644 --- a/lib/cpp/test/EnumTest.cpp +++ b/lib/cpp/test/EnumTest.cpp @@ -26,6 +26,13 @@ std::ostream& operator <<(std::ostream& os, const MyEnumWithCustomOstream::type& 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) { @@ -66,7 +73,6 @@ std::string EnumToString(_T e) return ss.str(); } - BOOST_AUTO_TEST_CASE(test_enum_ostream) { BOOST_CHECK_EQUAL(EnumToString(MyEnum1::ME1_0), "ME1_0"); @@ -75,10 +81,22 @@ BOOST_AUTO_TEST_CASE(test_enum_ostream) BOOST_CHECK_EQUAL(EnumToString(MyEnumWithCustomOstream::CustoM2), "{2:CUSTOM!}"); // some invalid or unknown value - MyEnum5::type uut = (MyEnum5::type)44; + auto uut = static_cast(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(44); + BOOST_CHECK_EQUAL(::to_string(uut), "44"); +} + BOOST_AUTO_TEST_CASE(test_enum_constant) { MyStruct ms; diff --git a/lib/cpp/test/GenericHelpers.h b/lib/cpp/test/GenericHelpers.h index e131c42ff5c..bcef9f24252 100644 --- a/lib/cpp/test/GenericHelpers.h +++ b/lib/cpp/test/GenericHelpers.h @@ -21,7 +21,7 @@ #define _THRIFT_TEST_GENERICHELPERS_H_ 1 #include -#include +#include #include /* ClassName Helper for cleaner exceptions */ @@ -63,43 +63,43 @@ class GenericIO { public: /* Write functions */ - static uint32_t write(apache::thrift::stdcxx::shared_ptr proto, const int8_t& val) { + static uint32_t write(std::shared_ptr proto, const int8_t& val) { return proto->writeByte(val); } - static uint32_t write(apache::thrift::stdcxx::shared_ptr proto, const int16_t& val) { + static uint32_t write(std::shared_ptr proto, const int16_t& val) { return proto->writeI16(val); } - static uint32_t write(apache::thrift::stdcxx::shared_ptr proto, const int32_t& val) { + static uint32_t write(std::shared_ptr proto, const int32_t& val) { return proto->writeI32(val); } - static uint32_t write(apache::thrift::stdcxx::shared_ptr proto, const double& val) { + static uint32_t write(std::shared_ptr proto, const double& val) { return proto->writeDouble(val); } - static uint32_t write(apache::thrift::stdcxx::shared_ptr proto, const int64_t& val) { + static uint32_t write(std::shared_ptr proto, const int64_t& val) { return proto->writeI64(val); } - static uint32_t write(apache::thrift::stdcxx::shared_ptr proto, const std::string& val) { + static uint32_t write(std::shared_ptr proto, const std::string& val) { return proto->writeString(val); } /* Read functions */ - static uint32_t read(apache::thrift::stdcxx::shared_ptr proto, int8_t& val) { return proto->readByte(val); } + static uint32_t read(std::shared_ptr proto, int8_t& val) { return proto->readByte(val); } - static uint32_t read(apache::thrift::stdcxx::shared_ptr proto, int16_t& val) { return proto->readI16(val); } + static uint32_t read(std::shared_ptr proto, int16_t& val) { return proto->readI16(val); } - static uint32_t read(apache::thrift::stdcxx::shared_ptr proto, int32_t& val) { return proto->readI32(val); } + static uint32_t read(std::shared_ptr proto, int32_t& val) { return proto->readI32(val); } - static uint32_t read(apache::thrift::stdcxx::shared_ptr proto, int64_t& val) { return proto->readI64(val); } + static uint32_t read(std::shared_ptr proto, int64_t& val) { return proto->readI64(val); } - static uint32_t read(apache::thrift::stdcxx::shared_ptr proto, double& val) { return proto->readDouble(val); } + static uint32_t read(std::shared_ptr proto, double& val) { return proto->readDouble(val); } - static uint32_t read(apache::thrift::stdcxx::shared_ptr proto, std::string& val) { + static uint32_t read(std::shared_ptr proto, std::string& val) { return proto->readString(val); } }; diff --git a/lib/cpp/test/JSONProtoTest.cpp b/lib/cpp/test/JSONProtoTest.cpp index 77bc2500c87..082c8a28b75 100644 --- a/lib/cpp/test/JSONProtoTest.cpp +++ b/lib/cpp/test/JSONProtoTest.cpp @@ -22,7 +22,7 @@ #include #include #include -#include +#include #include #include "gen-cpp/DebugProtoTest_types.h" @@ -34,7 +34,7 @@ using namespace apache::thrift; using apache::thrift::transport::TMemoryBuffer; using apache::thrift::protocol::TJSONProtocol; -static stdcxx::shared_ptr ooe; +static std::shared_ptr ooe; void testCaseSetup_1() { ooe.reset(new OneOfEach); @@ -67,7 +67,7 @@ BOOST_AUTO_TEST_CASE(test_json_proto_1) { "Expected:\n" << expected_result << "\nGotten:\n" << result); } -static stdcxx::shared_ptr n; +static std::shared_ptr n; void testCaseSetup_2() { testCaseSetup_1(); @@ -107,7 +107,7 @@ BOOST_AUTO_TEST_CASE(test_json_proto_2) { "Expected:\n" << expected_result << "\nGotten:\n" << result); } -static stdcxx::shared_ptr hm; +static std::shared_ptr hm; void testCaseSetup_3() { testCaseSetup_2(); @@ -185,8 +185,8 @@ BOOST_AUTO_TEST_CASE(test_json_proto_3) { BOOST_AUTO_TEST_CASE(test_json_proto_4) { testCaseSetup_1(); - stdcxx::shared_ptr buffer(new TMemoryBuffer()); - stdcxx::shared_ptr proto(new TJSONProtocol(buffer)); + std::shared_ptr buffer(new TMemoryBuffer()); + std::shared_ptr proto(new TJSONProtocol(buffer)); ooe->write(proto.get()); OneOfEach ooe2; @@ -198,8 +198,8 @@ BOOST_AUTO_TEST_CASE(test_json_proto_4) { BOOST_AUTO_TEST_CASE(test_json_proto_5) { testCaseSetup_3(); - stdcxx::shared_ptr buffer(new TMemoryBuffer()); - stdcxx::shared_ptr proto(new TJSONProtocol(buffer)); + std::shared_ptr buffer(new TMemoryBuffer()); + std::shared_ptr proto(new TJSONProtocol(buffer)); hm->write(proto.get()); HolyMoley hm2; @@ -229,15 +229,24 @@ BOOST_AUTO_TEST_CASE(test_json_proto_6) { "304},\"6\":{\"dbl\":1e-305},\"7\":{\"dbl\":0},\"8\":{\"dbl\":-0}}" ); - const std::string result(apache::thrift::ThriftJSONString(dub)); + std::shared_ptr buffer(new TMemoryBuffer()); + std::shared_ptr proto(new TJSONProtocol(buffer)); + dub.write(proto.get()); + Doubles dub_1; + dub_1.read(proto.get()); + const std::string result(apache::thrift::ThriftJSONString(dub)); + const std::string result_1(apache::thrift::ThriftJSONString(dub_1)); + BOOST_CHECK_MESSAGE(!expected_result.compare(result), "Expected:\n" << expected_result << "\nGotten:\n" << result); + BOOST_CHECK_MESSAGE(!expected_result.compare(result_1), + "Expected:\n" << expected_result << "\nGotten:\n" << result_1); } BOOST_AUTO_TEST_CASE(test_json_proto_7) { - stdcxx::shared_ptr buffer(new TMemoryBuffer()); - stdcxx::shared_ptr proto(new TJSONProtocol(buffer)); + std::shared_ptr buffer(new TMemoryBuffer()); + std::shared_ptr proto(new TJSONProtocol(buffer)); Base64 base; base.a = 123; @@ -265,9 +274,9 @@ BOOST_AUTO_TEST_CASE(test_json_proto_8) { "\",3,1,2,3]}}"; const std::size_t bufSiz = strlen(json_string) * sizeof(char); - stdcxx::shared_ptr buffer(new TMemoryBuffer( + std::shared_ptr buffer(new TMemoryBuffer( (uint8_t*)(json_string), static_cast(bufSiz))); - stdcxx::shared_ptr proto(new TJSONProtocol(buffer)); + std::shared_ptr proto(new TJSONProtocol(buffer)); OneOfEach ooe2; @@ -294,9 +303,9 @@ BOOST_AUTO_TEST_CASE(test_json_unicode_escaped) { "\",3,1,2,3]}}"; const char* expected_zomg_unicode = "\xe0\xb8\x81 \xf0\x9d\x94\xbe"; - stdcxx::shared_ptr buffer(new TMemoryBuffer( + std::shared_ptr buffer(new TMemoryBuffer( (uint8_t*)(json_string), sizeof(json_string))); - stdcxx::shared_ptr proto(new TJSONProtocol(buffer)); + std::shared_ptr proto(new TJSONProtocol(buffer)); OneOfEach ooe2; ooe2.read(proto.get()); @@ -315,9 +324,9 @@ BOOST_AUTO_TEST_CASE(test_json_unicode_escaped_missing_low_surrogate) { ":[\"i8\",3,1,2,3]},\"13\":{\"lst\":[\"i16\",3,1,2,3]},\"14\":{\"lst\":[\"i64" "\",3,1,2,3]}}"; - stdcxx::shared_ptr buffer(new TMemoryBuffer( + std::shared_ptr buffer(new TMemoryBuffer( (uint8_t*)(json_string), sizeof(json_string))); - stdcxx::shared_ptr proto(new TJSONProtocol(buffer)); + std::shared_ptr proto(new TJSONProtocol(buffer)); OneOfEach ooe2; BOOST_CHECK_THROW(ooe2.read(proto.get()), @@ -333,9 +342,9 @@ BOOST_AUTO_TEST_CASE(test_json_unicode_escaped_missing_hi_surrogate) { ":[\"i8\",3,1,2,3]},\"13\":{\"lst\":[\"i16\",3,1,2,3]},\"14\":{\"lst\":[\"i64" "\",3,1,2,3]}}"; - stdcxx::shared_ptr buffer(new TMemoryBuffer( + std::shared_ptr buffer(new TMemoryBuffer( (uint8_t*)(json_string), sizeof(json_string))); - stdcxx::shared_ptr proto(new TJSONProtocol(buffer)); + std::shared_ptr proto(new TJSONProtocol(buffer)); OneOfEach ooe2; BOOST_CHECK_THROW(ooe2.read(proto.get()), diff --git a/lib/cpp/test/Makefile.am b/lib/cpp/test/Makefile.am index 4b9f77d2142..7f630db10b9 100755 --- a/lib/cpp/test/Makefile.am +++ b/lib/cpp/test/Makefile.am @@ -16,7 +16,7 @@ # specific language governing permissions and limitations # under the License. # -AUTOMAKE_OPTIONS = subdir-objects serial-tests +AUTOMAKE_OPTIONS = subdir-objects serial-tests nostdinc BUILT_SOURCES = gen-cpp/AnnotationTest_types.h \ gen-cpp/DebugProtoTest_types.h \ @@ -30,7 +30,6 @@ BUILT_SOURCES = gen-cpp/AnnotationTest_types.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 @@ -54,11 +53,8 @@ nodist_libtestgencpp_la_SOURCES = \ 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 @@ -99,6 +95,7 @@ check_PROGRAMS = \ TInterruptTest \ TServerIntegrationTest \ SecurityTest \ + SecurityFromBufferTest \ ZlibTest \ TFileTransportTest \ link_test \ @@ -133,13 +130,8 @@ UnitTests_SOURCES = \ TypedefTest.cpp \ TServerSocketTest.cpp \ TServerTransportTest.cpp \ - TTransportCheckThrow.h - -if !WITH_BOOSTTHREADS -UnitTests_SOURCES += \ - concurrency/MutexTest.cpp \ - concurrency/RWMutexStarveTest.cpp -endif + TTransportCheckThrow.h \ + ThrifttReadCheckTests.cpp UnitTests_LDADD = \ libtestgencpp.la \ @@ -180,6 +172,17 @@ SecurityTest_LDADD = \ $(BOOST_SYSTEM_LDADD) \ $(BOOST_THREAD_LDADD) +SecurityFromBufferTest_SOURCES = \ + SecurityFromBufferTest.cpp + +SecurityFromBufferTest_LDADD = \ + libtestgencpp.la \ + libprocessortest.la \ + $(BOOST_TEST_LDADD) \ + $(BOOST_FILESYSTEM_LDADD) \ + $(BOOST_SYSTEM_LDADD) \ + $(BOOST_THREAD_LDADD) + TransportTest_SOURCES = \ TransportTest.cpp @@ -408,13 +411,13 @@ gen-cpp/Service.cpp gen-cpp/StressTest_types.cpp: $(top_srcdir)/test/StressTest. 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 +gen-cpp/OneWayService.cpp gen-cpp/OneWayTest_types.h gen-cpp/OneWayService.h: 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 -D__STDC_FORMAT_MACROS -D__STDC_LIMIT_MACROS -I. +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 diff --git a/lib/cpp/test/OneWayHTTPTest.cpp b/lib/cpp/test/OneWayHTTPTest.cpp index 3fe63b612a4..7823482f1f1 100644 --- a/lib/cpp/test/OneWayHTTPTest.cpp +++ b/lib/cpp/test/OneWayHTTPTest.cpp @@ -17,7 +17,7 @@ * under the License. */ -#include +#include #include #include #include @@ -30,7 +30,7 @@ #include #include #include -#include +#include #include #include "gen-cpp/OneWayService.h" @@ -54,7 +54,7 @@ using apache::thrift::transport::TMemoryBuffer; using apache::thrift::transport::TServerSocket; using apache::thrift::transport::TSocket; using apache::thrift::transport::TTransportException; -using apache::thrift::stdcxx::shared_ptr; +using std::shared_ptr; using std::cout; using std::cerr; using std::endl; @@ -66,14 +66,14 @@ namespace utf = boost::unit_test; class OneWayServiceHandler : public onewaytest::OneWayServiceIf { public: - OneWayServiceHandler() {} + OneWayServiceHandler() = default; void roundTripRPC() override { #ifdef ENABLE_STDERR_LOGGING cerr << "roundTripRPC()" << endl; #endif } - void oneWayRPC() { + void oneWayRPC() override { #ifdef ENABLE_STDERR_LOGGING cerr << "oneWayRPC()" << std::endl ; #endif @@ -82,13 +82,13 @@ class OneWayServiceHandler : public onewaytest::OneWayServiceIf { class OneWayServiceCloneFactory : virtual public onewaytest::OneWayServiceIfFactory { public: - virtual ~OneWayServiceCloneFactory() {} - virtual onewaytest::OneWayServiceIf* getHandler(const ::apache::thrift::TConnectionInfo& connInfo) + ~OneWayServiceCloneFactory() override = default; + onewaytest::OneWayServiceIf* getHandler(const ::apache::thrift::TConnectionInfo& connInfo) override { (void)connInfo ; return new OneWayServiceHandler; } - virtual void releaseHandler( onewaytest::OneWayServiceIf* handler) { + void releaseHandler( onewaytest::OneWayServiceIf* handler) override { delete handler; } }; @@ -96,7 +96,7 @@ class OneWayServiceCloneFactory : virtual public onewaytest::OneWayServiceIfFact class RPC0ThreadClass { public: RPC0ThreadClass(TThreadedServer& server) : server_(server) { } // Constructor -~RPC0ThreadClass() { } // Destructor +~RPC0ThreadClass() = default; // Destructor void Run() { server_.serve() ; @@ -112,21 +112,21 @@ using apache::thrift::concurrency::Synchronized; class TServerReadyEventHandler : public TServerEventHandler, public Monitor { public: TServerReadyEventHandler() : isListening_(false), accepted_(0) {} - virtual ~TServerReadyEventHandler() {} - virtual void preServe() { + ~TServerReadyEventHandler() override = default; + void preServe() override { Synchronized sync(*this); isListening_ = true; notify(); } - virtual void* createContext(shared_ptr input, - shared_ptr output) { + void* createContext(shared_ptr input, + shared_ptr output) override { Synchronized sync(*this); ++accepted_; notify(); (void)input; (void)output; - return NULL; + return nullptr; } bool isListening() const { return isListening_; } uint64_t acceptedCount() const { return accepted_; } @@ -138,13 +138,13 @@ class TServerReadyEventHandler : public TServerEventHandler, public Monitor { class TBlockableBufferedTransport : public TBufferedTransport { public: - TBlockableBufferedTransport(stdcxx::shared_ptr transport) + TBlockableBufferedTransport(std::shared_ptr transport) : TBufferedTransport(transport, 10240), blocked_(false) { } uint32_t write_buffer_length() { - uint32_t have_bytes = static_cast(wBase_ - wBuf_.get()); + auto have_bytes = static_cast(wBase_ - wBuf_.get()); return have_bytes ; } @@ -176,14 +176,14 @@ class TBlockableBufferedTransport : public TBufferedTransport { BOOST_AUTO_TEST_CASE( JSON_BufferedHTTP ) { - stdcxx::shared_ptr ss = stdcxx::make_shared(0) ; + std::shared_ptr ss = std::make_shared(0) ; TThreadedServer server( - stdcxx::make_shared(stdcxx::make_shared()), + std::make_shared(std::make_shared()), ss, //port - stdcxx::make_shared(), - stdcxx::make_shared()); + std::make_shared(), + std::make_shared()); - stdcxx::shared_ptr pEventHandler(new TServerReadyEventHandler) ; + std::shared_ptr pEventHandler(new TServerReadyEventHandler) ; server.setServerEventHandler(pEventHandler); #ifdef ENABLE_STDERR_LOGGING @@ -205,11 +205,11 @@ BOOST_AUTO_TEST_CASE( JSON_BufferedHTTP ) #endif { - stdcxx::shared_ptr socket(new TSocket("localhost", port)); + std::shared_ptr socket(new TSocket("localhost", port)); socket->setRecvTimeout(10000) ; // 1000msec should be enough - stdcxx::shared_ptr blockable_transport(new TBlockableBufferedTransport(socket)); - stdcxx::shared_ptr transport(new THttpClient(blockable_transport, "localhost", "/service")); - stdcxx::shared_ptr protocol(new TJSONProtocol(transport)); + std::shared_ptr blockable_transport(new TBlockableBufferedTransport(socket)); + std::shared_ptr transport(new THttpClient(blockable_transport, "localhost", "/service")); + std::shared_ptr protocol(new TJSONProtocol(transport)); onewaytest::OneWayServiceClient client(protocol); @@ -227,7 +227,7 @@ BOOST_AUTO_TEST_CASE( JSON_BufferedHTTP ) blockable_transport->flush() ; try { client.recv_roundTripRPC() ; - } catch (TTransportException e) { + } catch (const TTransportException &e) { BOOST_ERROR( "we should not get a transport exception -- this means we failed: " + std::string(e.what()) ) ; } transport->close(); diff --git a/lib/cpp/test/OneWayTest.thrift b/lib/cpp/test/OneWayTest.thrift index 127e9ffa364..37d1eb32849 100644 --- a/lib/cpp/test/OneWayTest.thrift +++ b/lib/cpp/test/OneWayTest.thrift @@ -26,7 +26,6 @@ namespace java onewaytest namespace cpp onewaytest namespace rb Onewaytest namespace perl OneWayTest -namespace csharp Onewaytest namespace js OneWayTest namespace st OneWayTest namespace py OneWayTest @@ -34,10 +33,8 @@ namespace py.twisted OneWayTest namespace go onewaytest namespace php OneWayTest namespace delphi Onewaytest -namespace cocoa 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 { diff --git a/lib/cpp/test/OpenSSLManualInitTest.cpp b/lib/cpp/test/OpenSSLManualInitTest.cpp index a30b3039a92..935a2051415 100644 --- a/lib/cpp/test/OpenSSLManualInitTest.cpp +++ b/lib/cpp/test/OpenSSLManualInitTest.cpp @@ -62,7 +62,7 @@ void test_openssl_availability() { // uninitialized. It might also fail on very old versions of // OpenSSL... const EVP_MD* md = EVP_get_digestbyname("SHA256"); - BOOST_CHECK(md != NULL); + BOOST_CHECK(md != nullptr); openssl_cleanup(); } @@ -88,6 +88,6 @@ boost::unit_test::test_suite* init_unit_test_suite(int argc, char* argv[]) { suite->add(BOOST_TEST_CASE(test_openssl_availability)); - return NULL; + return nullptr; } #endif \ No newline at end of file diff --git a/lib/cpp/test/OptionalRequiredTest.cpp b/lib/cpp/test/OptionalRequiredTest.cpp index 55fe249ecda..4c435469e92 100644 --- a/lib/cpp/test/OptionalRequiredTest.cpp +++ b/lib/cpp/test/OptionalRequiredTest.cpp @@ -40,7 +40,7 @@ template void trywrite(const Struct& s, bool should_work) { bool worked; try { - TBinaryProtocol protocol(stdcxx::shared_ptr(new TMemoryBuffer)); + TBinaryProtocol protocol(std::shared_ptr(new TMemoryBuffer)); s.write(&protocol); worked = true; } catch (TProtocolException & ex) { @@ -52,7 +52,7 @@ void trywrite(const Struct& s, bool should_work) { template void write_to_read(const Struct1& w, Struct2& r) { - TBinaryProtocol protocol(stdcxx::shared_ptr(new TMemoryBuffer)); + TBinaryProtocol protocol(std::shared_ptr(new TMemoryBuffer)); w.write(&protocol); r.read(&protocol); } @@ -303,7 +303,7 @@ BOOST_AUTO_TEST_CASE(test_optional_required_11) { o1.im_big.push_back(mymap); BOOST_CHECK(o1 == o2); - TBinaryProtocol protocol(stdcxx::shared_ptr(new TMemoryBuffer)); + TBinaryProtocol protocol(std::shared_ptr(new TMemoryBuffer)); o1.write(&protocol); o1.im_big.push_back(mymap); diff --git a/lib/cpp/test/RecursiveTest.cpp b/lib/cpp/test/RecursiveTest.cpp index ce5569bf7f5..ab2e46dd7a9 100644 --- a/lib/cpp/test/RecursiveTest.cpp +++ b/lib/cpp/test/RecursiveTest.cpp @@ -23,7 +23,7 @@ #include "gen-cpp/Recursive_types.h" #include -#include +#include #include #define BOOST_TEST_MODULE RecursiveTest @@ -31,7 +31,7 @@ using apache::thrift::transport::TMemoryBuffer; using apache::thrift::protocol::TBinaryProtocol; -using apache::thrift::stdcxx::shared_ptr; +using std::shared_ptr; BOOST_AUTO_TEST_CASE(test_recursive_1) { shared_ptr buf(new TMemoryBuffer()); @@ -60,8 +60,8 @@ BOOST_AUTO_TEST_CASE(test_recursive_2) { RecList resultlist; resultlist.read(prot.get()); - BOOST_CHECK(resultlist.nextitem != NULL); - BOOST_CHECK(resultlist.nextitem->nextitem == NULL); + BOOST_CHECK(resultlist.nextitem != nullptr); + BOOST_CHECK(resultlist.nextitem->nextitem == nullptr); } BOOST_AUTO_TEST_CASE(test_recursive_3) { @@ -75,8 +75,8 @@ BOOST_AUTO_TEST_CASE(test_recursive_3) { c.write(prot.get()); c.read(prot.get()); - BOOST_CHECK(c.other != NULL); - BOOST_CHECK(c.other->other.other == NULL); + BOOST_CHECK(c.other != nullptr); + BOOST_CHECK(c.other->other.other == nullptr); } BOOST_AUTO_TEST_CASE(test_recursive_4) { diff --git a/lib/cpp/test/SecurityFromBufferTest.cpp b/lib/cpp/test/SecurityFromBufferTest.cpp new file mode 100644 index 00000000000..72a4c2a28f0 --- /dev/null +++ b/lib/cpp/test/SecurityFromBufferTest.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. + */ + +#define BOOST_TEST_MODULE SecurityFromBufferTest +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef __linux__ +#include +#endif + +using apache::thrift::transport::TServerTransport; +using apache::thrift::transport::TSSLServerSocket; +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; +} +std::string certString(const std::string& filename) { + std::ifstream ifs(certFile(filename).string()); + if(!ifs.is_open() || !ifs.good()) { + throw(std::runtime_error("Failed to open key file " + filename + " for reading")); + } + std::stringstream buffer; + buffer << ifs.rdbuf(); + return buffer.str(); +} +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 SecurityFromBufferFixture { + void server(apache::thrift::transport::SSLProtocol protocol) { + try { + boost::mutex::scoped_lock lock(mMutex); + + shared_ptr pServerSocketFactory; + shared_ptr pServerSocket; + + pServerSocketFactory.reset(new TSSLSocketFactory(static_cast(protocol))); + pServerSocketFactory->ciphers("ALL:!ADH:!LOW:!EXP:!MD5:@STRENGTH"); + pServerSocketFactory->loadCertificateFromBuffer(certString("server.crt").c_str()); + pServerSocketFactory->loadPrivateKeyFromBuffer(certString("server.key").c_str()); + pServerSocketFactory->server(true); + pServerSocket.reset(new TSSLServerSocket("localhost", 0, pServerSocketFactory)); + shared_ptr 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 pClientSocketFactory; + shared_ptr pClientSocket; + + try { + pClientSocketFactory.reset(new TSSLSocketFactory(static_cast(protocol))); + pClientSocketFactory->authenticate(true); + pClientSocketFactory->loadCertificateFromBuffer(certString("client.crt").c_str()); + pClientSocketFactory->loadPrivateKeyFromBuffer(certString("client.key").c_str()); + pClientSocketFactory->loadTrustedCertificatesFromBuffer(certString("CA.pem").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, SecurityFromBufferFixture) + +BOOST_AUTO_TEST_CASE(ssl_security_matrix) { + try { + // matrix of connection success between client and server with different SSLProtocol selections + static_assert(apache::thrift::transport::LATEST == 5, "Mismatch in assumed number of ssl protocols"); + 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(&SecurityFromBufferFixture::server, this, + static_cast(si))); + mCVar.wait(lock); // wait for listen() to succeed + lock.unlock(); + (void)threads.create_thread(bind(&SecurityFromBufferFixture::client, this, + static_cast(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/lib/cpp/test/SecurityTest.cpp b/lib/cpp/test/SecurityTest.cpp index 51ee4272716..982a4f30c1e 100644 --- a/lib/cpp/test/SecurityTest.cpp +++ b/lib/cpp/test/SecurityTest.cpp @@ -23,7 +23,7 @@ #include #include #include -#include +#include #include #include #include @@ -40,8 +40,8 @@ using apache::thrift::transport::TTransport; using apache::thrift::transport::TTransportException; using apache::thrift::transport::TTransportFactory; -using apache::thrift::stdcxx::bind; -using apache::thrift::stdcxx::shared_ptr; +using std::bind; +using std::shared_ptr; boost::filesystem::path keyDir; boost::filesystem::path certFile(const std::string& filename) diff --git a/lib/cpp/test/SpecializationTest.cpp b/lib/cpp/test/SpecializationTest.cpp index a060b4fcb59..008837d319c 100644 --- a/lib/cpp/test/SpecializationTest.cpp +++ b/lib/cpp/test/SpecializationTest.cpp @@ -82,8 +82,8 @@ BOOST_AUTO_TEST_CASE(test_specialization_1) { stage2.back().message = "nevermore"; hm.bonks["poe"] = stage2; - apache::thrift::stdcxx::shared_ptr buffer(new TMemoryBuffer()); - apache::thrift::stdcxx::shared_ptr proto(new MyProtocol(buffer)); + std::shared_ptr buffer(new TMemoryBuffer()); + std::shared_ptr proto(new MyProtocol(buffer)); ooe.write(proto.get()); OneOfEach ooe2; diff --git a/lib/cpp/test/TBufferBaseTest.cpp b/lib/cpp/test/TBufferBaseTest.cpp index 4201ddb7149..3aa845bc53a 100644 --- a/lib/cpp/test/TBufferBaseTest.cpp +++ b/lib/cpp/test/TBufferBaseTest.cpp @@ -18,12 +18,12 @@ */ #include -#include +#include #include #include -#include +#include -using apache::thrift::stdcxx::shared_ptr; +using std::shared_ptr; using apache::thrift::transport::TMemoryBuffer; using apache::thrift::transport::TBufferedTransport; using apache::thrift::transport::TFramedTransport; @@ -165,8 +165,8 @@ void init_data() { // Repeatability. Kind of. std::srand(42); - for (size_t i = 0; i < (sizeof(data)/sizeof(data[0])); ++i) { - data[i] = (uint8_t)rand(); + for (unsigned char & i : data) { + i = (uint8_t)rand(); } data_str.assign((char*)data, sizeof(data)); @@ -178,14 +178,14 @@ BOOST_AUTO_TEST_SUITE( TBufferBaseTest ) BOOST_AUTO_TEST_CASE( test_MemoryBuffer_Write_GetBuffer ) { init_data(); - for (int d1 = 0; d1 < 3; d1++) { + for (auto & d1 : dist) { TMemoryBuffer buffer(16); int offset = 0; int index = 0; while (offset < 1<<15) { - buffer.write(&data[offset], dist[d1][index]); - offset += dist[d1][index]; + buffer.write(&data[offset], d1[index]); + offset += d1[index]; index++; } @@ -197,8 +197,8 @@ BOOST_AUTO_TEST_CASE( test_MemoryBuffer_Write_GetBuffer ) { BOOST_AUTO_TEST_CASE( test_MemoryBuffer_Write_Read ) { init_data(); - for (int d1 = 0; d1 < 3; d1++) { - for (int d2 = 0; d2 < 3; d2++) { + for (auto & d1 : dist) { + for (auto & d2 : dist) { TMemoryBuffer buffer(16); uint8_t data_out[1<<15]; int offset; @@ -207,17 +207,17 @@ BOOST_AUTO_TEST_CASE( test_MemoryBuffer_Write_Read ) { offset = 0; index = 0; while (offset < 1<<15) { - buffer.write(&data[offset], dist[d1][index]); - offset += dist[d1][index]; + 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], dist[d2][index]); - BOOST_CHECK_EQUAL(got, dist[d2][index]); - offset += dist[d2][index]; + unsigned int got = buffer.read(&data_out[offset], d2[index]); + BOOST_CHECK_EQUAL(got, d2[index]); + offset += d2[index]; index++; } @@ -229,8 +229,8 @@ BOOST_AUTO_TEST_CASE( test_MemoryBuffer_Write_Read ) { BOOST_AUTO_TEST_CASE( test_MemoryBuffer_Write_ReadString ) { init_data(); - for (int d1 = 0; d1 < 3; d1++) { - for (int d2 = 0; d2 < 3; d2++) { + for (auto & d1 : dist) { + for (auto & d2 : dist) { TMemoryBuffer buffer(16); string output; int offset; @@ -239,17 +239,17 @@ BOOST_AUTO_TEST_CASE( test_MemoryBuffer_Write_ReadString ) { offset = 0; index = 0; while (offset < 1<<15) { - buffer.write(&data[offset], dist[d1][index]); - offset += dist[d1][index]; + buffer.write(&data[offset], d1[index]); + offset += d1[index]; index++; } offset = 0; index = 0; while (offset < 1<<15) { - unsigned int got = buffer.readAppendToString(output, dist[d2][index]); - BOOST_CHECK_EQUAL(got, dist[d2][index]); - offset += dist[d2][index]; + unsigned int got = buffer.readAppendToString(output, d2[index]); + BOOST_CHECK_EQUAL(got, d2[index]); + offset += d2[index]; index++; } @@ -263,8 +263,8 @@ BOOST_AUTO_TEST_CASE( test_MemoryBuffer_Write_Read_Multi1 ) { // Do shorter writes and reads so we don't align to power-of-two boundaries. - for (int d1 = 0; d1 < 3; d1++) { - for (int d2 = 0; d2 < 3; d2++) { + for (auto & d1 : dist) { + for (auto & d2 : dist) { TMemoryBuffer buffer(16); uint8_t data_out[1<<15]; int offset; @@ -274,16 +274,16 @@ BOOST_AUTO_TEST_CASE( test_MemoryBuffer_Write_Read_Multi1 ) { offset = 0; index = 0; while (offset < (1<<15)-42) { - buffer.write(&data[offset], dist[d1][index]); - offset += dist[d1][index]; + buffer.write(&data[offset], d1[index]); + offset += d1[index]; index++; } offset = 0; index = 0; while (offset < (1<<15)-42) { - buffer.read(&data_out[offset], dist[d2][index]); - offset += dist[d2][index]; + buffer.read(&data_out[offset], d2[index]); + offset += d2[index]; index++; } @@ -303,8 +303,8 @@ BOOST_AUTO_TEST_CASE( test_MemoryBuffer_Write_Read_Multi2 ) { // Pull the buffer out of the loop so its state gets worked harder. TMemoryBuffer buffer(16); - for (int d1 = 0; d1 < 3; d1++) { - for (int d2 = 0; d2 < 3; d2++) { + for (auto & d1 : dist) { + for (auto & d2 : dist) { uint8_t data_out[1<<15]; int offset; int index; @@ -313,16 +313,16 @@ BOOST_AUTO_TEST_CASE( test_MemoryBuffer_Write_Read_Multi2 ) { offset = 0; index = 0; while (offset < (1<<15)-42) { - buffer.write(&data[offset], dist[d1][index]); - offset += dist[d1][index]; + buffer.write(&data[offset], d1[index]); + offset += d1[index]; index++; } offset = 0; index = 0; while (offset < (1<<15)-42) { - buffer.read(&data_out[offset], dist[d2][index]); - offset += dist[d2][index]; + buffer.read(&data_out[offset], d2[index]); + offset += d2[index]; index++; } @@ -341,8 +341,8 @@ BOOST_AUTO_TEST_CASE( test_MemoryBuffer_Write_Read_Incomplete ) { // 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 (int d1 = 0; d1 < 3; d1++) { - for (int d2 = 0; d2 < 3; d2++) { + for (auto & d1 : dist) { + for (auto & d2 : dist) { TMemoryBuffer buffer(16); uint8_t data_out[1<<13]; @@ -350,7 +350,7 @@ BOOST_AUTO_TEST_CASE( test_MemoryBuffer_Write_Read_Incomplete ) { int write_index = 0; unsigned int to_write = (1<<14)-42; while (to_write > 0) { - int write_amt = (std::min)(dist[d1][write_index], to_write); + int write_amt = (std::min)(d1[write_index], to_write); buffer.write(&data[write_offset], write_amt); write_offset += write_amt; write_index++; @@ -361,7 +361,7 @@ BOOST_AUTO_TEST_CASE( test_MemoryBuffer_Write_Read_Incomplete ) { int read_index = 0; unsigned int to_read = (1<<13)-42; while (to_read > 0) { - int read_amt = (std::min)(dist[d2][read_index], to_read); + 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; @@ -375,7 +375,7 @@ BOOST_AUTO_TEST_CASE( test_MemoryBuffer_Write_Read_Incomplete ) { int second_index = write_index-1; unsigned int to_second = (1<<14)+42; while (to_second > 0) { - int second_amt = (std::min)(dist[d1][second_index], to_second); + 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; @@ -399,17 +399,16 @@ BOOST_AUTO_TEST_CASE( test_BufferedTransport_Write ) { 1<<14, 1<<17, }; - for (size_t i = 0; i < sizeof (sizes) / sizeof (sizes[0]); i++) { - int size = sizes[i]; - for (int d1 = 0; d1 < 3; d1++) { + for (int size : sizes) { + for (auto & d1 : dist) { shared_ptr buffer(new TMemoryBuffer(16)); TBufferedTransport trans(buffer, size); int offset = 0; int index = 0; while (offset < 1<<15) { - trans.write(&data[offset], dist[d1][index]); - offset += dist[d1][index]; + trans.write(&data[offset], d1[index]); + offset += d1[index]; index++; } trans.flush(); @@ -430,9 +429,8 @@ BOOST_AUTO_TEST_CASE( test_BufferedTransport_Read_Full ) { 1<<14, 1<<17, }; - for (size_t i = 0; i < sizeof (sizes) / sizeof (sizes[0]); i++) { - int size = sizes[i]; - for (int d1 = 0; d1 < 3; d1++) { + for (int size : sizes) { + for (auto & d1 : dist) { shared_ptr buffer(new TMemoryBuffer(data, sizeof(data))); TBufferedTransport trans(buffer, size); uint8_t data_out[1<<15]; @@ -443,8 +441,8 @@ BOOST_AUTO_TEST_CASE( test_BufferedTransport_Read_Full ) { // 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], dist[d1][index]); - offset += dist[d1][index]; + trans.readAll(&data_out[offset], d1[index]); + offset += d1[index]; index++; } @@ -463,9 +461,8 @@ BOOST_AUTO_TEST_CASE( test_BufferedTransport_Read_Short ) { 1<<14, 1<<17, }; - for (size_t i = 0; i < sizeof (sizes) / sizeof (sizes[0]); i++) { - int size = sizes[i]; - for (int d1 = 0; d1 < 3; d1++) { + for (int size : sizes) { + for (auto & d1 : dist) { shared_ptr buffer(new TMemoryBuffer(data, sizeof(data))); shared_ptr tshort(new TShortReadTransport(buffer, 0.125)); TBufferedTransport trans(buffer, size); @@ -477,8 +474,8 @@ BOOST_AUTO_TEST_CASE( test_BufferedTransport_Read_Short ) { // 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], dist[d1][index]); - offset += dist[d1][index]; + trans.readAll(&data_out[offset], d1[index]); + offset += d1[index]; index++; } @@ -497,17 +494,16 @@ BOOST_AUTO_TEST_CASE( test_FramedTransport_Write ) { 1<<14, 1<<17, }; - for (size_t i = 0; i < sizeof (sizes) / sizeof (sizes[0]); i++) { - int size = sizes[i]; - for (int d1 = 0; d1 < 3; d1++) { + for (int size : sizes) { + for (auto & d1 : dist) { shared_ptr buffer(new TMemoryBuffer(16)); TFramedTransport trans(buffer, size); int offset = 0; int index = 0; while (offset < 1<<15) { - trans.write(&data[offset], dist[d1][index]); - offset += dist[d1][index]; + trans.write(&data[offset], d1[index]); + offset += d1[index]; index++; } trans.flush(); @@ -526,7 +522,7 @@ BOOST_AUTO_TEST_CASE( test_FramedTransport_Write ) { BOOST_AUTO_TEST_CASE( test_FramedTransport_Read ) { init_data(); - for (int d1 = 0; d1 < 3; d1++) { + for (auto & d1 : dist) { uint8_t data_out[1<<15]; shared_ptr buffer(new TMemoryBuffer()); TFramedTransport trans(buffer); @@ -539,8 +535,8 @@ BOOST_AUTO_TEST_CASE( test_FramedTransport_Read ) { int index = 0; while (offset < 1<<15) { // This should work with read because we have one huge frame. - trans.read(&data_out[offset], dist[d1][index]); - offset += dist[d1][index]; + trans.read(&data_out[offset], d1[index]); + offset += d1[index]; index++; } @@ -560,11 +556,9 @@ BOOST_AUTO_TEST_CASE( test_FramedTransport_Write_Read ) { int probs[] = { 1, 2, 4, 8, 16, 32, }; - for (size_t i = 0; i < sizeof (sizes) / sizeof (sizes[0]); i++) { - int size = sizes[i]; - for (size_t j = 0; j < sizeof (probs) / sizeof (probs[0]); j++) { - int prob = probs[j]; - for (int d1 = 0; d1 < 3; d1++) { + for (int size : sizes) { + for (int prob : probs) { + for (auto & d1 : dist) { shared_ptr buffer(new TMemoryBuffer(16)); TFramedTransport trans(buffer, size); std::vector data_out(1<<17, 0); @@ -574,9 +568,9 @@ BOOST_AUTO_TEST_CASE( test_FramedTransport_Write_Read ) { int write_index = 0; int flush_size = 0; while (write_offset < 1<<15) { - trans.write(&data[write_offset], dist[d1][write_index]); - write_offset += dist[d1][write_index]; - flush_size += dist[d1][write_index]; + 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); @@ -593,8 +587,7 @@ BOOST_AUTO_TEST_CASE( test_FramedTransport_Write_Read ) { int read_offset = 0; int read_index = 0; - for (unsigned int k = 0; k < flush_sizes.size(); k++) { - int fsize = flush_sizes[k]; + 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. diff --git a/lib/cpp/test/TFileTransportTest.cpp b/lib/cpp/test/TFileTransportTest.cpp index d0c26b3a0c7..c116fdc1b79 100644 --- a/lib/cpp/test/TFileTransportTest.cpp +++ b/lib/cpp/test/TFileTransportTest.cpp @@ -64,12 +64,12 @@ class FsyncLog { }; typedef std::list CallList; - FsyncLog() {} + FsyncLog() = default; void fsync(int fd) { (void)fd; FsyncCall call; - THRIFT_GETTIMEOFDAY(&call.time, NULL); + THRIFT_GETTIMEOFDAY(&call.time, nullptr); calls_.push_back(call); } @@ -123,7 +123,7 @@ class TempFile { if (path_) { ::unlink(path_); delete[] path_; - path_ = NULL; + path_ = nullptr; } } @@ -195,9 +195,9 @@ BOOST_AUTO_TEST_CASE(test_destructor) { struct timeval start; struct timeval end; - THRIFT_GETTIMEOFDAY(&start, NULL); + THRIFT_GETTIMEOFDAY(&start, nullptr); delete transport; - THRIFT_GETTIMEOFDAY(&end, NULL); + THRIFT_GETTIMEOFDAY(&end, nullptr); int delta = time_diff(&start, &end); @@ -264,7 +264,7 @@ void test_flush_max_us_impl(uint32_t flush_us, uint32_t write_us, uint32_t test_ delete transport; // Stop logging new fsync() calls - fsync_log = NULL; + fsync_log = nullptr; // Examine the fsync() log // @@ -278,13 +278,13 @@ void test_flush_max_us_impl(uint32_t flush_us, uint32_t write_us, uint32_t test_ // Make sure TFileTransport called fsync at least once BOOST_WARN_GE(calls->size(), static_cast(1)); - const struct timeval* prev_time = NULL; - for (FsyncLog::CallList::const_iterator it = calls->begin(); it != calls->end(); ++it) { + const struct timeval* prev_time = nullptr; + for (const auto & call : *calls) { if (prev_time) { - int delta = time_diff(prev_time, &it->time); + int delta = time_diff(prev_time, &call.time); BOOST_WARN( delta < max_allowed_delta ); } - prev_time = &it->time; + prev_time = &call.time; } } @@ -318,13 +318,13 @@ BOOST_AUTO_TEST_CASE(test_noop_flush) { transport.write(buf, 1); struct timeval start; - THRIFT_GETTIMEOFDAY(&start, NULL); + THRIFT_GETTIMEOFDAY(&start, nullptr); for (unsigned int n = 0; n < 10; ++n) { transport.flush(); struct timeval now; - THRIFT_GETTIMEOFDAY(&now, NULL); + 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()) @@ -349,11 +349,11 @@ void print_usage(FILE* f, const char* argv0) { void parse_args(int argc, char* argv[]) { struct option long_opts[] - = {{"help", false, NULL, 'h'}, {"tmp-dir", true, NULL, 't'}, {NULL, 0, NULL, 0}}; + = {{"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, NULL); + int optchar = getopt_long(argc, argv, "ht:", long_opts, nullptr); if (optchar == -1) { break; } @@ -378,7 +378,7 @@ void parse_args(int argc, char* argv[]) { #ifdef BOOST_TEST_DYN_LINK static int myArgc = 0; -static char **myArgv = NULL; +static char **myArgv = nullptr; bool init_unit_test_suite() { boost::unit_test::framework::master_test_suite().p_name.value = "TFileTransportTest"; @@ -399,6 +399,6 @@ boost::unit_test::test_suite* init_unit_test_suite(int argc, char* argv[]) { // Parse arguments parse_args(argc, argv); - return NULL; + return nullptr; } #endif diff --git a/lib/cpp/test/TMemoryBufferTest.cpp b/lib/cpp/test/TMemoryBufferTest.cpp index d81b1d80425..759aa0c9b9c 100644 --- a/lib/cpp/test/TMemoryBufferTest.cpp +++ b/lib/cpp/test/TMemoryBufferTest.cpp @@ -17,12 +17,12 @@ * under the License. */ -#include +#include #include #include #include #include -#include +#include #include #include "gen-cpp/ThriftTest_types.h" @@ -31,7 +31,7 @@ BOOST_AUTO_TEST_SUITE(TMemoryBufferTest) using apache::thrift::protocol::TBinaryProtocol; using apache::thrift::transport::TMemoryBuffer; using apache::thrift::transport::TTransportException; -using apache::thrift::stdcxx::shared_ptr; +using std::shared_ptr; using std::cout; using std::endl; using std::string; @@ -81,9 +81,9 @@ BOOST_AUTO_TEST_CASE(test_roundtrip) { } BOOST_AUTO_TEST_CASE(test_readAppendToString) { - string* str1 = new string("abcd1234"); - TMemoryBuffer buf((uint8_t*)str1->data(), - static_cast(str1->length()), + string str1 = "abcd1234"; + TMemoryBuffer buf((uint8_t*)str1.data(), + static_cast(str1.length()), TMemoryBuffer::COPY); string str3 = "wxyz", str4 = "6789"; diff --git a/lib/cpp/test/TNonblockingSSLServerTest.cpp b/lib/cpp/test/TNonblockingSSLServerTest.cpp index 3e9700f1f19..dc40c125703 100644 --- a/lib/cpp/test/TNonblockingSSLServerTest.cpp +++ b/lib/cpp/test/TNonblockingSSLServerTest.cpp @@ -19,8 +19,6 @@ #define BOOST_TEST_MODULE TNonblockingSSLServerTest #include -#include -#include #include #include @@ -41,17 +39,17 @@ using apache::thrift::transport::TSSLSocketFactory; using apache::thrift::transport::TSSLSocket; struct Handler : public test::ParentServiceIf { - void addString(const std::string& s) { strings_.push_back(s); } - void getStrings(std::vector& _return) { _return = strings_; } + void addString(const std::string& s) override { strings_.push_back(s); } + void getStrings(std::vector& _return) override { _return = strings_; } std::vector strings_; // dummy overrides not used in this test - int32_t incrementGeneration() { return 0; } - int32_t getGeneration() { return 0; } - void getDataWait(std::string&, const int32_t) {} - void onewayWait() {} - void exceptionWait(const std::string&) {} - void unexpectedExceptionWait(const std::string&) {} + 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; @@ -105,8 +103,8 @@ BOOST_GLOBAL_FIXTURE(GlobalFixtureSSL); BOOST_GLOBAL_FIXTURE(GlobalFixtureSSL) #endif -stdcxx::shared_ptr createServerSocketFactory() { - stdcxx::shared_ptr pServerSocketFactory; +std::shared_ptr createServerSocketFactory() { + std::shared_ptr pServerSocketFactory; pServerSocketFactory.reset(new TSSLSocketFactory()); pServerSocketFactory->ciphers("ALL:!ADH:!LOW:!EXP:!MD5:@STRENGTH"); @@ -116,8 +114,8 @@ stdcxx::shared_ptr createServerSocketFactory() { return pServerSocketFactory; } -stdcxx::shared_ptr createClientSocketFactory() { - stdcxx::shared_ptr pClientSocketFactory; +std::shared_ptr createClientSocketFactory() { + std::shared_ptr pClientSocketFactory; pClientSocketFactory.reset(new TSSLSocketFactory()); pClientSocketFactory->authenticate(true); @@ -133,7 +131,7 @@ class Fixture { public: ListenEventHandler(Mutex* mutex) : listenMonitor_(mutex), ready_(false) {} - void preServe() /* override */ { + void preServe() override /* override */ { Guard g(listenMonitor_.mutex()); ready_ = true; listenMonitor_.notify(); @@ -145,19 +143,19 @@ class Fixture { struct Runner : public apache::thrift::concurrency::Runnable { int port; - stdcxx::shared_ptr userEventBase; - stdcxx::shared_ptr processor; - stdcxx::shared_ptr server; - stdcxx::shared_ptr listenHandler; - stdcxx::shared_ptr pServerSocketFactory; - stdcxx::shared_ptr socket; + std::shared_ptr userEventBase; + std::shared_ptr processor; + std::shared_ptr server; + std::shared_ptr listenHandler; + std::shared_ptr pServerSocketFactory; + std::shared_ptr socket; Mutex mutex_; - Runner() { + Runner():port(0) { listenHandler.reset(new ListenEventHandler(&mutex_)); } - virtual void run() { + 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(); @@ -198,7 +196,7 @@ class Fixture { }; protected: - Fixture() : processor(new test::ParentServiceProcessor(stdcxx::make_shared())) {} + Fixture() : processor(new test::ParentServiceProcessor(std::make_shared())) {} ~Fixture() { if (server) { @@ -214,18 +212,13 @@ class Fixture { } int startServer(int port) { - stdcxx::shared_ptr runner(new Runner); + std::shared_ptr runner(new Runner); runner->port = port; runner->processor = processor; runner->userEventBase = userEventBase_; - apache::thrift::stdcxx::scoped_ptr threadFactory( - new apache::thrift::concurrency::PlatformThreadFactory( -#if !USE_BOOST_THREAD && !USE_STD_THREAD - concurrency::PlatformThreadFactory::OTHER, concurrency::PlatformThreadFactory::NORMAL, - 1, -#endif - false)); + std::unique_ptr threadFactory( + new apache::thrift::concurrency::ThreadFactory(false)); thread = threadFactory->newThread(runner); thread->start(); runner->readyBarrier(); @@ -235,11 +228,11 @@ class Fixture { } bool canCommunicate(int serverPort) { - stdcxx::shared_ptr pClientSocketFactory = createClientSocketFactory(); - stdcxx::shared_ptr socket = pClientSocketFactory->createSocket("localhost", serverPort); + std::shared_ptr pClientSocketFactory = createClientSocketFactory(); + std::shared_ptr socket = pClientSocketFactory->createSocket("localhost", serverPort); socket->open(); - test::ParentServiceClient client(stdcxx::make_shared( - stdcxx::make_shared(socket))); + test::ParentServiceClient client(std::make_shared( + std::make_shared(socket))); client.addString("foo"); std::vector strings; client.getStrings(strings); @@ -247,12 +240,12 @@ class Fixture { } private: - stdcxx::shared_ptr userEventBase_; - stdcxx::shared_ptr processor; + std::shared_ptr userEventBase_; + std::shared_ptr processor; protected: - stdcxx::shared_ptr server; + std::shared_ptr server; private: - stdcxx::shared_ptr thread; + std::shared_ptr thread; }; diff --git a/lib/cpp/test/TNonblockingServerTest.cpp b/lib/cpp/test/TNonblockingServerTest.cpp index 63d8a046133..f9aab4cc1b8 100644 --- a/lib/cpp/test/TNonblockingServerTest.cpp +++ b/lib/cpp/test/TNonblockingServerTest.cpp @@ -19,12 +19,12 @@ #define BOOST_TEST_MODULE TNonblockingServerTest #include +#include #include "thrift/concurrency/Monitor.h" #include "thrift/concurrency/Thread.h" #include "thrift/server/TNonblockingServer.h" #include "thrift/transport/TNonblockingServerSocket.h" -#include "thrift/stdcxx.h" #include "gen-cpp/ParentService.h" @@ -33,28 +33,28 @@ using apache::thrift::concurrency::Guard; using apache::thrift::concurrency::Monitor; using apache::thrift::concurrency::Mutex; -using apache::thrift::concurrency::PlatformThreadFactory; +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 apache::thrift::stdcxx::make_shared; -using apache::thrift::stdcxx::shared_ptr; +using std::make_shared; +using std::shared_ptr; using namespace apache::thrift; struct Handler : public test::ParentServiceIf { - void addString(const std::string& s) { strings_.push_back(s); } - void getStrings(std::vector& _return) { _return = strings_; } + void addString(const std::string& s) override { strings_.push_back(s); } + void getStrings(std::vector& _return) override { _return = strings_; } std::vector strings_; // dummy overrides not used in this test - int32_t incrementGeneration() { return 0; } - int32_t getGeneration() { return 0; } - void getDataWait(std::string&, const int32_t) {} - void onewayWait() {} - void exceptionWait(const std::string&) {} - void unexpectedExceptionWait(const std::string&) {} + 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 { @@ -63,7 +63,7 @@ class Fixture { public: ListenEventHandler(Mutex* mutex) : listenMonitor_(mutex), ready_(false) {} - void preServe() /* override */ { + void preServe() override /* override */ { Guard g(listenMonitor_.mutex()); ready_ = true; listenMonitor_.notify(); @@ -83,10 +83,11 @@ class Fixture { Mutex mutex_; Runner() { + port = 0; listenHandler.reset(new ListenEventHandler(&mutex_)); } - virtual void run() { + 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); @@ -147,12 +148,7 @@ class Fixture { runner->userEventBase = userEventBase_; shared_ptr threadFactory( - new PlatformThreadFactory( -#if !USE_BOOST_THREAD && !USE_STD_THREAD - PlatformThreadFactory::OTHER, PlatformThreadFactory::NORMAL, - 1, -#endif - false)); + new ThreadFactory(false)); thread = threadFactory->newThread(runner); thread->start(); runner->readyBarrier(); diff --git a/lib/cpp/test/TPipeInterruptTest.cpp b/lib/cpp/test/TPipeInterruptTest.cpp index 232e4bb73e8..2423f5646ec 100644 --- a/lib/cpp/test/TPipeInterruptTest.cpp +++ b/lib/cpp/test/TPipeInterruptTest.cpp @@ -27,7 +27,7 @@ #include #include #include -#include +#include using apache::thrift::transport::TPipeServer; using apache::thrift::transport::TPipe; @@ -52,7 +52,7 @@ static void acceptWorker(TPipeServer *pipe) { { for (;;) { - stdcxx::shared_ptr temp = pipe->accept(); + std::shared_ptr temp = pipe->accept(); } } catch (...) {/*just want to make sure nothing crashes*/ } @@ -70,8 +70,8 @@ BOOST_AUTO_TEST_CASE(stress_pipe_accept_interruption) { { TPipeServer pipeServer("TPipeInterruptTest"); pipeServer.listen(); - boost::thread acceptThread(stdcxx::bind(acceptWorker, &pipeServer)); - boost::thread interruptThread(stdcxx::bind(interruptWorker, &pipeServer)); + boost::thread acceptThread(std::bind(acceptWorker, &pipeServer)); + boost::thread interruptThread(std::bind(interruptWorker, &pipeServer)); try { for (;;) diff --git a/lib/cpp/test/TPipedTransportTest.cpp b/lib/cpp/test/TPipedTransportTest.cpp index a3ce662a3ae..f3091a48779 100644 --- a/lib/cpp/test/TPipedTransportTest.cpp +++ b/lib/cpp/test/TPipedTransportTest.cpp @@ -18,7 +18,7 @@ */ #include -#include +#include #include #include @@ -31,9 +31,9 @@ using apache::thrift::transport::TMemoryBuffer; using namespace apache::thrift; BOOST_AUTO_TEST_CASE(test_read_write) { - stdcxx::shared_ptr underlying(new TMemoryBuffer); - stdcxx::shared_ptr pipe(new TMemoryBuffer); - stdcxx::shared_ptr trans(new TPipedTransport(underlying, pipe)); + std::shared_ptr underlying(new TMemoryBuffer); + std::shared_ptr pipe(new TMemoryBuffer); + std::shared_ptr trans(new TPipedTransport(underlying, pipe)); uint8_t buffer[4]; diff --git a/lib/cpp/test/TSSLSocketInterruptTest.cpp b/lib/cpp/test/TSSLSocketInterruptTest.cpp index 85f6c398dd1..83fb993d456 100644 --- a/lib/cpp/test/TSSLSocketInterruptTest.cpp +++ b/lib/cpp/test/TSSLSocketInterruptTest.cpp @@ -17,14 +17,14 @@ * under the License. */ -#include +#include #include #include #include #include #include #include -#include +#include #include #include #ifdef __linux__ @@ -37,8 +37,8 @@ using apache::thrift::transport::TTransport; using apache::thrift::transport::TTransportException; using apache::thrift::transport::TSSLSocketFactory; -using apache::thrift::stdcxx::static_pointer_cast; -using apache::thrift::stdcxx::shared_ptr; +using std::static_pointer_cast; +using std::shared_ptr; BOOST_AUTO_TEST_SUITE(TSSLSocketInterruptTest) @@ -146,7 +146,7 @@ BOOST_AUTO_TEST_CASE(test_ssl_interruptable_child_read_while_handshaking) { shared_ptr clientSock = pClientSocketFactory->createSocket("localhost", port); clientSock->open(); shared_ptr accepted = sock1.accept(); - boost::thread readThread(apache::thrift::stdcxx::bind(readerWorkerMustThrow, accepted)); + 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(); @@ -166,7 +166,7 @@ BOOST_AUTO_TEST_CASE(test_ssl_interruptable_child_read) { shared_ptr clientSock = pClientSocketFactory->createSocket("localhost", port); clientSock->open(); shared_ptr accepted = sock1.accept(); - boost::thread readThread(apache::thrift::stdcxx::bind(readerWorkerMustThrow, accepted)); + 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 @@ -189,7 +189,7 @@ BOOST_AUTO_TEST_CASE(test_ssl_non_interruptable_child_read) { clientSock->open(); shared_ptr accepted = sock1.accept(); static_pointer_cast(accepted)->setRecvTimeout(1000); - boost::thread readThread(apache::thrift::stdcxx::bind(readerWorker, accepted, 0)); + 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 @@ -241,7 +241,7 @@ BOOST_AUTO_TEST_CASE(test_ssl_interruptable_child_peek) { shared_ptr clientSock = pClientSocketFactory->createSocket("localhost", port); clientSock->open(); shared_ptr accepted = sock1.accept(); - boost::thread peekThread(apache::thrift::stdcxx::bind(peekerWorkerInterrupt, accepted)); + 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 @@ -264,7 +264,7 @@ BOOST_AUTO_TEST_CASE(test_ssl_non_interruptable_child_peek) { clientSock->open(); shared_ptr accepted = sock1.accept(); static_pointer_cast(accepted)->setRecvTimeout(1000); - boost::thread peekThread(apache::thrift::stdcxx::bind(peekerWorker, accepted, false)); + 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 diff --git a/lib/cpp/test/TServerIntegrationTest.cpp b/lib/cpp/test/TServerIntegrationTest.cpp index 5f7b2e88b88..ab235d5a4f7 100644 --- a/lib/cpp/test/TServerIntegrationTest.cpp +++ b/lib/cpp/test/TServerIntegrationTest.cpp @@ -18,8 +18,8 @@ */ #define BOOST_TEST_MODULE TServerIntegrationTest -#include -#include +#include +#include #include #include #include @@ -27,7 +27,7 @@ #include #include #include -#include +#include #include #include #include @@ -55,9 +55,9 @@ using apache::thrift::server::TServerEventHandler; using apache::thrift::server::TSimpleServer; using apache::thrift::server::TThreadPoolServer; using apache::thrift::server::TThreadedServer; -using apache::thrift::stdcxx::dynamic_pointer_cast; -using apache::thrift::stdcxx::make_shared; -using apache::thrift::stdcxx::shared_ptr; +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; @@ -74,21 +74,21 @@ using boost::posix_time::milliseconds; class TServerReadyEventHandler : public TServerEventHandler, public Monitor { public: TServerReadyEventHandler() : isListening_(false), accepted_(0) {} - virtual ~TServerReadyEventHandler() {} - virtual void preServe() { + ~TServerReadyEventHandler() override = default; + void preServe() override { Synchronized sync(*this); isListening_ = true; notify(); } - virtual void* createContext(shared_ptr input, - shared_ptr output) { + void* createContext(shared_ptr input, + shared_ptr output) override { Synchronized sync(*this); ++accepted_; notify(); (void)input; (void)output; - return NULL; + return nullptr; } bool isListening() const { return isListening_; } uint64_t acceptedCount() const { return accepted_; } @@ -105,36 +105,36 @@ class ParentHandler : public ParentServiceIf { public: ParentHandler() : generation_(0) {} - int32_t incrementGeneration() { + int32_t incrementGeneration() override { Guard g(mutex_); return ++generation_; } - int32_t getGeneration() { + int32_t getGeneration() override { Guard g(mutex_); return generation_; } - void addString(const std::string& s) { + void addString(const std::string& s) override { Guard g(mutex_); strings_.push_back(s); } - void getStrings(std::vector& _return) { + void getStrings(std::vector& _return) override { Guard g(mutex_); _return = strings_; } - void getDataWait(std::string& _return, const int32_t length) { + void getDataWait(std::string& _return, const int32_t length) override { THRIFT_UNUSED_VARIABLE(_return); THRIFT_UNUSED_VARIABLE(length); } - void onewayWait() {} + void onewayWait() override {} - void exceptionWait(const std::string& message) { THRIFT_UNUSED_VARIABLE(message); } + void exceptionWait(const std::string& message) override { THRIFT_UNUSED_VARIABLE(message); } - void unexpectedExceptionWait(const std::string& message) { THRIFT_UNUSED_VARIABLE(message); } + void unexpectedExceptionWait(const std::string& message) override { THRIFT_UNUSED_VARIABLE(message); } protected: Mutex mutex_; @@ -177,7 +177,7 @@ class TServerIntegrationTestFixture { } void startServer() { - pServerThread.reset(new boost::thread(apache::thrift::stdcxx::bind(&TServerType::serve, pServer.get()))); + 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())); @@ -235,7 +235,7 @@ class TServerIntegrationTestFixture { pClientSock->open(); client.incrementGeneration(); holdThreads.push_back(shared_ptr( - new boost::thread(apache::thrift::stdcxx::bind(&TServerIntegrationTestFixture::delayClose, + new boost::thread(std::bind(&TServerIntegrationTestFixture::delayClose, this, pClientSock, milliseconds(10 * numToMake))))); @@ -264,7 +264,7 @@ class TServerIntegrationTestFixture { * \returns the server port number */ int getServerPort() { - TServerSocket* pSock = dynamic_cast(pServer->getServerTransport().get()); + auto* pSock = dynamic_cast(pServer->getServerTransport().get()); if (!pSock) { throw std::logic_error("how come?"); } return pSock->getPort(); } @@ -284,7 +284,7 @@ class TServerIntegrationTestFixture { std::vector > holdThreads; for (int64_t i = 0; i < numToMake; ++i) { holdThreads.push_back(shared_ptr( - new boost::thread(apache::thrift::stdcxx::bind(&TServerIntegrationTestFixture::stressor, this)))); + new boost::thread(std::bind(&TServerIntegrationTestFixture::stressor, this)))); } boost::this_thread::sleep(duration); @@ -310,10 +310,10 @@ class TServerIntegrationTestFixture { shared_ptr pProtocol(new TBinaryProtocol(pSocket)); ParentServiceClient client(pProtocol); pSocket->open(); - bStressConnectionCount.fetch_add(1, boost::memory_order_relaxed); + bStressConnectionCount.fetch_add(1, std::memory_order_relaxed); for (int i = 0; i < rand() % 1000; ++i) { client.incrementGeneration(); - bStressRequestCount.fetch_add(1, boost::memory_order_relaxed); + bStressRequestCount.fetch_add(1, std::memory_order_relaxed); } } } @@ -321,9 +321,9 @@ class TServerIntegrationTestFixture { shared_ptr pServer; shared_ptr pEventHandler; shared_ptr pServerThread; - boost::atomic bStressDone; - boost::atomic_int64_t bStressConnectionCount; - boost::atomic_int64_t bStressRequestCount; + std::atomic bStressDone; + std::atomic bStressConnectionCount; + std::atomic bStressRequestCount; }; template @@ -379,7 +379,7 @@ BOOST_FIXTURE_TEST_CASE(test_threadpool_factory, TServerIntegrationProcessorFactoryTestFixture) { pServer->getThreadManager()->threadFactory( shared_ptr( - new apache::thrift::concurrency::PlatformThreadFactory)); + new apache::thrift::concurrency::ThreadFactory)); pServer->getThreadManager()->start(); // thread factory has 4 threads as a default @@ -394,7 +394,7 @@ BOOST_FIXTURE_TEST_CASE(test_threadpool, TServerIntegrationProcessorTestFixture) { pServer->getThreadManager()->threadFactory( shared_ptr( - new apache::thrift::concurrency::PlatformThreadFactory)); + new apache::thrift::concurrency::ThreadFactory)); pServer->getThreadManager()->start(); // thread factory has 4 threads as a default @@ -409,7 +409,7 @@ BOOST_FIXTURE_TEST_CASE(test_threadpool_bound, TServerIntegrationProcessorTestFixture) { pServer->getThreadManager()->threadFactory( shared_ptr( - new apache::thrift::concurrency::PlatformThreadFactory)); + new apache::thrift::concurrency::ThreadFactory)); pServer->getThreadManager()->start(); pServer->setConcurrentClientLimit(4); @@ -420,7 +420,7 @@ BOOST_FIXTURE_TEST_CASE(test_threadpool_stress, TServerIntegrationProcessorTestFixture) { pServer->getThreadManager()->threadFactory( shared_ptr( - new apache::thrift::concurrency::PlatformThreadFactory)); + new apache::thrift::concurrency::ThreadFactory)); pServer->getThreadManager()->start(); stress(10, boost::posix_time::seconds(3)); @@ -479,11 +479,11 @@ BOOST_AUTO_TEST_CASE(test_stop_with_uninterruptable_clients_connected) { // Ensure they have been accepted blockUntilAccepted(2); - boost::thread t1(apache::thrift::stdcxx::bind(&TServerIntegrationTestFixture::delayClose, + boost::thread t1(std::bind(&TServerIntegrationTestFixture::delayClose, this, pClientSock1, milliseconds(250))); - boost::thread t2(apache::thrift::stdcxx::bind(&TServerIntegrationTestFixture::delayClose, + boost::thread t2(std::bind(&TServerIntegrationTestFixture::delayClose, this, pClientSock2, milliseconds(250))); @@ -517,7 +517,7 @@ BOOST_AUTO_TEST_CASE(test_concurrent_client_limit) { BOOST_CHECK_EQUAL(2, pServer->getConcurrentClientCount()); // a third client cannot connect until one of the other two closes - boost::thread t2(apache::thrift::stdcxx::bind(&TServerIntegrationTestFixture::delayClose, + boost::thread t2(std::bind(&TServerIntegrationTestFixture::delayClose, this, pClientSock2, milliseconds(250))); diff --git a/lib/cpp/test/TServerSocketTest.cpp b/lib/cpp/test/TServerSocketTest.cpp index a191147eb17..929defa0a89 100644 --- a/lib/cpp/test/TServerSocketTest.cpp +++ b/lib/cpp/test/TServerSocketTest.cpp @@ -17,10 +17,10 @@ * under the License. */ -#include +#include #include #include -#include +#include #include "TTransportCheckThrow.h" #include @@ -28,13 +28,14 @@ using apache::thrift::transport::TServerSocket; using apache::thrift::transport::TSocket; using apache::thrift::transport::TTransport; using apache::thrift::transport::TTransportException; -using apache::thrift::stdcxx::shared_ptr; +using std::shared_ptr; BOOST_AUTO_TEST_SUITE(TServerSocketTest) BOOST_AUTO_TEST_CASE(test_bind_to_address) { TServerSocket sock1("localhost", 0); sock1.listen(); + BOOST_CHECK(sock1.isOpen()); int port = sock1.getPort(); TSocket clientSock("localhost", port); clientSock.open(); @@ -48,17 +49,20 @@ BOOST_AUTO_TEST_CASE(test_bind_to_address) { sock2.close(); } -BOOST_AUTO_TEST_CASE(test_listen_valid_port) { +BOOST_AUTO_TEST_CASE(test_listen_invalid_port) { TServerSocket sock1(-1); TTRANSPORT_CHECK_THROW(sock1.listen(), TTransportException::BAD_ARGS); + BOOST_CHECK(!sock1.isOpen()); TServerSocket sock2(65536); TTRANSPORT_CHECK_THROW(sock2.listen(), TTransportException::BAD_ARGS); + BOOST_CHECK(!sock1.isOpen()); } BOOST_AUTO_TEST_CASE(test_close_before_listen) { TServerSocket sock1("localhost", 0); sock1.close(); + BOOST_CHECK(!sock1.isOpen()); } BOOST_AUTO_TEST_CASE(test_get_port) { diff --git a/lib/cpp/test/TServerTransportTest.cpp b/lib/cpp/test/TServerTransportTest.cpp index dc6aede87ca..8944737d583 100644 --- a/lib/cpp/test/TServerTransportTest.cpp +++ b/lib/cpp/test/TServerTransportTest.cpp @@ -17,15 +17,15 @@ * under the License. */ -#include +#include #include #include -#include +#include using apache::thrift::transport::TServerTransport; using apache::thrift::transport::TTransport; using apache::thrift::transport::TTransportException; -using apache::thrift::stdcxx::shared_ptr; +using std::shared_ptr; BOOST_AUTO_TEST_SUITE(TServerTransportTest) @@ -34,12 +34,12 @@ class TestTTransport : public TTransport {}; class TestTServerTransport : public TServerTransport { public: TestTServerTransport() : valid_(true) {} - void close() {} + void close() override {} bool valid_; protected: - shared_ptr acceptImpl() { - return valid_ ? shared_ptr(new TestTTransport) + shared_ptr acceptImpl() override { + return valid_ ? std::make_shared() : shared_ptr(); } }; diff --git a/lib/cpp/test/TSocketInterruptTest.cpp b/lib/cpp/test/TSocketInterruptTest.cpp index 3a189cce52a..1e3e59c5cda 100644 --- a/lib/cpp/test/TSocketInterruptTest.cpp +++ b/lib/cpp/test/TSocketInterruptTest.cpp @@ -18,14 +18,14 @@ */ #define BOOST_TEST_MODULE TSocketInterruptTest -#include +#include #include #include #include #include #include -#include +#include using apache::thrift::transport::TServerSocket; using apache::thrift::transport::TSocket; @@ -35,12 +35,12 @@ using namespace apache::thrift; BOOST_AUTO_TEST_SUITE(TSocketInterruptTest) -void readerWorker(stdcxx::shared_ptr tt, uint32_t expectedResult) { +void readerWorker(std::shared_ptr tt, uint32_t expectedResult) { uint8_t buf[4]; BOOST_CHECK_EQUAL(expectedResult, tt->read(buf, 4)); } -void readerWorkerMustThrow(stdcxx::shared_ptr tt) { +void readerWorkerMustThrow(std::shared_ptr tt) { try { uint8_t buf[4]; tt->read(buf, 4); @@ -53,11 +53,12 @@ void readerWorkerMustThrow(stdcxx::shared_ptr tt) { BOOST_AUTO_TEST_CASE(test_interruptable_child_read) { TServerSocket sock1("localhost", 0); sock1.listen(); + BOOST_CHECK(sock1.isOpen()); int port = sock1.getPort(); TSocket clientSock("localhost", port); clientSock.open(); - stdcxx::shared_ptr accepted = sock1.accept(); - boost::thread readThread(stdcxx::bind(readerWorkerMustThrow, accepted)); + std::shared_ptr 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(); @@ -75,8 +76,8 @@ BOOST_AUTO_TEST_CASE(test_non_interruptable_child_read) { int port = sock1.getPort(); TSocket clientSock("localhost", port); clientSock.open(); - stdcxx::shared_ptr accepted = sock1.accept(); - boost::thread readThread(stdcxx::bind(readerWorker, accepted, 0)); + std::shared_ptr 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(); @@ -97,7 +98,7 @@ BOOST_AUTO_TEST_CASE(test_cannot_change_after_listen) { sock1.close(); } -void peekerWorker(stdcxx::shared_ptr tt, bool expectedResult) { +void peekerWorker(std::shared_ptr tt, bool expectedResult) { BOOST_CHECK_EQUAL(expectedResult, tt->peek()); } @@ -107,9 +108,9 @@ BOOST_AUTO_TEST_CASE(test_interruptable_child_peek) { int port = sock1.getPort(); TSocket clientSock("localhost", port); clientSock.open(); - stdcxx::shared_ptr accepted = sock1.accept(); + std::shared_ptr accepted = sock1.accept(); // peek() will return false if child is interrupted - boost::thread peekThread(stdcxx::bind(peekerWorker, accepted, false)); + 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(); @@ -127,9 +128,9 @@ BOOST_AUTO_TEST_CASE(test_non_interruptable_child_peek) { int port = sock1.getPort(); TSocket clientSock("localhost", port); clientSock.open(); - stdcxx::shared_ptr accepted = sock1.accept(); + std::shared_ptr accepted = sock1.accept(); // peek() will return false when remote side is closed - boost::thread peekThread(stdcxx::bind(peekerWorker, accepted, false)); + 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(); diff --git a/lib/cpp/test/ThrifttReadCheckTests.cpp b/lib/cpp/test/ThrifttReadCheckTests.cpp new file mode 100644 index 00000000000..eb4ca01b235 --- /dev/null +++ b/lib/cpp/test/ThrifttReadCheckTests.cpp @@ -0,0 +1,227 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#define MAX_MESSAGE_SIZE 2 + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +BOOST_AUTO_TEST_SUITE(ThriftReadCheckExceptionTest) + +using apache::thrift::TConfiguration; +using apache::thrift::protocol::TBinaryProtocol; +using apache::thrift::protocol::TCompactProtocol; +using apache::thrift::protocol::TJSONProtocol; +using apache::thrift::protocol::TType; +using apache::thrift::transport::TPipedTransport; +using apache::thrift::transport::TMemoryBuffer; +using apache::thrift::transport::TSimpleFileTransport; +using apache::thrift::transport::TFileTransport; +using apache::thrift::transport::TFDTransport; +using apache::thrift::transport::TTransportException; +using apache::thrift::transport::TBufferedTransport; +using apache::thrift::transport::TFramedTransport; +using std::shared_ptr; +using std::cout; +using std::endl; +using std::string; +using std::memset; +using namespace apache::thrift; +using namespace apache::thrift::protocol; + + +BOOST_AUTO_TEST_CASE(test_tmemorybuffer_read_check_exception) { + std::shared_ptr config(new TConfiguration(MAX_MESSAGE_SIZE)); + TMemoryBuffer trans_out(config); + uint8_t buffer[6] = {1, 2, 3, 4, 5, 6}; + trans_out.write((const uint8_t*)buffer, sizeof(buffer)); + trans_out.close(); + + TMemoryBuffer trans_in(config); + memset(buffer, 0, sizeof(buffer)); + BOOST_CHECK_THROW(trans_in.read(buffer, sizeof(buffer)), TTransportException); + trans_in.close(); +} + +BOOST_AUTO_TEST_CASE(test_tpipedtransport_read_check_exception) { + std::shared_ptr config(new TConfiguration(MAX_MESSAGE_SIZE)); + std::shared_ptr pipe(new TMemoryBuffer); + std::shared_ptr underlying(new TMemoryBuffer); + std::shared_ptr trans(new TPipedTransport(underlying, pipe, config)); + + uint8_t buffer[4]; + + underlying->write((uint8_t*)"abcd", 4); + BOOST_CHECK_THROW(trans->read(buffer, sizeof(buffer)), TTransportException); + BOOST_CHECK_THROW(trans->readAll(buffer, sizeof(buffer)), TTransportException); + trans->readEnd(); + pipe->resetBuffer(); + underlying->write((uint8_t*)"ef", 2); + BOOST_CHECK_THROW(trans->read(buffer, sizeof(buffer)), TTransportException); + BOOST_CHECK_THROW(trans->readAll(buffer, sizeof(buffer)), TTransportException); + trans->readEnd(); +} + +BOOST_AUTO_TEST_CASE(test_tsimplefiletransport_read_check_exception) { + std::shared_ptr config(new TConfiguration(MAX_MESSAGE_SIZE)); + TSimpleFileTransport trans_out("data", false, true, config); + uint8_t buffer[6] = {1, 2, 3, 4, 5, 6}; + trans_out.write((const uint8_t*)buffer, sizeof(buffer)); + trans_out.close(); + + TSimpleFileTransport trans_in("data",true, false, config); + memset(buffer, 0, sizeof(buffer)); + BOOST_CHECK_THROW(trans_in.read(buffer, sizeof(buffer)), TTransportException); + trans_in.close(); + + remove("./data"); +} + +BOOST_AUTO_TEST_CASE(test_tfiletransport_read_check_exception) { + std::shared_ptr config(new TConfiguration(MAX_MESSAGE_SIZE)); + TFileTransport trans_out("data", false, config); + uint8_t buffer[6] = {1, 2, 3, 4, 5, 6}; + trans_out.write((const uint8_t*)buffer, sizeof(buffer)); + + TFileTransport trans_in("data", false, config); + memset(buffer, 0, sizeof(buffer)); + BOOST_CHECK_THROW(trans_in.read(buffer, sizeof(buffer)), TTransportException); + + remove("./data"); +} + +BOOST_AUTO_TEST_CASE(test_tbufferedtransport_read_check_exception) { + uint8_t arr[4] = {1, 2, 3, 4}; + std::shared_ptr buffer (new TMemoryBuffer(arr, sizeof(arr))); + std::shared_ptr config (new TConfiguration(MAX_MESSAGE_SIZE)); + std::shared_ptr trans (new TBufferedTransport(buffer, config)); + + trans->write((const uint8_t*)arr, sizeof(arr)); + BOOST_CHECK_THROW(trans->read(arr, sizeof(arr)), TTransportException); +} + +BOOST_AUTO_TEST_CASE(test_tframedtransport_read_check_exception) { + uint8_t arr[4] = {1, 2, 3, 4}; + std::shared_ptr buffer (new TMemoryBuffer(arr, sizeof(arr))); + std::shared_ptr config (new TConfiguration(MAX_MESSAGE_SIZE)); + std::shared_ptr trans (new TFramedTransport(buffer, config)); + + trans->write((const uint8_t*)arr, sizeof(arr)); + BOOST_CHECK_THROW(trans->read(arr, sizeof(arr)), TTransportException); +} + +BOOST_AUTO_TEST_CASE(test_tthriftbinaryprotocol_read_check_exception) { + std::shared_ptr config (new TConfiguration(MAX_MESSAGE_SIZE)); + std::shared_ptr transport(new TMemoryBuffer(config)); + std::shared_ptr protocol(new TBinaryProtocol(transport)); + + uint32_t val = 0; + TType elemType = apache::thrift::protocol::T_STOP; + TType elemType1 = apache::thrift::protocol::T_STOP; + TList list(T_I32, 8); + protocol->writeListBegin(list.elemType_, list.size_); + protocol->writeListEnd(); + BOOST_CHECK_THROW(protocol->readListBegin(elemType, val), TTransportException); + protocol->readListEnd(); + + TSet set(T_I32, 8); + protocol->writeSetBegin(set.elemType_, set.size_); + protocol->writeSetEnd(); + BOOST_CHECK_THROW(protocol->readSetBegin(elemType, val), TTransportException); + protocol->readSetEnd(); + + TMap map(T_I32, T_I32, 8); + protocol->writeMapBegin(map.keyType_, map.valueType_, map.size_); + protocol->writeMapEnd(); + BOOST_CHECK_THROW(protocol->readMapBegin(elemType, elemType1, val), TTransportException); + protocol->readMapEnd(); +} + +BOOST_AUTO_TEST_CASE(test_tthriftcompactprotocol_read_check_exception) { + std::shared_ptr config (new TConfiguration(MAX_MESSAGE_SIZE)); + std::shared_ptr transport(new TMemoryBuffer(config)); + std::shared_ptr protocol(new TCompactProtocol(transport)); + + uint32_t val = 0; + TType elemType = apache::thrift::protocol::T_STOP; + TType elemType1 = apache::thrift::protocol::T_STOP; + TList list(T_I32, 8); + protocol->writeListBegin(list.elemType_, list.size_); + protocol->writeListEnd(); + BOOST_CHECK_THROW(protocol->readListBegin(elemType, val), TTransportException); + protocol->readListEnd(); + + TSet set(T_I32, 8); + protocol->writeSetBegin(set.elemType_, set.size_); + protocol->writeSetEnd(); + BOOST_CHECK_THROW(protocol->readSetBegin(elemType, val), TTransportException); + protocol->readSetEnd(); + + TMap map(T_I32, T_I32, 8); + protocol->writeMapBegin(map.keyType_, map.valueType_, map.size_); + protocol->writeMapEnd(); + BOOST_CHECK_THROW(protocol->readMapBegin(elemType, elemType1, val), TTransportException); + protocol->readMapEnd(); +} + +BOOST_AUTO_TEST_CASE(test_tthriftjsonprotocol_read_check_exception) { + std::shared_ptr config (new TConfiguration(MAX_MESSAGE_SIZE)); + std::shared_ptr transport(new TMemoryBuffer(config)); + std::shared_ptr protocol(new TJSONProtocol(transport)); + + uint32_t val = 0; + TType elemType = apache::thrift::protocol::T_STOP; + TType elemType1 = apache::thrift::protocol::T_STOP; + TList list(T_I32, 8); + protocol->writeListBegin(list.elemType_, list.size_); + protocol->writeListEnd(); + BOOST_CHECK_THROW(protocol->readListBegin(elemType, val), TTransportException); + protocol->readListEnd(); + + TSet set(T_I32, 8); + protocol->writeSetBegin(set.elemType_, set.size_); + protocol->writeSetEnd(); + BOOST_CHECK_THROW(protocol->readSetBegin(elemType, val), TTransportException); + protocol->readSetEnd(); + + TMap map(T_I32, T_I32, 8); + protocol->writeMapBegin(map.keyType_, map.valueType_, map.size_); + protocol->writeMapEnd(); + BOOST_CHECK_THROW(protocol->readMapBegin(elemType, elemType1, val), TTransportException); + protocol->readMapEnd(); +} + +BOOST_AUTO_TEST_SUITE_END() diff --git a/lib/cpp/test/ToStringTest.cpp b/lib/cpp/test/ToStringTest.cpp index d204cb346ed..5a05ed70a61 100644 --- a/lib/cpp/test/ToStringTest.cpp +++ b/lib/cpp/test/ToStringTest.cpp @@ -20,7 +20,7 @@ #include #include -#include +#include #include diff --git a/lib/cpp/test/TransportTest.cpp b/lib/cpp/test/TransportTest.cpp index d6ab457e2f7..085197a084e 100644 --- a/lib/cpp/test/TransportTest.cpp +++ b/lib/cpp/test/TransportTest.cpp @@ -26,7 +26,6 @@ #endif #include #include -#include #include #include @@ -58,7 +57,7 @@ void initrand(unsigned int seed) { class SizeGenerator { public: - virtual ~SizeGenerator() {} + virtual ~SizeGenerator() = default; virtual uint32_t nextSize() = 0; virtual std::string describe() const = 0; }; @@ -66,8 +65,8 @@ class SizeGenerator { class ConstantSizeGenerator : public SizeGenerator { public: ConstantSizeGenerator(uint32_t value) : value_(value) {} - uint32_t nextSize() { return value_; } - std::string describe() const { + uint32_t nextSize() override { return value_; } + std::string describe() const override { std::ostringstream desc; desc << value_; return desc.str(); @@ -82,9 +81,9 @@ class RandomSizeGenerator : public SizeGenerator { RandomSizeGenerator(uint32_t min, uint32_t max) : generator_(rng, boost::uniform_int(min, max)) {} - uint32_t nextSize() { return generator_(); } + uint32_t nextSize() override { return generator_(); } - std::string describe() const { + std::string describe() const override { std::ostringstream desc; desc << "rand(" << getMin() << ", " << getMax() << ")"; return desc.str(); @@ -110,11 +109,11 @@ class GenericSizeGenerator : public SizeGenerator { GenericSizeGenerator(uint32_t min, uint32_t max) : generator_(new RandomSizeGenerator(min, max)) {} - uint32_t nextSize() { return generator_->nextSize(); } - std::string describe() const { return generator_->describe(); } + uint32_t nextSize() override { return generator_->nextSize(); } + std::string describe() const override { return generator_->describe(); } private: - stdcxx::shared_ptr generator_; + std::shared_ptr generator_; }; /************************************************************************** @@ -132,17 +131,17 @@ class GenericSizeGenerator : public SizeGenerator { template class CoupledTransports { public: - virtual ~CoupledTransports() {} + virtual ~CoupledTransports() = default; typedef Transport_ TransportType; CoupledTransports() : in(), out() {} - stdcxx::shared_ptr in; - stdcxx::shared_ptr out; + std::shared_ptr in; + std::shared_ptr out; private: - CoupledTransports(const CoupledTransports&); - CoupledTransports& operator=(const CoupledTransports&); + CoupledTransports(const CoupledTransports&) = delete; + CoupledTransports& operator=(const CoupledTransports&) = delete; }; /** @@ -155,7 +154,7 @@ class CoupledMemoryBuffers : public CoupledTransports { out = buf; } - stdcxx::shared_ptr buf; + std::shared_ptr buf; }; /** @@ -231,7 +230,7 @@ class CoupledPipeTransports : public CoupledTransports { HANDLE hWrite; CoupledPipeTransports() { - BOOST_REQUIRE(CreatePipe(&hRead, &hWrite, NULL, 1048576 * 2)); + BOOST_REQUIRE(CreatePipe(&hRead, &hWrite, nullptr, 1048576 * 2)); in.reset(new TPipe(hRead, hWrite)); in->open(); out = in; @@ -283,7 +282,7 @@ class CoupledFileTransports : public CoupledTransports { out.reset(new TFileTransport(filename)); } - ~CoupledFileTransports() { remove(filename.c_str()); } + ~CoupledFileTransports() override { remove(filename.c_str()); } std::string filename; }; @@ -341,11 +340,11 @@ class CoupledBufferBases : public CoupledTransports { **************************************************************************/ struct TriggerInfo { - TriggerInfo(int seconds, const stdcxx::shared_ptr& transport, uint32_t writeLength) - : timeoutSeconds(seconds), transport(transport), writeLength(writeLength), next(NULL) {} + TriggerInfo(int seconds, const std::shared_ptr& transport, uint32_t writeLength) + : timeoutSeconds(seconds), transport(transport), writeLength(writeLength), next(nullptr) {} int timeoutSeconds; - stdcxx::shared_ptr transport; + std::shared_ptr transport; uint32_t writeLength; TriggerInfo* next; }; @@ -356,7 +355,7 @@ unsigned int g_numTriggersFired; bool g_teardown = false; void alarm_handler() { - TriggerInfo* info = NULL; + TriggerInfo* info = nullptr; { apache::thrift::concurrency::Synchronized s(g_alarm_monitor); // The alarm timed out, which almost certainly means we're stuck @@ -367,7 +366,7 @@ void alarm_handler() { // 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 == NULL) { + if (g_triggerInfo == nullptr) { printf(" trigger stack is empty!\n"); } @@ -378,7 +377,7 @@ void alarm_handler() { } // Write some data to the transport to hopefully unblock it. - uint8_t* buf = new uint8_t[info->writeLength]; + auto* buf = new uint8_t[info->writeLength]; memset(buf, 'b', info->writeLength); boost::scoped_array array(buf); info->transport->write(buf, info->writeLength); @@ -396,7 +395,7 @@ void alarm_handler_wrapper() { if (g_teardown) return; // calculate timeout - if (g_triggerInfo == NULL) { + if (g_triggerInfo == nullptr) { timeout = 0; } else { timeout = g_triggerInfo->timeoutSeconds * 1000; @@ -420,12 +419,12 @@ void alarm_handler_wrapper() { * to the end.) */ void add_trigger(unsigned int seconds, - const stdcxx::shared_ptr& transport, + const std::shared_ptr& transport, uint32_t write_len) { - TriggerInfo* info = new TriggerInfo(seconds, transport, write_len); + auto* info = new TriggerInfo(seconds, transport, write_len); { apache::thrift::concurrency::Synchronized s(g_alarm_monitor); - if (g_triggerInfo == NULL) { + if (g_triggerInfo == nullptr) { // This is the first trigger. // Set g_triggerInfo, and schedule the alarm g_triggerInfo = info; @@ -442,17 +441,17 @@ void add_trigger(unsigned int seconds, } void clear_triggers() { - TriggerInfo* info = NULL; + TriggerInfo* info = nullptr; { apache::thrift::concurrency::Synchronized s(g_alarm_monitor); info = g_triggerInfo; - g_triggerInfo = NULL; + g_triggerInfo = nullptr; g_numTriggersFired = 0; g_alarm_monitor.notify(); } - while (info != NULL) { + while (info != nullptr) { TriggerInfo* next = info->next; delete info; info = next; @@ -460,7 +459,7 @@ void clear_triggers() { } void set_trigger(unsigned int seconds, - const stdcxx::shared_ptr& transport, + const std::shared_ptr& transport, uint32_t write_len) { clear_triggers(); add_trigger(seconds, transport, write_len); @@ -501,8 +500,8 @@ void test_rw(uint32_t totalSize, SizeGenerator& rChunkGenerator, uint32_t maxOutstanding) { CoupledTransports transports; - BOOST_REQUIRE(transports.in != NULL); - BOOST_REQUIRE(transports.out != NULL); + BOOST_REQUIRE(transports.in != nullptr); + BOOST_REQUIRE(transports.out != nullptr); boost::shared_array wbuf = boost::shared_array(new uint8_t[totalSize]); boost::shared_array rbuf = boost::shared_array(new uint8_t[totalSize]); @@ -594,8 +593,8 @@ void test_rw(uint32_t totalSize, template void test_read_part_available() { CoupledTransports transports; - BOOST_REQUIRE(transports.in != NULL); - BOOST_REQUIRE(transports.out != NULL); + BOOST_REQUIRE(transports.in != nullptr); + BOOST_REQUIRE(transports.out != nullptr); uint8_t write_buf[16]; uint8_t read_buf[16]; @@ -616,8 +615,8 @@ void test_read_part_available() { template void test_read_part_available_in_chunks() { CoupledTransports transports; - BOOST_REQUIRE(transports.in != NULL); - BOOST_REQUIRE(transports.out != NULL); + BOOST_REQUIRE(transports.in != nullptr); + BOOST_REQUIRE(transports.out != nullptr); uint8_t write_buf[16]; uint8_t read_buf[16]; @@ -643,8 +642,8 @@ void test_read_part_available_in_chunks() { template void test_read_partial_midframe() { CoupledTransports transports; - BOOST_REQUIRE(transports.in != NULL); - BOOST_REQUIRE(transports.out != NULL); + BOOST_REQUIRE(transports.in != nullptr); + BOOST_REQUIRE(transports.out != nullptr); uint8_t write_buf[16]; uint8_t read_buf[16]; @@ -701,14 +700,14 @@ void test_read_partial_midframe() { template void test_borrow_part_available() { CoupledTransports transports; - BOOST_REQUIRE(transports.in != NULL); - BOOST_REQUIRE(transports.out != NULL); + 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 + // Attemping to borrow 10 bytes when only 9 are available should return nullptr // immediately. transports.out->write(write_buf, 9); transports.out->flush(); @@ -716,7 +715,7 @@ void test_borrow_part_available() { 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 == NULL); + BOOST_CHECK(borrowed_buf == nullptr); clear_triggers(); } @@ -724,12 +723,10 @@ void test_borrow_part_available() { template void test_read_none_available() { CoupledTransports transports; - BOOST_REQUIRE(transports.in != NULL); - BOOST_REQUIRE(transports.out != NULL); + 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)); // Attempting to read when no data is available should either block until // some data is available, or fail immediately. (e.g., TSocket blocks, @@ -754,8 +751,8 @@ void test_read_none_available() { template void test_borrow_none_available() { CoupledTransports transports; - BOOST_REQUIRE(transports.in != NULL); - BOOST_REQUIRE(transports.out != NULL); + BOOST_REQUIRE(transports.in != nullptr); + BOOST_REQUIRE(transports.out != nullptr); uint8_t write_buf[16]; memset(write_buf, 'a', sizeof(write_buf)); @@ -763,8 +760,8 @@ void test_borrow_none_available() { // 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(NULL, &borrow_len); - BOOST_CHECK(borrowed_buf == NULL); + 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(); @@ -976,11 +973,11 @@ class TransportTestGen { << rChunkSizeGen.describe() << ", " << maxOutstanding << ")"; #if (BOOST_VERSION >= 105900) - stdcxx::function test_func + std::function test_func #else boost::unit_test::callback0<> test_func #endif - = stdcxx::bind(test_rw, + = std::bind(test_rw, totalSize, wSizeGen, rSizeGen, @@ -1026,13 +1023,13 @@ class TransportTestGen { **************************************************************************/ struct global_fixture { - stdcxx::shared_ptr alarmThread_; + std::shared_ptr alarmThread_; global_fixture() { #if _WIN32 apache::thrift::transport::TWinsockSingleton::create(); #endif - apache::thrift::concurrency::PlatformThreadFactory factory; + apache::thrift::concurrency::ThreadFactory factory; factory.setDetached(false); alarmThread_ = factory.newThread( @@ -1058,7 +1055,7 @@ BOOST_GLOBAL_FIXTURE(global_fixture) #ifdef BOOST_TEST_DYN_LINK bool init_unit_test_suite() { struct timeval tv; - THRIFT_GETTIMEOFDAY(&tv, NULL); + THRIFT_GETTIMEOFDAY(&tv, nullptr); int seed = tv.tv_sec ^ tv.tv_usec; initrand(seed); @@ -1078,7 +1075,7 @@ 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); + THRIFT_GETTIMEOFDAY(&tv, nullptr); int seed = tv.tv_sec ^ tv.tv_usec; initrand(seed); @@ -1087,6 +1084,6 @@ boost::unit_test::test_suite* init_unit_test_suite(int argc, char* argv[]) { suite->p_name.value = "TransportTest"; TransportTestGen transport_test_generator(suite, 1); transport_test_generator.generate(); - return NULL; + return nullptr; } #endif diff --git a/lib/cpp/test/UnitTestMain.cpp b/lib/cpp/test/UnitTestMain.cpp index f0ef1e4a612..04b37ff276a 100644 --- a/lib/cpp/test/UnitTestMain.cpp +++ b/lib/cpp/test/UnitTestMain.cpp @@ -18,4 +18,4 @@ */ #define BOOST_TEST_MODULE thrift -#include +#include diff --git a/lib/cpp/test/ZlibTest.cpp b/lib/cpp/test/ZlibTest.cpp index ea54487ebe6..274a243913c 100644 --- a/lib/cpp/test/ZlibTest.cpp +++ b/lib/cpp/test/ZlibTest.cpp @@ -36,7 +36,7 @@ #include #include #include -#include +#include #include #include @@ -47,7 +47,7 @@ #include using namespace apache::thrift::transport; -using apache::thrift::stdcxx::shared_ptr; +using std::shared_ptr; using std::string; boost::mt19937 rng; @@ -58,14 +58,14 @@ boost::mt19937 rng; class SizeGenerator { public: - virtual ~SizeGenerator() {} + virtual ~SizeGenerator() = default; virtual unsigned int getSize() = 0; }; class ConstantSizeGenerator : public SizeGenerator { public: ConstantSizeGenerator(unsigned int value) : value_(value) {} - virtual unsigned int getSize() { return value_; } + unsigned int getSize() override { return value_; } private: unsigned int value_; @@ -76,10 +76,10 @@ class LogNormalSizeGenerator : public SizeGenerator { LogNormalSizeGenerator(double mean, double std_dev) : gen_(rng, boost::lognormal_distribution(mean, std_dev)) {} - virtual unsigned int getSize() { + unsigned int getSize() override { // Loop until we get a size of 1 or more while (true) { - unsigned int value = static_cast(gen_()); + auto value = static_cast(gen_()); if (value >= 1) { return value; } @@ -91,13 +91,13 @@ class LogNormalSizeGenerator : public SizeGenerator { }; boost::shared_array gen_uniform_buffer(uint32_t buf_len, uint8_t c) { - uint8_t* buf = new uint8_t[buf_len]; + auto* buf = new uint8_t[buf_len]; memset(buf, c, buf_len); return boost::shared_array(buf); } boost::shared_array gen_compressible_buffer(uint32_t buf_len) { - uint8_t* buf = new uint8_t[buf_len]; + auto* buf = new uint8_t[buf_len]; // Generate small runs of alternately increasing and decreasing bytes boost::uniform_smallint run_length_distribution(1, 64); @@ -129,7 +129,7 @@ boost::shared_array gen_compressible_buffer(uint32_t buf_len) { } boost::shared_array gen_random_buffer(uint32_t buf_len) { - uint8_t* buf = new uint8_t[buf_len]; + auto* buf = new uint8_t[buf_len]; boost::uniform_smallint distribution(0, UINT8_MAX); boost::variate_generator > @@ -347,8 +347,8 @@ void test_get_underlying_transport() { do { \ ::std::ostringstream name_ss; \ name_ss << name << "-" << BOOST_STRINGIZE(_FUNC); \ - ::apache::thrift::stdcxx::function test_func = \ - ::apache::thrift::stdcxx::bind(_FUNC, ##__VA_ARGS__); \ + ::std::function 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); \ @@ -359,7 +359,7 @@ void test_get_underlying_transport() { ::std::ostringstream name_ss; \ name_ss << name << "-" << BOOST_STRINGIZE(_FUNC); \ ::boost::unit_test::test_case* tc \ - = ::boost::unit_test::make_test_case(::apache::thrift::stdcxx::bind(_FUNC, \ + = ::boost::unit_test::make_test_case(::std::bind(_FUNC, \ ##__VA_ARGS__), \ name_ss.str()); \ (suite)->add(tc); \ @@ -427,7 +427,7 @@ void print_usage(FILE* f, const char* argv0) { #ifdef BOOST_TEST_DYN_LINK bool init_unit_test_suite() { - uint32_t seed = static_cast(time(NULL)); + auto seed = static_cast(time(nullptr)); #ifdef HAVE_INTTYPES_H printf("seed: %" PRIu32 "\n", seed); #endif @@ -454,7 +454,7 @@ int main( int argc, char* argv[] ) { 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(time(NULL)); + uint32_t seed = static_cast(time(nullptr)); #ifdef HAVE_INTTYPES_H printf("seed: %" PRIu32 "\n", seed); #endif @@ -470,6 +470,6 @@ boost::unit_test::test_suite* init_unit_test_suite(int argc, char* argv[]) { suite->add(BOOST_TEST_CASE(test_no_write)); - return NULL; + return nullptr; } #endif diff --git a/lib/cpp/test/concurrency/MutexTest.cpp b/lib/cpp/test/concurrency/MutexTest.cpp deleted file mode 100644 index 781ec1a4072..00000000000 --- a/lib/cpp/test/concurrency/MutexTest.cpp +++ /dev/null @@ -1,123 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -// This is linked into the UnitTests test executable - -#include - -#include "thrift/concurrency/Exception.h" -#include "thrift/concurrency/Mutex.h" - -using boost::unit_test::test_suite; -using boost::unit_test::framework::master_test_suite; - -using namespace apache::thrift::concurrency; - -struct LFAT -{ - LFAT() - : uut(Mutex::ERRORCHECK_INITIALIZER) - { - BOOST_CHECK_EQUAL(0, pthread_mutex_init(&mx, 0)); - BOOST_CHECK_EQUAL(0, pthread_cond_init(&cv, 0)); - } - - Mutex uut; - pthread_mutex_t mx; - pthread_cond_t cv; -}; - -// Helper for testing mutex behavior when locked by another thread -void * lockFromAnotherThread(void *ptr) -{ - struct LFAT *lfat = (LFAT *)ptr; - BOOST_CHECK_EQUAL (0, pthread_mutex_lock(&lfat->mx)); // synchronize with testing thread - BOOST_CHECK_NO_THROW( lfat->uut.lock()); - BOOST_CHECK_EQUAL (0, pthread_cond_signal(&lfat->cv)); // tell testing thread we have locked the mutex - BOOST_CHECK_EQUAL (0, pthread_cond_wait(&lfat->cv, &lfat->mx)); // wait for testing thread to signal condition variable telling us to unlock - BOOST_CHECK_NO_THROW( lfat->uut.unlock()); - return ptr; // testing thread should join to ensure completeness -} - -BOOST_AUTO_TEST_SUITE(MutexTest) - -BOOST_AUTO_TEST_CASE(happy_path) -{ - Mutex uut(Mutex::ERRORCHECK_INITIALIZER); // needed to test unlocking twice without undefined behavior - - BOOST_CHECK_NO_THROW( uut.lock()); - BOOST_CHECK_THROW ( uut.lock(), SystemResourceException); // EDEADLK (this thread owns it) - BOOST_CHECK_NO_THROW( uut.unlock()); -} - -BOOST_AUTO_TEST_CASE(recursive_happy_path) -{ - Mutex uut(Mutex::RECURSIVE_INITIALIZER); - - BOOST_CHECK_NO_THROW( uut.lock()); - BOOST_CHECK_NO_THROW( uut.lock()); - BOOST_CHECK_NO_THROW( uut.unlock()); - BOOST_CHECK_NO_THROW( uut.lock()); - BOOST_CHECK_NO_THROW( uut.lock()); - BOOST_CHECK_NO_THROW( uut.unlock()); - BOOST_CHECK_NO_THROW( uut.lock()); - BOOST_CHECK_NO_THROW( uut.unlock()); - BOOST_CHECK_NO_THROW( uut.unlock()); - BOOST_CHECK_NO_THROW( uut.unlock()); -} - -BOOST_AUTO_TEST_CASE(trylock) -{ - Mutex uut(Mutex::ADAPTIVE_INITIALIZER); // just using another initializer for coverage - - BOOST_CHECK ( uut.trylock()); - BOOST_CHECK (!uut.trylock()); - BOOST_CHECK_NO_THROW( uut.unlock()); -} - -BOOST_AUTO_TEST_CASE(timedlock) -{ - pthread_t th; - struct LFAT lfat; - - BOOST_CHECK ( lfat.uut.timedlock(100)); - BOOST_CHECK_THROW ( lfat.uut.timedlock(100), - SystemResourceException); // EDEADLK (current thread owns mutex - logic error) - BOOST_CHECK_NO_THROW( lfat.uut.unlock()); - - BOOST_CHECK_EQUAL (0, pthread_mutex_lock(&lfat.mx)); // synchronize with helper thread - BOOST_CHECK_EQUAL (0, pthread_create(&th, NULL, - lockFromAnotherThread, &lfat)); // create helper thread - BOOST_CHECK_EQUAL (0, pthread_cond_wait(&lfat.cv, &lfat.mx)); // wait for helper thread to lock mutex - - BOOST_CHECK (!lfat.uut.timedlock(100)); // false: another thread owns the lock - - BOOST_CHECK_EQUAL (0, pthread_cond_signal(&lfat.cv)); // tell helper thread we are done - BOOST_CHECK_EQUAL (0, pthread_mutex_unlock(&lfat.mx)); // let helper thread clean up - BOOST_CHECK_EQUAL (0, pthread_join(th, 0)); // wait for testing thread to unlock and be done -} - -BOOST_AUTO_TEST_CASE(underlying) -{ - Mutex uut; - - BOOST_CHECK ( uut.getUnderlyingImpl()); -} - -BOOST_AUTO_TEST_SUITE_END() diff --git a/lib/cpp/test/concurrency/RWMutexStarveTest.cpp b/lib/cpp/test/concurrency/RWMutexStarveTest.cpp deleted file mode 100644 index 849e078bd2a..00000000000 --- a/lib/cpp/test/concurrency/RWMutexStarveTest.cpp +++ /dev/null @@ -1,157 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -// This is linked into the UnitTests test executable - -#include - -#include "thrift/concurrency/Mutex.h" -#include "thrift/concurrency/PosixThreadFactory.h" -#include - -using apache::thrift::stdcxx::shared_ptr; -using boost::unit_test::test_suite; -using boost::unit_test::framework::master_test_suite; - -using namespace apache::thrift::concurrency; - -class Locker : public Runnable { -protected: - Locker(shared_ptr rwlock, bool writer) - : rwlock_(rwlock), writer_(writer), started_(false), gotLock_(false), signaled_(false) {} - -public: - virtual void run() { - started_ = true; - if (writer_) { - rwlock_->acquireWrite(); - } else { - rwlock_->acquireRead(); - } - gotLock_ = true; - while (!signaled_) { - usleep(5000); - } - rwlock_->release(); - } - - bool started() const { return started_; } - bool gotLock() const { return gotLock_; } - void signal() { signaled_ = true; } - -protected: - shared_ptr rwlock_; - bool writer_; - volatile bool started_; - volatile bool gotLock_; - volatile bool signaled_; -}; - -class Reader : public Locker { -public: - Reader(shared_ptr rwlock) : Locker(rwlock, false) {} -}; - -class Writer : public Locker { -public: - Writer(shared_ptr rwlock) : Locker(rwlock, true) {} -}; - -void test_starve(PosixThreadFactory::POLICY policy) { - // the man pages for pthread_wrlock_rdlock suggest that any OS guarantee about - // writer starvation may be influenced by the scheduling policy, so let's try - // all 3 policies to see if any of them work. - PosixThreadFactory factory(policy); - factory.setDetached(false); - - shared_ptr rwlock(new NoStarveReadWriteMutex()); - - shared_ptr reader1(new Reader(rwlock)); - shared_ptr reader2(new Reader(rwlock)); - shared_ptr writer(new Writer(rwlock)); - - shared_ptr treader1 = factory.newThread(reader1); - shared_ptr treader2 = factory.newThread(reader2); - shared_ptr twriter = factory.newThread(writer); - - // launch a reader and make sure he has the lock - treader1->start(); - while (!reader1->gotLock()) { - usleep(2000); - } - - // launch a writer and make sure he's blocked on the lock - twriter->start(); - while (!writer->started()) { - usleep(2000); - } - // tricky part... we can never be 100% sure that the writer is actually - // blocked on the lock, but we can pretty reasonably sure because we know - // he just executed the line immediately before getting the lock, and - // we'll wait a full second for him to get on it. - sleep(1); - - // launch a second reader... if the RWMutex guarantees that writers won't - // starve, this reader should not be able to acquire the lock until the writer - // has acquired and released it. - treader2->start(); - while (!reader2->started()) { - usleep(2000); - } - // again... can't be 100% sure the reader is waiting on (or has) the lock - // but we can be close. - sleep(1); - - // tell reader 1 to let go of the lock - reader1->signal(); - - // wait for someone to get the lock - while (!reader2->gotLock() && !writer->gotLock()) { - usleep(2000); - } - - // the test succeeded if the WRITER got the lock. - bool success = writer->gotLock(); - - // tell everyone we're done and wait for them to finish - reader2->signal(); - writer->signal(); - treader1->join(); - treader2->join(); - twriter->join(); - - // make sure it worked. - BOOST_CHECK_MESSAGE(success, "writer is starving"); -} - -BOOST_AUTO_TEST_SUITE(RWMutexStarveTest) - -BOOST_AUTO_TEST_CASE(test_starve_other) { - test_starve(PosixThreadFactory::OTHER); -} - -BOOST_AUTO_TEST_CASE(test_starve_rr) { - test_starve(PosixThreadFactory::ROUND_ROBIN); -} - -BOOST_AUTO_TEST_CASE(test_starve_fifo) { - test_starve(PosixThreadFactory::FIFO); -} - -BOOST_AUTO_TEST_SUITE_END() diff --git a/lib/cpp/test/concurrency/Tests.cpp b/lib/cpp/test/concurrency/Tests.cpp index fc0ba7f1567..8c734c2d5af 100644 --- a/lib/cpp/test/concurrency/Tests.cpp +++ b/lib/cpp/test/concurrency/Tests.cpp @@ -31,8 +31,6 @@ static int WEIGHT = 10; int main(int argc, char** argv) { - std::string arg; - std::vector args(argc - 1 > 1 ? argc - 1 : 1); args[0] = "all"; @@ -41,7 +39,7 @@ int main(int argc, char** argv) { args[ix - 1] = std::string(argv[ix]); } - if (getenv("VALGRIND") != 0) { + if (getenv("VALGRIND") != nullptr) { // lower the scale of every test WEIGHT = 1; } @@ -94,18 +92,18 @@ int main(int argc, char** argv) { std::cout << "\t\tUtil minimum time" << std::endl; - int64_t time00 = Util::currentTime(); - int64_t time01 = Util::currentTime(); + int64_t time00 = std::chrono::duration_cast(std::chrono::steady_clock::now().time_since_epoch()).count(); + int64_t time01 = std::chrono::duration_cast(std::chrono::steady_clock::now().time_since_epoch()).count(); std::cout << "\t\t\tMinimum time: " << time01 - time00 << "ms" << std::endl; - time00 = Util::currentTime(); + time00 = std::chrono::duration_cast(std::chrono::steady_clock::now().time_since_epoch()).count(); time01 = time00; size_t count = 0; while (time01 < time00 + 10) { count++; - time01 = Util::currentTime(); + time01 = std::chrono::duration_cast(std::chrono::steady_clock::now().time_since_epoch()).count(); } std::cout << "\t\t\tscall per ms: " << count / (time01 - time00) << std::endl; diff --git a/lib/cpp/test/concurrency/ThreadFactoryTests.h b/lib/cpp/test/concurrency/ThreadFactoryTests.h index 48330f374c0..23e46e3de21 100644 --- a/lib/cpp/test/concurrency/ThreadFactoryTests.h +++ b/lib/cpp/test/concurrency/ThreadFactoryTests.h @@ -19,10 +19,9 @@ #include #include -#include +#include #include #include -#include #include #include @@ -33,7 +32,7 @@ namespace thrift { namespace concurrency { namespace test { -using stdcxx::shared_ptr; +using std::shared_ptr; using namespace apache::thrift::concurrency; /** @@ -52,7 +51,7 @@ class ThreadFactoryTests { public: ReapNTask(Monitor& monitor, int& activeCount) : _monitor(monitor), _count(activeCount) {} - void run() { + void run() override { Synchronized s(_monitor); if (--_count == 0) { @@ -66,7 +65,7 @@ class ThreadFactoryTests { bool reapNThreads(int loop = 1, int count = 10) { - PlatformThreadFactory threadFactory = PlatformThreadFactory(); + ThreadFactory threadFactory = ThreadFactory(); shared_ptr monitor(new Monitor); for (int lix = 0; lix < loop; lix++) { @@ -84,7 +83,7 @@ class ThreadFactoryTests { } catch (SystemResourceException& e) { std::cout << "\t\t\tfailed to create " << lix* count + tix << " thread " << e.what() << std::endl; - throw e; + throw; } } @@ -98,7 +97,7 @@ class ThreadFactoryTests { } catch (SystemResourceException& e) { std::cout << "\t\t\tfailed to start " << lix* count + tix << " thread " << e.what() << std::endl; - throw e; + throw; } } @@ -123,7 +122,7 @@ class ThreadFactoryTests { SynchStartTask(Monitor& monitor, volatile STATE& state) : _monitor(monitor), _state(state) {} - void run() { + void run() override { { Synchronized s(_monitor); if (_state == SynchStartTask::STARTING) { @@ -159,7 +158,7 @@ class ThreadFactoryTests { shared_ptr task = shared_ptr(new SynchStartTask(monitor, state)); - PlatformThreadFactory threadFactory = PlatformThreadFactory(); + ThreadFactory threadFactory = ThreadFactory(); shared_ptr thread = threadFactory.newThread(task); @@ -221,7 +220,7 @@ class ThreadFactoryTests { Monitor monitor; - int64_t startTime = Util::currentTime(); + int64_t startTime = std::chrono::duration_cast(std::chrono::steady_clock::now().time_since_epoch()).count(); for (int64_t ix = 0; ix < count; ix++) { { @@ -233,7 +232,7 @@ class ThreadFactoryTests { } } - int64_t endTime = Util::currentTime(); + int64_t endTime = std::chrono::duration_cast(std::chrono::steady_clock::now().time_since_epoch()).count(); bool success = (endTime - startTime) >= (count * timeout); @@ -248,14 +247,14 @@ class ThreadFactoryTests { class FloodTask : public Runnable { public: FloodTask(const size_t id, Monitor& mon) : _id(id), _mon(mon) {} - ~FloodTask() { + ~FloodTask() override { if (_id % 10000 == 0) { Synchronized sync(_mon); std::cout << "\t\tthread " << _id << " done" << std::endl; } } - void run() { + void run() override { if (_id % 10000 == 0) { Synchronized sync(_mon); std::cout << "\t\tthread " << _id << " started" << std::endl; @@ -265,7 +264,7 @@ class ThreadFactoryTests { Monitor& _mon; }; - void foo(PlatformThreadFactory* tf) { (void)tf; } + void foo(ThreadFactory* tf) { (void)tf; } bool floodNTest(size_t loop = 1, size_t count = 100000) { @@ -274,7 +273,7 @@ class ThreadFactoryTests { for (size_t lix = 0; lix < loop; lix++) { - PlatformThreadFactory threadFactory = PlatformThreadFactory(); + ThreadFactory threadFactory = ThreadFactory(); threadFactory.setDetached(true); for (size_t tix = 0; tix < count; tix++) { diff --git a/lib/cpp/test/concurrency/ThreadManagerTests.h b/lib/cpp/test/concurrency/ThreadManagerTests.h index 9ecd6bad5a2..fee7c7c51a1 100644 --- a/lib/cpp/test/concurrency/ThreadManagerTests.h +++ b/lib/cpp/test/concurrency/ThreadManagerTests.h @@ -19,9 +19,8 @@ #include #include -#include +#include #include -#include #include #include @@ -36,8 +35,8 @@ namespace test { using namespace apache::thrift::concurrency; -static std::deque > m_expired; -static void expiredNotifier(stdcxx::shared_ptr runnable) +static std::deque > m_expired; +static void expiredNotifier(std::shared_ptr runnable) { m_expired.push_back(runnable); } @@ -64,13 +63,13 @@ class ThreadManagerTests { Task(Monitor& monitor, size_t& count, int64_t timeout) : _monitor(monitor), _count(count), _timeout(timeout), _startTime(0), _endTime(0), _done(false) {} - void run() { + void run() override { - _startTime = Util::currentTime(); + _startTime = std::chrono::duration_cast(std::chrono::steady_clock::now().time_since_epoch()).count(); sleep_(_timeout); - _endTime = Util::currentTime(); + _endTime = std::chrono::duration_cast(std::chrono::steady_clock::now().time_since_epoch()).count(); _done = true; @@ -108,12 +107,9 @@ class ThreadManagerTests { shared_ptr threadManager = ThreadManager::newSimpleThreadManager(workerCount); - shared_ptr threadFactory - = shared_ptr(new PlatformThreadFactory(false)); + shared_ptr threadFactory + = shared_ptr(new ThreadFactory(false)); -#if !USE_BOOST_THREAD && !USE_STD_THREAD - threadFactory->setPriority(PosixThreadFactory::HIGHEST); -#endif threadManager->threadFactory(threadFactory); threadManager->start(); @@ -126,9 +122,9 @@ class ThreadManagerTests { new ThreadManagerTests::Task(monitor, activeCount, timeout))); } - int64_t time00 = Util::currentTime(); + int64_t time00 = std::chrono::duration_cast(std::chrono::steady_clock::now().time_since_epoch()).count(); - for (std::set >::iterator ix = tasks.begin(); + for (auto ix = tasks.begin(); ix != tasks.end(); ix++) { @@ -146,7 +142,7 @@ class ThreadManagerTests { } } - int64_t time01 = Util::currentTime(); + int64_t time01 = std::chrono::duration_cast(std::chrono::steady_clock::now().time_since_epoch()).count(); int64_t firstTime = 9223372036854775807LL; int64_t lastTime = 0; @@ -155,7 +151,7 @@ class ThreadManagerTests { int64_t minTime = 9223372036854775807LL; int64_t maxTime = 0; - for (std::set >::iterator ix = tasks.begin(); + for (auto ix = tasks.begin(); ix != tasks.end(); ix++) { @@ -205,7 +201,7 @@ class ThreadManagerTests { 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() { + void run() override { { Synchronized s(_entryMonitor); _entered = true; @@ -257,12 +253,9 @@ class ThreadManagerTests { shared_ptr threadManager = ThreadManager::newSimpleThreadManager(workerCount, pendingTaskMaxCount); - shared_ptr threadFactory - = shared_ptr(new PlatformThreadFactory()); + shared_ptr threadFactory + = shared_ptr(new ThreadFactory()); -#if !USE_BOOST_THREAD && !USE_STD_THREAD - threadFactory->setPriority(PosixThreadFactory::HIGHEST); -#endif threadManager->threadFactory(threadFactory); threadManager->start(); @@ -282,7 +275,7 @@ class ThreadManagerTests { new ThreadManagerTests::BlockTask(entryMonitor, blockMonitor, blocked[1], doneMonitor, activeCounts[1]))); } - for (std::vector >::iterator ix = tasks.begin(); + for (auto ix = tasks.begin(); ix != tasks.end(); ix++) { threadManager->add(*ix); @@ -393,66 +386,27 @@ class ThreadManagerTests { bool apiTest() { // prove currentTime has milliseconds granularity since many other things depend on it - int64_t a = Util::currentTime(); + int64_t a = std::chrono::duration_cast(std::chrono::steady_clock::now().time_since_epoch()).count(); sleep_(100); - int64_t b = Util::currentTime(); + int64_t b = std::chrono::duration_cast(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; } -#if !USE_BOOST_THREAD && !USE_STD_THREAD - // test once with a detached thread factory and once with a joinable thread factory - - shared_ptr threadFactory - = shared_ptr(new PosixThreadFactory(false)); - - std::cout << "\t\t\tapiTest with joinable thread factory" << std::endl; - if (!apiTestWithThreadFactory(threadFactory)) { - return false; - } - - threadFactory.reset(new PosixThreadFactory(true)); - std::cout << "\t\t\tapiTest with detached thread factory" << std::endl; - return apiTestWithThreadFactory(threadFactory); -#else - return apiTestWithThreadFactory(shared_ptr(new PlatformThreadFactory())); -#endif + return apiTestWithThreadFactory(shared_ptr(new ThreadFactory())); } - bool apiTestWithThreadFactory(shared_ptr threadFactory) + bool apiTestWithThreadFactory(shared_ptr threadFactory) { shared_ptr threadManager = ThreadManager::newSimpleThreadManager(1); threadManager->threadFactory(threadFactory); -#if !USE_BOOST_THREAD && !USE_STD_THREAD - threadFactory->setPriority(PosixThreadFactory::HIGHEST); - - // verify we cannot change the thread factory to one with the opposite detached setting - shared_ptr threadFactory2 - = shared_ptr(new PlatformThreadFactory( - PosixThreadFactory::ROUND_ROBIN, - PosixThreadFactory::NORMAL, - 1, - !threadFactory->isDetached())); - try { - threadManager->threadFactory(threadFactory2); - // if the call succeeded we changed the thread factory to one that had the opposite setting for "isDetached()". - // this is bad, because the thread manager checks with the thread factory to see if it should join threads - // as they are leaving - so the detached status of new threads cannot change while there are existing threads. - std::cerr << "\t\t\tShould not be able to change thread factory detached disposition" << std::endl; - return false; - } - catch (InvalidArgumentException& ex) { - /* expected */ - } -#endif - std::cout << "\t\t\t\tstarting.. " << std::endl; threadManager->start(); - threadManager->setExpireCallback(expiredNotifier); // apache::thrift::stdcxx::bind(&ThreadManagerTests::expiredNotifier, this)); + 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; } } diff --git a/lib/cpp/test/concurrency/TimerManagerTests.h b/lib/cpp/test/concurrency/TimerManagerTests.h index 1c52c470b54..2d1a2620ab5 100644 --- a/lib/cpp/test/concurrency/TimerManagerTests.h +++ b/lib/cpp/test/concurrency/TimerManagerTests.h @@ -18,11 +18,12 @@ */ #include -#include +#include #include -#include #include +#include +#include #include namespace apache { @@ -37,19 +38,19 @@ class TimerManagerTests { public: class Task : public Runnable { public: - Task(Monitor& monitor, int64_t timeout) + Task(Monitor& monitor, uint64_t timeout) : _timeout(timeout), - _startTime(Util::currentTime()), + _startTime(std::chrono::duration_cast(std::chrono::steady_clock::now().time_since_epoch()).count()), _endTime(0), _monitor(monitor), _success(false), _done(false) {} - ~Task() { std::cerr << this << std::endl; } + ~Task() override { std::cerr << this << std::endl; } - void run() { + void run() override { - _endTime = Util::currentTime(); + _endTime = std::chrono::duration_cast(std::chrono::steady_clock::now().time_since_epoch()).count(); _success = (_endTime - _startTime) >= _timeout; { @@ -73,14 +74,14 @@ class TimerManagerTests { * properly clean up itself and the remaining orphaned timeout task when the * manager goes out of scope and its destructor is called. */ - bool test00(int64_t timeout = 1000LL) { + bool test00(uint64_t timeout = 1000LL) { shared_ptr orphanTask = shared_ptr(new TimerManagerTests::Task(_monitor, 10 * timeout)); { TimerManager timerManager; - timerManager.threadFactory(shared_ptr(new PlatformThreadFactory())); + timerManager.threadFactory(shared_ptr(new ThreadFactory())); timerManager.start(); if (timerManager.state() != TimerManager::STARTED) { std::cerr << "timerManager is not in the STARTED state, but should be" << std::endl; @@ -95,7 +96,7 @@ class TimerManagerTests { Synchronized s(_monitor); timerManager.add(orphanTask, 10 * timeout); - THRIFT_SLEEP_USEC(timeout * 1000); + std::this_thread::sleep_for(std::chrono::milliseconds(timeout)); task.reset(new TimerManagerTests::Task(_monitor, timeout)); timerManager.add(task, timeout); @@ -123,9 +124,9 @@ class TimerManagerTests { * 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(int64_t timeout = 1000LL) { + bool test01(uint64_t timeout = 1000LL) { TimerManager timerManager; - timerManager.threadFactory(shared_ptr(new PlatformThreadFactory())); + timerManager.threadFactory(shared_ptr(new ThreadFactory())); timerManager.start(); assert(timerManager.state() == TimerManager::STARTED); @@ -156,9 +157,9 @@ class TimerManagerTests { * clean up itself and the remaining orphaned timeout task when the manager goes out of scope * and its destructor is called. */ - bool test02(int64_t timeout = 1000LL) { + bool test02(uint64_t timeout = 1000LL) { TimerManager timerManager; - timerManager.threadFactory(shared_ptr(new PlatformThreadFactory())); + timerManager.threadFactory(shared_ptr(new ThreadFactory())); timerManager.start(); assert(timerManager.state() == TimerManager::STARTED); @@ -189,9 +190,9 @@ class TimerManagerTests { * 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(int64_t timeout = 1000LL) { + bool test03(uint64_t timeout = 1000LL) { TimerManager timerManager; - timerManager.threadFactory(shared_ptr(new PlatformThreadFactory())); + timerManager.threadFactory(shared_ptr(new ThreadFactory())); timerManager.start(); assert(timerManager.state() == TimerManager::STARTED); @@ -216,7 +217,7 @@ class TimerManagerTests { // Verify behavior when removing the removed task try { timerManager.remove(timer); - assert(0 == "ERROR: This remove should send a NoSuchTaskException exception."); + assert(nullptr == "ERROR: This remove should send a NoSuchTaskException exception."); } catch (NoSuchTaskException&) { } @@ -224,11 +225,11 @@ class TimerManagerTests { } /** - * This test creates one tasks, and tries to remove it after it has expired. + * This test creates one task, and tries to remove it after it has expired. */ - bool test04(int64_t timeout = 1000LL) { + bool test04(uint64_t timeout = 1000LL) { TimerManager timerManager; - timerManager.threadFactory(shared_ptr(new PlatformThreadFactory())); + timerManager.threadFactory(shared_ptr(new ThreadFactory())); timerManager.start(); assert(timerManager.state() == TimerManager::STARTED); @@ -238,15 +239,24 @@ class TimerManagerTests { shared_ptr task = shared_ptr(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 - try { - timerManager.remove(timer); - assert(0 == "ERROR: This remove should send a NoSuchTaskException exception."); - } catch (NoSuchTaskException&) { + // 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; diff --git a/lib/cpp/test/processor/EventLog.cpp b/lib/cpp/test/processor/EventLog.cpp index e3ddbccc41b..c75955d277e 100644 --- a/lib/cpp/test/processor/EventLog.cpp +++ b/lib/cpp/test/processor/EventLog.cpp @@ -98,7 +98,7 @@ Event EventLog::waitForEvent(int64_t timeout) { while (events_.empty()) { monitor_.wait(timeout); } - } catch (TimedOutException ex) { + } catch (const TimedOutException &) { return Event(ET_LOG_END, 0, 0, ""); } @@ -110,7 +110,7 @@ Event EventLog::waitForEvent(int64_t timeout) { Event EventLog::waitForConnEvent(uint32_t connId, int64_t timeout) { Synchronized s(monitor_); - EventList::iterator it = events_.begin(); + auto it = events_.begin(); while (true) { try { // TODO: it would be nicer to honor timeout for the duration of this @@ -119,7 +119,7 @@ Event EventLog::waitForConnEvent(uint32_t connId, int64_t timeout) { while (it == events_.end()) { monitor_.wait(timeout); } - } catch (TimedOutException ex) { + } catch (const TimedOutException &) { return Event(ET_LOG_END, 0, 0, ""); } diff --git a/lib/cpp/test/processor/Handlers.h b/lib/cpp/test/processor/Handlers.h index ad47229ad05..d72a23c9072 100644 --- a/lib/cpp/test/processor/Handlers.h +++ b/lib/cpp/test/processor/Handlers.h @@ -29,34 +29,34 @@ namespace test { class ParentHandler : virtual public ParentServiceIf { public: - ParentHandler(const stdcxx::shared_ptr& log) + ParentHandler(const std::shared_ptr& log) : triggerMonitor(&mutex_), generation_(0), wait_(false), log_(log) {} - int32_t incrementGeneration() { + int32_t incrementGeneration() override { concurrency::Guard g(mutex_); log_->append(EventLog::ET_CALL_INCREMENT_GENERATION, 0, 0); return ++generation_; } - int32_t getGeneration() { + 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) { + 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& _return) { + void getStrings(std::vector& _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) { + void getDataWait(std::string& _return, const int32_t length) override { concurrency::Guard g(mutex_); log_->append(EventLog::ET_CALL_GET_DATA_WAIT, 0, 0); @@ -65,14 +65,14 @@ class ParentHandler : virtual public ParentServiceIf { _return.append(length, 'a'); } - void onewayWait() { + void onewayWait() override { concurrency::Guard g(mutex_); log_->append(EventLog::ET_CALL_ONEWAY_WAIT, 0, 0); blockUntilTriggered(); } - void exceptionWait(const std::string& message) { + void exceptionWait(const std::string& message) override { concurrency::Guard g(mutex_); log_->append(EventLog::ET_CALL_EXCEPTION_WAIT, 0, 0); @@ -83,7 +83,7 @@ class ParentHandler : virtual public ParentServiceIf { throw e; } - void unexpectedExceptionWait(const std::string& message) { + void unexpectedExceptionWait(const std::string& message) override { concurrency::Guard g(mutex_); log_->append(EventLog::ET_CALL_UNEXPECTED_EXCEPTION_WAIT, 0, 0); @@ -136,19 +136,19 @@ class ParentHandler : virtual public ParentServiceIf { int32_t generation_; bool wait_; std::vector strings_; - stdcxx::shared_ptr log_; + std::shared_ptr log_; }; -#ifdef _WIN32 +#ifdef _MSC_VER #pragma warning( push ) #pragma warning (disable : 4250 ) //inheriting methods via dominance #endif class ChildHandler : public ParentHandler, virtual public ChildServiceIf { public: - ChildHandler(const stdcxx::shared_ptr& log) : ParentHandler(log), value_(0) {} + ChildHandler(const std::shared_ptr& log) : ParentHandler(log), value_(0) {} - int32_t setValue(const int32_t value) { + int32_t setValue(const int32_t value) override { concurrency::Guard g(mutex_); log_->append(EventLog::ET_CALL_SET_VALUE, 0, 0); @@ -157,7 +157,7 @@ class ChildHandler : public ParentHandler, virtual public ChildServiceIf { return oldValue; } - int32_t getValue() { + int32_t getValue() override { concurrency::Guard g(mutex_); log_->append(EventLog::ET_CALL_GET_VALUE, 0, 0); @@ -168,19 +168,19 @@ class ChildHandler : public ParentHandler, virtual public ChildServiceIf { int32_t value_; }; -#ifdef _WIN32 +#ifdef _MSC_VER #pragma warning( pop ) #endif struct ConnContext { public: - ConnContext(stdcxx::shared_ptr in, - stdcxx::shared_ptr out, + ConnContext(std::shared_ptr in, + std::shared_ptr out, uint32_t id) : input(in), output(out), id(id) {} - stdcxx::shared_ptr input; - stdcxx::shared_ptr output; + std::shared_ptr input; + std::shared_ptr output; uint32_t id; }; @@ -196,22 +196,22 @@ struct CallContext { class ServerEventHandler : public server::TServerEventHandler { public: - ServerEventHandler(const stdcxx::shared_ptr& log) : nextId_(1), log_(log) {} + ServerEventHandler(const std::shared_ptr& log) : nextId_(1), log_(log) {} - virtual void preServe() {} + void preServe() override {} - virtual void* createContext(stdcxx::shared_ptr input, - stdcxx::shared_ptr output) { + void* createContext(std::shared_ptr input, + std::shared_ptr output) override { ConnContext* context = new ConnContext(input, output, nextId_); ++nextId_; log_->append(EventLog::ET_CONN_CREATED, context->id, 0); return context; } - virtual void deleteContext(void* serverContext, - stdcxx::shared_ptr input, - stdcxx::shared_ptr output) { - ConnContext* context = reinterpret_cast(serverContext); + void deleteContext(void* serverContext, + std::shared_ptr input, + std::shared_ptr output) override { + auto* context = reinterpret_cast(serverContext); if (input != context->input) { abort(); @@ -225,8 +225,8 @@ class ServerEventHandler : public server::TServerEventHandler { delete context; } - virtual void processContext(void* serverContext, - stdcxx::shared_ptr transport) { + void processContext(void* serverContext, + std::shared_ptr 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 @@ -251,15 +251,15 @@ class ServerEventHandler : public server::TServerEventHandler { protected: uint32_t nextId_; - stdcxx::shared_ptr log_; + std::shared_ptr log_; }; class ProcessorEventHandler : public TProcessorEventHandler { public: - ProcessorEventHandler(const stdcxx::shared_ptr& log) : nextId_(1), log_(log) {} + ProcessorEventHandler(const std::shared_ptr& log) : nextId_(1), log_(log) {} - void* getContext(const char* fnName, void* serverContext) { - ConnContext* connContext = reinterpret_cast(serverContext); + void* getContext(const char* fnName, void* serverContext) override { + auto* connContext = reinterpret_cast(serverContext); CallContext* context = new CallContext(connContext, nextId_, fnName); ++nextId_; @@ -268,47 +268,47 @@ class ProcessorEventHandler : public TProcessorEventHandler { return context; } - void freeContext(void* ctx, const char* fnName) { - CallContext* context = reinterpret_cast(ctx); + void freeContext(void* ctx, const char* fnName) override { + auto* context = reinterpret_cast(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) { - CallContext* context = reinterpret_cast(ctx); + void preRead(void* ctx, const char* fnName) override { + auto* context = reinterpret_cast(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) { + void postRead(void* ctx, const char* fnName, uint32_t bytes) override { THRIFT_UNUSED_VARIABLE(bytes); - CallContext* context = reinterpret_cast(ctx); + auto* context = reinterpret_cast(ctx); checkName(context, fnName); log_->append(EventLog::ET_POST_READ, context->connContext->id, context->id, fnName); } - void preWrite(void* ctx, const char* fnName) { - CallContext* context = reinterpret_cast(ctx); + void preWrite(void* ctx, const char* fnName) override { + auto* context = reinterpret_cast(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) { + void postWrite(void* ctx, const char* fnName, uint32_t bytes) override { THRIFT_UNUSED_VARIABLE(bytes); - CallContext* context = reinterpret_cast(ctx); + auto* context = reinterpret_cast(ctx); checkName(context, fnName); log_->append(EventLog::ET_POST_WRITE, context->connContext->id, context->id, fnName); } - void asyncComplete(void* ctx, const char* fnName) { - CallContext* context = reinterpret_cast(ctx); + void asyncComplete(void* ctx, const char* fnName) override { + auto* context = reinterpret_cast(ctx); checkName(context, fnName); log_->append(EventLog::ET_ASYNC_COMPLETE, context->connContext->id, context->id, fnName); } - void handlerError(void* ctx, const char* fnName) { - CallContext* context = reinterpret_cast(ctx); + void handlerError(void* ctx, const char* fnName) override { + auto* context = reinterpret_cast(ctx); checkName(context, fnName); log_->append(EventLog::ET_HANDLER_ERROR, context->connContext->id, context->id, fnName); } @@ -329,7 +329,7 @@ class ProcessorEventHandler : public TProcessorEventHandler { } uint32_t nextId_; - stdcxx::shared_ptr log_; + std::shared_ptr log_; }; } } diff --git a/lib/cpp/test/processor/ProcessorTest.cpp b/lib/cpp/test/processor/ProcessorTest.cpp index c9e186face9..017fa89ea9e 100644 --- a/lib/cpp/test/processor/ProcessorTest.cpp +++ b/lib/cpp/test/processor/ProcessorTest.cpp @@ -25,7 +25,7 @@ #include -#include +#include #include #include #include @@ -57,13 +57,13 @@ class TSimpleServerTraits { public: typedef TSimpleServer ServerType; - stdcxx::shared_ptr createServer( - const stdcxx::shared_ptr& processor, + std::shared_ptr createServer( + const std::shared_ptr& processor, uint16_t port, - const stdcxx::shared_ptr& transportFactory, - const stdcxx::shared_ptr& protocolFactory) { - stdcxx::shared_ptr socket(new TServerSocket(port)); - return stdcxx::shared_ptr( + const std::shared_ptr& transportFactory, + const std::shared_ptr& protocolFactory) { + std::shared_ptr socket(new TServerSocket(port)); + return std::shared_ptr( new TSimpleServer(processor, socket, transportFactory, protocolFactory)); } }; @@ -72,13 +72,13 @@ class TThreadedServerTraits { public: typedef TThreadedServer ServerType; - stdcxx::shared_ptr createServer( - const stdcxx::shared_ptr& processor, + std::shared_ptr createServer( + const std::shared_ptr& processor, uint16_t port, - const stdcxx::shared_ptr& transportFactory, - const stdcxx::shared_ptr& protocolFactory) { - stdcxx::shared_ptr socket(new TServerSocket(port)); - return stdcxx::shared_ptr( + const std::shared_ptr& transportFactory, + const std::shared_ptr& protocolFactory) { + std::shared_ptr socket(new TServerSocket(port)); + return std::shared_ptr( new TThreadedServer(processor, socket, transportFactory, protocolFactory)); } }; @@ -87,19 +87,19 @@ class TThreadPoolServerTraits { public: typedef TThreadPoolServer ServerType; - stdcxx::shared_ptr createServer( - const stdcxx::shared_ptr& processor, + std::shared_ptr createServer( + const std::shared_ptr& processor, uint16_t port, - const stdcxx::shared_ptr& transportFactory, - const stdcxx::shared_ptr& protocolFactory) { - stdcxx::shared_ptr socket(new TServerSocket(port)); + const std::shared_ptr& transportFactory, + const std::shared_ptr& protocolFactory) { + std::shared_ptr socket(new TServerSocket(port)); - stdcxx::shared_ptr threadFactory(new PlatformThreadFactory); - stdcxx::shared_ptr threadManager = ThreadManager::newSimpleThreadManager(8); + std::shared_ptr threadFactory(new ThreadFactory); + std::shared_ptr threadManager = ThreadManager::newSimpleThreadManager(8); threadManager->threadFactory(threadFactory); threadManager->start(); - return stdcxx::shared_ptr( + return std::shared_ptr( new TThreadPoolServer(processor, socket, transportFactory, protocolFactory, threadManager)); } }; @@ -108,27 +108,27 @@ class TNonblockingServerTraits { public: typedef TNonblockingServer ServerType; - stdcxx::shared_ptr createServer( - const stdcxx::shared_ptr& processor, + std::shared_ptr createServer( + const std::shared_ptr& processor, uint16_t port, - const stdcxx::shared_ptr& transportFactory, - const stdcxx::shared_ptr& protocolFactory) { + const std::shared_ptr& transportFactory, + const std::shared_ptr& protocolFactory) { // TNonblockingServer automatically uses TFramedTransport. // Raise an exception if the supplied transport factory is not a // TFramedTransportFactory - TFramedTransportFactory* framedFactory + auto* framedFactory = dynamic_cast(transportFactory.get()); - if (framedFactory == NULL) { + if (framedFactory == nullptr) { throw TException("TNonblockingServer must use TFramedTransport"); } - stdcxx::shared_ptr socket(new TNonblockingServerSocket(port)); - stdcxx::shared_ptr threadFactory(new PlatformThreadFactory); - stdcxx::shared_ptr threadManager = ThreadManager::newSimpleThreadManager(8); + std::shared_ptr socket(new TNonblockingServerSocket(port)); + std::shared_ptr threadFactory(new ThreadFactory); + std::shared_ptr threadManager = ThreadManager::newSimpleThreadManager(8); threadManager->threadFactory(threadFactory); threadManager->start(); - return stdcxx::shared_ptr( + return std::shared_ptr( new TNonblockingServer(processor, protocolFactory, socket, threadManager)); } }; @@ -137,24 +137,24 @@ class TNonblockingServerNoThreadsTraits { public: typedef TNonblockingServer ServerType; - stdcxx::shared_ptr createServer( - const stdcxx::shared_ptr& processor, + std::shared_ptr createServer( + const std::shared_ptr& processor, uint16_t port, - const stdcxx::shared_ptr& transportFactory, - const stdcxx::shared_ptr& protocolFactory) { + const std::shared_ptr& transportFactory, + const std::shared_ptr& protocolFactory) { // TNonblockingServer automatically uses TFramedTransport. // Raise an exception if the supplied transport factory is not a // TFramedTransportFactory - TFramedTransportFactory* framedFactory + auto* framedFactory = dynamic_cast(transportFactory.get()); - if (framedFactory == NULL) { + if (framedFactory == nullptr) { throw TException("TNonblockingServer must use TFramedTransport"); } - stdcxx::shared_ptr socket(new TNonblockingServerSocket(port)); + std::shared_ptr socket(new TNonblockingServerSocket(port)); // Use a NULL ThreadManager - stdcxx::shared_ptr threadManager; - return stdcxx::shared_ptr( + std::shared_ptr threadManager; + return std::shared_ptr( new TNonblockingServer(processor, protocolFactory, socket, threadManager)); } }; @@ -244,48 +244,48 @@ class ServiceState : public ServerState { processor_->setEventHandler(processorEventHandler_); } - stdcxx::shared_ptr createServer(uint16_t port) { + std::shared_ptr createServer(uint16_t port) override { ServerTraits_ serverTraits; return serverTraits.createServer(processor_, port, transportFactory_, protocolFactory_); } - stdcxx::shared_ptr getServerEventHandler() { return serverEventHandler_; } + std::shared_ptr getServerEventHandler() override { return serverEventHandler_; } - void bindSuccessful(uint16_t port) { port_ = port; } + void bindSuccessful(uint16_t port) override { port_ = port; } uint16_t getPort() const { return port_; } - const stdcxx::shared_ptr& getLog() const { return log_; } + const std::shared_ptr& getLog() const { return log_; } - const stdcxx::shared_ptr& getHandler() const { return handler_; } + const std::shared_ptr& getHandler() const { return handler_; } - stdcxx::shared_ptr createClient() { + std::shared_ptr createClient() { typedef typename ServiceTraits_::Protocol Protocol; - stdcxx::shared_ptr socket(new TSocket("127.0.0.1", port_)); - stdcxx::shared_ptr transport(new Transport_(socket)); - stdcxx::shared_ptr protocol(new Protocol(transport)); + std::shared_ptr socket(new TSocket("127.0.0.1", port_)); + std::shared_ptr transport(new Transport_(socket)); + std::shared_ptr protocol(new Protocol(transport)); transport->open(); - stdcxx::shared_ptr client(new Client(protocol)); + std::shared_ptr client(new Client(protocol)); return client; } private: uint16_t port_; - stdcxx::shared_ptr log_; - stdcxx::shared_ptr handler_; - stdcxx::shared_ptr processor_; - stdcxx::shared_ptr transportFactory_; - stdcxx::shared_ptr protocolFactory_; - stdcxx::shared_ptr serverEventHandler_; - stdcxx::shared_ptr processorEventHandler_; + std::shared_ptr log_; + std::shared_ptr handler_; + std::shared_ptr processor_; + std::shared_ptr transportFactory_; + std::shared_ptr protocolFactory_; + std::shared_ptr serverEventHandler_; + std::shared_ptr processorEventHandler_; }; /** * Check that there are no more events in the log */ -void checkNoEvents(const stdcxx::shared_ptr& log) { +void checkNoEvents(const std::shared_ptr& 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 @@ -299,7 +299,7 @@ void checkNoEvents(const stdcxx::shared_ptr& log) { * * Returns the connection ID allocated by the server. */ -uint32_t checkNewConnEvents(const stdcxx::shared_ptr& log) { +uint32_t checkNewConnEvents(const std::shared_ptr& log) { // Check for an ET_CONN_CREATED event Event event = log->waitForEvent(2500); BOOST_CHECK_EQUAL(EventLog::ET_CONN_CREATED, event.type); @@ -314,7 +314,7 @@ uint32_t checkNewConnEvents(const stdcxx::shared_ptr& log) { /** * Check for the events that should be logged when a connection is closed. */ -void checkCloseEvents(const stdcxx::shared_ptr& log, uint32_t connId) { +void checkCloseEvents(const std::shared_ptr& log, uint32_t connId) { // Check for an ET_CONN_DESTROYED event Event event = log->waitForEvent(); BOOST_CHECK_EQUAL(EventLog::ET_CONN_DESTROYED, event.type); @@ -332,7 +332,7 @@ void checkCloseEvents(const stdcxx::shared_ptr& log, uint32_t connId) * * Returns the call ID allocated by the server. */ -uint32_t checkCallHandlerEvents(const stdcxx::shared_ptr& log, +uint32_t checkCallHandlerEvents(const std::shared_ptr& log, uint32_t connId, EventType callType, const string& callName) { @@ -369,7 +369,7 @@ uint32_t checkCallHandlerEvents(const stdcxx::shared_ptr& log, /** * Check for the events that should be after a handler returns. */ -void checkCallPostHandlerEvents(const stdcxx::shared_ptr& log, +void checkCallPostHandlerEvents(const std::shared_ptr& log, uint32_t connId, uint32_t callId, const string& callName) { @@ -409,7 +409,7 @@ void checkCallPostHandlerEvents(const stdcxx::shared_ptr& log, * * Returns the call ID allocated by the server. */ -uint32_t checkCallEvents(const stdcxx::shared_ptr& log, +uint32_t checkCallEvents(const std::shared_ptr& log, uint32_t connId, EventType callType, const string& callName) { @@ -424,8 +424,8 @@ uint32_t checkCallEvents(const stdcxx::shared_ptr& log, */ template -void testParentService(const stdcxx::shared_ptr& state) { - stdcxx::shared_ptr client = state->createClient(); +void testParentService(const std::shared_ptr& state) { + std::shared_ptr client = state->createClient(); int32_t gen = client->getGeneration(); int32_t newGen = client->incrementGeneration(); @@ -446,8 +446,8 @@ void testParentService(const stdcxx::shared_ptr& state) { } template -void testChildService(const stdcxx::shared_ptr& state) { - stdcxx::shared_ptr client = state->createClient(); +void testChildService(const std::shared_ptr& state) { + std::shared_ptr client = state->createClient(); // Test calling some of the parent methids via the a child client int32_t gen = client->getGeneration(); @@ -468,7 +468,7 @@ void testBasicService() { typedef ServiceState > State; // Start the server - stdcxx::shared_ptr state(new State); + std::shared_ptr state(new State); ServerThread serverThread(state, true); testParentService(state); @@ -479,7 +479,7 @@ void testInheritedService() { typedef ServiceState > State; // Start the server - stdcxx::shared_ptr state(new State); + std::shared_ptr state(new State); ServerThread serverThread(state, true); testParentService(state); @@ -502,10 +502,10 @@ void testEventSequencing() { TBufferedTransport> State; // Start the server - stdcxx::shared_ptr state(new State); + std::shared_ptr state(new State); ServerThread serverThread(state, true); - const stdcxx::shared_ptr& log = state->getLog(); + const std::shared_ptr& log = state->getLog(); // Make sure we're at the end of the log checkNoEvents(log); @@ -514,7 +514,7 @@ void testEventSequencing() { // Make sure createContext() is called after a connection has been // established. We open a plain socket instead of creating a client. - stdcxx::shared_ptr socket(new TSocket("127.0.0.1", state->getPort())); + std::shared_ptr socket(new TSocket("127.0.0.1", state->getPort())); socket->open(); // Make sure the proper events occurred after a new connection @@ -524,7 +524,7 @@ void testEventSequencing() { // can test the timing for the preRead() call. string requestName = "getDataWait"; string eventName = "ParentService.getDataWait"; - int32_t seqid = int32_t(time(NULL)); + auto seqid = int32_t(time(nullptr)); TBinaryProtocol protocol(socket); protocol.writeMessageBegin(requestName, T_CALL, seqid); socket->flush(); @@ -635,19 +635,19 @@ void testSeparateConnections() { typedef ServiceState > State; // Start the server - stdcxx::shared_ptr state(new State); + std::shared_ptr state(new State); ServerThread serverThread(state, true); - const stdcxx::shared_ptr& log = state->getLog(); + const std::shared_ptr& log = state->getLog(); // Create a client - stdcxx::shared_ptr client1 = state->createClient(); + std::shared_ptr client1 = state->createClient(); // Make sure the expected events were logged uint32_t client1Id = checkNewConnEvents(log); // Create a second client - stdcxx::shared_ptr client2 = state->createClient(); + std::shared_ptr client2 = state->createClient(); // Make sure the expected events were logged uint32_t client2Id = checkNewConnEvents(log); @@ -683,13 +683,13 @@ void testOnewayCall() { typedef ServiceState > State; // Start the server - stdcxx::shared_ptr state(new State); + std::shared_ptr state(new State); ServerThread serverThread(state, true); - const stdcxx::shared_ptr& log = state->getLog(); + const std::shared_ptr& log = state->getLog(); // Create a client - stdcxx::shared_ptr client = state->createClient(); + std::shared_ptr client = state->createClient(); uint32_t connId = checkNewConnEvents(log); // Make a oneway call @@ -735,13 +735,13 @@ void testExpectedError() { typedef ServiceState > State; // Start the server - stdcxx::shared_ptr state(new State); + std::shared_ptr state(new State); ServerThread serverThread(state, true); - const stdcxx::shared_ptr& log = state->getLog(); + const std::shared_ptr& log = state->getLog(); // Create a client - stdcxx::shared_ptr client = state->createClient(); + std::shared_ptr client = state->createClient(); uint32_t connId = checkNewConnEvents(log); // Send the exceptionWait() call @@ -790,13 +790,13 @@ void testUnexpectedError() { typedef ServiceState > State; // Start the server - stdcxx::shared_ptr state(new State); + std::shared_ptr state(new State); ServerThread serverThread(state, true); - const stdcxx::shared_ptr& log = state->getLog(); + const std::shared_ptr& log = state->getLog(); // Create a client - stdcxx::shared_ptr client = state->createClient(); + std::shared_ptr client = state->createClient(); uint32_t connId = checkNewConnEvents(log); // Send the unexpectedExceptionWait() call @@ -924,6 +924,6 @@ ::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; + return nullptr; } #endif diff --git a/lib/cpp/test/processor/ServerThread.cpp b/lib/cpp/test/processor/ServerThread.cpp index e752d5e1dc8..b0505005be1 100644 --- a/lib/cpp/test/processor/ServerThread.cpp +++ b/lib/cpp/test/processor/ServerThread.cpp @@ -21,7 +21,7 @@ #include "ServerThread.h" -#include +#include #include #include #include @@ -38,7 +38,7 @@ void ServerThread::start() { helper_.reset(new Helper(this)); // Start the other thread - concurrency::PlatformThreadFactory threadFactory; + concurrency::ThreadFactory threadFactory; threadFactory.setDetached(false); thread_ = threadFactory.newThread(helper_); @@ -130,7 +130,7 @@ void ServerThread::preServe() { serverState_->bindSuccessful(port_); // Set the real server event handler (replacing ourself) - stdcxx::shared_ptr serverEventHandler + std::shared_ptr serverEventHandler = serverState_->getServerEventHandler(); server_->setServerEventHandler(serverEventHandler); diff --git a/lib/cpp/test/processor/ServerThread.h b/lib/cpp/test/processor/ServerThread.h index 21c3b60e086..9cca2d6002e 100644 --- a/lib/cpp/test/processor/ServerThread.h +++ b/lib/cpp/test/processor/ServerThread.h @@ -35,7 +35,7 @@ namespace test { */ class ServerState { public: - virtual ~ServerState() {} + virtual ~ServerState() = default; /** * Create a server to listen on the specified port. @@ -43,7 +43,7 @@ class ServerState { * 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 stdcxx::shared_ptr createServer(uint16_t port) = 0; + virtual std::shared_ptr createServer(uint16_t port) = 0; /** * Get the TServerEventHandler to set on the server. @@ -52,8 +52,8 @@ class ServerState { * start serving traffic. It is invoked from the server thread, rather than * the main thread. */ - virtual stdcxx::shared_ptr getServerEventHandler() { - return stdcxx::shared_ptr(); + virtual std::shared_ptr getServerEventHandler() { + return std::shared_ptr(); } /** @@ -70,7 +70,7 @@ class ServerState { */ class ServerThread { public: - ServerThread(const stdcxx::shared_ptr& state, bool autoStart) + ServerThread(const std::shared_ptr& state, bool autoStart) : port_(0), running_(false), serving_(false), @@ -105,9 +105,9 @@ class ServerThread { public: Helper(ServerThread* serverThread) : serverThread_(serverThread) {} - void run() { serverThread_->run(); } + void run() override { serverThread_->run(); } - void preServe() { serverThread_->preServe(); } + void preServe() override { serverThread_->preServe(); } private: ServerThread* serverThread_; @@ -116,7 +116,7 @@ class ServerThread { void run(); void preServe(); - stdcxx::shared_ptr helper_; + std::shared_ptr helper_; uint16_t port_; bool running_; @@ -124,9 +124,9 @@ class ServerThread { bool error_; concurrency::Monitor serverMonitor_; - stdcxx::shared_ptr serverState_; - stdcxx::shared_ptr server_; - stdcxx::shared_ptr thread_; + std::shared_ptr serverState_; + std::shared_ptr server_; + std::shared_ptr thread_; }; } } diff --git a/lib/cpp/test/qt/TQTcpServerTest.cpp b/lib/cpp/test/qt/TQTcpServerTest.cpp index 8a327aa24ca..3371a9ae824 100644 --- a/lib/cpp/test/qt/TQTcpServerTest.cpp +++ b/lib/cpp/test/qt/TQTcpServerTest.cpp @@ -8,7 +8,6 @@ #include #ifndef Q_MOC_RUN - #include "thrift/stdcxx.h" #include "thrift/protocol/TBinaryProtocol.h" #include "thrift/async/TAsyncProcessor.h" #include "thrift/qt/TQTcpServer.h" @@ -21,25 +20,25 @@ using namespace apache::thrift; struct AsyncHandler : public test::ParentServiceCobSvIf { std::vector strings; - virtual void addString(stdcxx::function cob, const std::string& s) { + void addString(std::function cob, const std::string& s) override { strings.push_back(s); cob(); } - virtual void getStrings(stdcxx::function const& _return)> cob) { + void getStrings(std::function const& _return)> cob) override { cob(strings); } // Overrides not used in this test - virtual void incrementGeneration(stdcxx::function cob) {} - virtual void getGeneration(stdcxx::function cob) {} - virtual void getDataWait(stdcxx::function cob, - const int32_t length) {} - virtual void onewayWait(stdcxx::function cob) {} - virtual void exceptionWait( - stdcxx::function cob, - stdcxx::function /* exn_cob */, - const std::string& message) {} - virtual void unexpectedExceptionWait(stdcxx::function cob, const std::string& message) {} + void incrementGeneration(std::function cob) override {} + void getGeneration(std::function cob) override {} + void getDataWait(std::function cob, + const int32_t length) override {} + void onewayWait(std::function cob) override {} + void exceptionWait( + std::function cob, + std::function /* exn_cob */, + const std::string& message) override {} + void unexpectedExceptionWait(std::function cob, const std::string& message) override {} }; class TQTcpServerTest : public QObject { @@ -51,18 +50,18 @@ private slots: void test_communicate(); private: - stdcxx::shared_ptr serverThread; - stdcxx::shared_ptr server; - stdcxx::shared_ptr client; + std::shared_ptr serverThread; + std::shared_ptr server; + std::shared_ptr client; }; void TQTcpServerTest::initTestCase() { // setup server - stdcxx::shared_ptr serverSocket = stdcxx::make_shared(); + std::shared_ptr serverSocket = std::make_shared(); server.reset(new async::TQTcpServer(serverSocket, - stdcxx::make_shared( - stdcxx::make_shared()), - stdcxx::make_shared())); + std::make_shared( + std::make_shared()), + std::make_shared())); QVERIFY(serverSocket->listen(QHostAddress::LocalHost)); int port = serverSocket->serverPort(); QVERIFY(port > 0); @@ -74,9 +73,9 @@ void TQTcpServerTest::initTestCase() { serverThread->start(); // setup client - stdcxx::shared_ptr socket = stdcxx::make_shared(); - client.reset(new test::ParentServiceClient(stdcxx::make_shared( - stdcxx::make_shared(socket)))); + std::shared_ptr socket = std::make_shared(); + client.reset(new test::ParentServiceClient(std::make_shared( + std::make_shared(socket)))); socket->connectToHost(QHostAddress::LocalHost, port); QVERIFY(socket->waitForConnected()); } diff --git a/lib/csharp/Makefile.am b/lib/csharp/Makefile.am deleted file mode 100644 index be49d5ecd82..00000000000 --- a/lib/csharp/Makefile.am +++ /dev/null @@ -1,111 +0,0 @@ -# -# Licensed to the Apache Software Foundation (ASF) under one -# or more contributor license agreements. See the NOTICE file -# distributed with this work for additional information -# regarding copyright ownership. The ASF licenses this file -# to you under the Apache License, Version 2.0 (the -# "License"); you may not use this file except in compliance -# with the License. You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, -# software distributed under the License is distributed on an -# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -# KIND, either express or implied. See the License for the -# specific language governing permissions and limitations -# under the License. -# - -SUBDIRS = . test/Multiplex - -THRIFTCODE = \ - src/Collections/THashSet.cs \ - src/Collections/TCollections.cs \ - src/Properties/AssemblyInfo.cs \ - src/Protocol/TAbstractBase.cs \ - src/Protocol/TBase.cs \ - src/Protocol/TBase64Utils.cs \ - src/Protocol/TJSONProtocol.cs \ - src/Protocol/TProtocolException.cs \ - src/Protocol/TProtocolFactory.cs \ - src/Protocol/TList.cs \ - src/Protocol/TSet.cs \ - src/Protocol/TMap.cs \ - src/Protocol/TProtocolUtil.cs \ - src/Protocol/TMessageType.cs \ - src/Protocol/TProtocol.cs \ - src/Protocol/TProtocolDecorator.cs \ - src/Protocol/TMultiplexedProtocol.cs \ - src/Protocol/TMultiplexedProcessor.cs \ - src/Protocol/TType.cs \ - src/Protocol/TField.cs \ - src/Protocol/TMessage.cs \ - src/Protocol/TStruct.cs \ - src/Protocol/TBinaryProtocol.cs \ - src/Protocol/TCompactProtocol.cs \ - src/Server/TThreadedServer.cs \ - src/Server/TThreadPoolServer.cs \ - src/Server/TSimpleServer.cs \ - src/Server/TServer.cs \ - src/Server/TServerEventHandler.cs \ - src/Transport/TBufferedTransport.cs \ - src/Transport/TTransport.cs \ - src/Transport/TSocket.cs \ - src/Transport/TSocketVersionizer.cs \ - src/Transport/TTransportException.cs \ - src/Transport/TStreamTransport.cs \ - src/Transport/TFramedTransport.cs \ - src/Transport/TServerTransport.cs \ - src/Transport/TServerSocket.cs \ - src/Transport/TTransportFactory.cs \ - src/Transport/THttpClient.cs \ - src/Transport/THttpHandler.cs \ - src/Transport/TMemoryBuffer.cs \ - src/Transport/TNamedPipeClientTransport.cs \ - src/Transport/TNamedPipeServerTransport.cs \ - src/Transport/TTLSSocket.cs \ - src/Transport/TTLSServerSocket.cs \ - src/TProcessor.cs \ - src/TProcessorFactory.cs \ - src/TSingletonProcessorFactory.cs \ - src/TPrototypeProcessorFactory.cs \ - src/TControllingHandler.cs \ - src/TException.cs \ - src/TApplicationException.cs - -if MONO_MCS -export CSC = mcs -else -export CSC = gmcs -endif - -if NET_2_0 -export CSC_DEFINES = -d:NET_2_0 -endif - -all-local: Thrift.dll Thrift.45.dll - -Thrift.dll: $(THRIFTCODE) - $(CSC) $(CSC_DEFINES) -out:$@ -target:library -reference:System.Web $(THRIFTCODE) - -Thrift.45.dll: $(THRIFTCODE) - $(CSC) $(CSC_DEFINES) -out:$@ -target:library -reference:System.Web $(THRIFTCODE) - -clean-local: - $(RM) Thrift.dll \ - $(RM) Thrift.45.dll - -EXTRA_DIST = \ - $(THRIFTCODE) \ - ThriftMSBuildTask \ - src/Thrift.csproj \ - src/Thrift.45.csproj \ - src/Thrift.sln \ - src/Net35/ExtensionsNet35.cs \ - src/Transport/TSilverlightSocket.cs \ - src/Transport/THttpTaskAsyncHandler.cs \ - src/TAsyncProcessor.cs \ - test \ - coding_standards.md \ - README.md diff --git a/lib/csharp/README.md b/lib/csharp/README.md deleted file mode 100644 index b7dc5de3272..00000000000 --- a/lib/csharp/README.md +++ /dev/null @@ -1,26 +0,0 @@ -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# -==================== - -Thrift requires Mono >= 1.2.6 or .NET framework >= 3.5 diff --git a/lib/csharp/ThriftMSBuildTask/Properties/AssemblyInfo.cs b/lib/csharp/ThriftMSBuildTask/Properties/AssemblyInfo.cs deleted file mode 100644 index 19abcf7be13..00000000000 --- a/lib/csharp/ThriftMSBuildTask/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,60 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - * - * Contains some contributions under the Thrift Software License. - * Please see doc/old-thrift-license.txt in the Thrift distribution for - * details. - */ - -using System.Reflection; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; - -// General Information about an assembly is controlled through the following -// set of attributes. Change these attribute values to modify the information -// associated with an assembly. -[assembly: AssemblyTitle("ThriftMSBuildTask")] -[assembly: AssemblyDescription("MSBuild Task to generate csharp from .thrift files, and compile the code into a library: ThriftImpl.dll")] -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("The Apache Software Foundation")] -[assembly: AssemblyProduct("ThriftMSBuildTask")] -[assembly: AssemblyCopyright("The Apache Software Foundation")] -[assembly: AssemblyTrademark("")] -[assembly: AssemblyCulture("")] -//@TODO where to put License information? - -// Setting ComVisible to false makes the types in this assembly not visible -// to COM components. If you need to access a type in this assembly from -// COM, set the ComVisible attribute to true on that type. -[assembly: ComVisible(false)] - -// The following GUID is for the ID of the typelib if this project is exposed to COM -[assembly: Guid("5095e09d-7b95-4be1-b250-e1c1db1c485e")] - -// Version information for an assembly consists of the following four values: -// -// Major Version -// Minor Version -// Build Number -// Revision -// -// You can specify all the values or you can default the Build and Revision Numbers -// by using the '*' as shown below: -// [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("0.12.0.*")] -[assembly: AssemblyFileVersion("0.12.0.*")] diff --git a/lib/csharp/ThriftMSBuildTask/ThriftBuild.cs b/lib/csharp/ThriftMSBuildTask/ThriftBuild.cs deleted file mode 100644 index 4e6d3011256..00000000000 --- a/lib/csharp/ThriftMSBuildTask/ThriftBuild.cs +++ /dev/null @@ -1,246 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - * - * Contains some contributions under the Thrift Software License. - * Please see doc/old-thrift-license.txt in the Thrift distribution for - * details. - */ - -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using Microsoft.Build.Framework; -using Microsoft.Build.Utilities; -using Microsoft.Build.Tasks; -using System.IO; -using System.Diagnostics; - -namespace ThriftMSBuildTask -{ - /// - /// MSBuild Task to generate csharp from .thrift files, and compile the code into a library: ThriftImpl.dll - /// - public class ThriftBuild : Task - { - /// - /// The full path to the thrift.exe compiler - /// - [Required] - public ITaskItem ThriftExecutable - { - get; - set; - } - - /// - /// The full path to a thrift.dll C# library - /// - [Required] - public ITaskItem ThriftLibrary - { - get; - set; - } - - /// - /// A direcotry containing .thrift files - /// - [Required] - public ITaskItem ThriftDefinitionDir - { - get; - set; - } - - /// - /// The name of the auto-gen and compiled thrift library. It will placed in - /// the same directory as ThriftLibrary - /// - [Required] - public ITaskItem OutputName - { - get; - set; - } - - /// - /// The full path to the compiled ThriftLibrary. This allows msbuild tasks to use this - /// output as a variable for use elsewhere. - /// - [Output] - public ITaskItem ThriftImplementation - { - get { return thriftImpl; } - } - - private ITaskItem thriftImpl; - private const string lastCompilationName = "LAST_COMP_TIMESTAMP"; - - //use the Message Build Task to write something to build log - private void LogMessage(string text, MessageImportance importance) - { - Message m = new Message(); - m.Text = text; - m.Importance = importance.ToString(); - m.BuildEngine = this.BuildEngine; - m.Execute(); - } - - //recursively find .cs files in srcDir, paths should initially be non-null and empty - private void FindSourcesHelper(string srcDir, List paths) - { - string[] files = Directory.GetFiles(srcDir, "*.cs"); - foreach (string f in files) - { - paths.Add(f); - } - string[] dirs = Directory.GetDirectories(srcDir); - foreach (string dir in dirs) - { - FindSourcesHelper(dir, paths); - } - } - - /// - /// Quote paths with spaces - /// - private string SafePath(string path) - { - if (path.Contains(' ') && !path.StartsWith("\"")) - { - return "\"" + path + "\""; - } - return path; - } - - private ITaskItem[] FindSources(string srcDir) - { - List files = new List(); - FindSourcesHelper(srcDir, files); - ITaskItem[] items = new ITaskItem[files.Count]; - for (int i = 0; i < items.Length; i++) - { - items[i] = new TaskItem(files[i]); - } - return items; - } - - private string LastWriteTime(string defDir) - { - string[] files = Directory.GetFiles(defDir, "*.thrift"); - DateTime d = (new DirectoryInfo(defDir)).LastWriteTime; - foreach(string file in files) - { - FileInfo f = new FileInfo(file); - DateTime curr = f.LastWriteTime; - if (DateTime.Compare(curr, d) > 0) - { - d = curr; - } - } - return d.ToFileTimeUtc().ToString(); - } - - public override bool Execute() - { - string defDir = SafePath(ThriftDefinitionDir.ItemSpec); - //look for last compilation timestamp - string lastBuildPath = Path.Combine(defDir, lastCompilationName); - DirectoryInfo defDirInfo = new DirectoryInfo(defDir); - string lastWrite = LastWriteTime(defDir); - if (File.Exists(lastBuildPath)) - { - string lastComp = File.ReadAllText(lastBuildPath); - //don't recompile if the thrift library has been updated since lastComp - FileInfo f = new FileInfo(ThriftLibrary.ItemSpec); - string thriftLibTime = f.LastWriteTimeUtc.ToFileTimeUtc().ToString(); - if (lastComp.CompareTo(thriftLibTime) < 0) - { - //new thrift library, do a compile - lastWrite = thriftLibTime; - } - else if (lastComp == lastWrite || (lastComp == thriftLibTime && lastComp.CompareTo(lastWrite) > 0)) - { - //the .thrift dir hasn't been written to since last compilation, don't need to do anything - LogMessage("ThriftImpl up-to-date", MessageImportance.High); - return true; - } - } - - //find the directory of the thriftlibrary (that's where output will go) - FileInfo thriftLibInfo = new FileInfo(SafePath(ThriftLibrary.ItemSpec)); - string thriftDir = thriftLibInfo.Directory.FullName; - - string genDir = Path.Combine(thriftDir, "gen-csharp"); - if (Directory.Exists(genDir)) - { - try - { - Directory.Delete(genDir, true); - } - catch { /*eh i tried, just over-write now*/} - } - - //run the thrift executable to generate C# - foreach (string thriftFile in Directory.GetFiles(defDir, "*.thrift")) - { - LogMessage("Generating code for: " + thriftFile, MessageImportance.Normal); - Process p = new Process(); - p.StartInfo.FileName = SafePath(ThriftExecutable.ItemSpec); - p.StartInfo.Arguments = "--gen csharp -o " + SafePath(thriftDir) + " -r " + thriftFile; - p.StartInfo.UseShellExecute = false; - p.StartInfo.CreateNoWindow = true; - p.StartInfo.RedirectStandardOutput = false; - p.Start(); - p.WaitForExit(); - if (p.ExitCode != 0) - { - LogMessage("thrift.exe failed to compile " + thriftFile, MessageImportance.High); - return false; - } - if (p.ExitCode != 0) - { - LogMessage("thrift.exe failed to compile " + thriftFile, MessageImportance.High); - return false; - } - } - - Csc csc = new Csc(); - csc.TargetType = "library"; - csc.References = new ITaskItem[] { new TaskItem(ThriftLibrary.ItemSpec) }; - csc.EmitDebugInformation = true; - string outputPath = Path.Combine(thriftDir, OutputName.ItemSpec); - csc.OutputAssembly = new TaskItem(outputPath); - csc.Sources = FindSources(Path.Combine(thriftDir, "gen-csharp")); - csc.BuildEngine = this.BuildEngine; - LogMessage("Compiling generated cs...", MessageImportance.Normal); - if (!csc.Execute()) - { - return false; - } - - //write file to defDir to indicate a build was successfully completed - File.WriteAllText(lastBuildPath, lastWrite); - - thriftImpl = new TaskItem(outputPath); - - return true; - } - } -} diff --git a/lib/csharp/ThriftMSBuildTask/ThriftMSBuildTask.csproj b/lib/csharp/ThriftMSBuildTask/ThriftMSBuildTask.csproj deleted file mode 100644 index 68427c3def5..00000000000 --- a/lib/csharp/ThriftMSBuildTask/ThriftMSBuildTask.csproj +++ /dev/null @@ -1,118 +0,0 @@ - - - - - Debug - AnyCPU - 9.0.21022 - 2.0 - {EC0A0231-66EA-4593-A792-C6CA3BB8668E} - Library - Properties - ThriftMSBuildTask - ThriftMSBuildTask - v3.5 - 512 - - - 3.5 - - publish\ - true - Disk - false - Foreground - 7 - Days - false - false - true - 0 - 0.12.0.%2a - false - false - true - - - true - full - false - bin\Debug\ - DEBUG;TRACE - prompt - 4 - AllRules.ruleset - - - pdbonly - true - bin\Release\ - TRACE - prompt - 4 - AllRules.ruleset - - - - - - - - 3.5 - - - 3.5 - - - 3.5 - - - - - - - - - - - False - .NET Framework 3.5 SP1 Client Profile - false - - - False - .NET Framework 3.5 SP1 - true - - - False - Windows Installer 3.1 - true - - - - - diff --git a/lib/csharp/coding_standards.md b/lib/csharp/coding_standards.md deleted file mode 100644 index dc87190bfb1..00000000000 --- a/lib/csharp/coding_standards.md +++ /dev/null @@ -1,6 +0,0 @@ -## C# Coding Standards - -Please follow: - * [Thrift General Coding Standards](/doc/coding_standards.md) - * [MSDN C# Coding Conventions](http://msdn.microsoft.com/en-us/library/ff926074.aspx) - * [C# Coding Guidelines](http://csharpguidelines.codeplex.com/) diff --git a/lib/csharp/src/Collections/TCollections.cs b/lib/csharp/src/Collections/TCollections.cs deleted file mode 100644 index 84afb6a62a7..00000000000 --- a/lib/csharp/src/Collections/TCollections.cs +++ /dev/null @@ -1,94 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -using System; -using System.Collections; - -namespace Thrift.Collections -{ - public class TCollections - { - /// - /// This will return true if the two collections are value-wise the same. - /// If the collection contains a collection, the collections will be compared using this method. - /// - public static bool Equals (IEnumerable first, IEnumerable second) - { - if (first == null && second == null) - { - return true; - } - if (first == null || second == null) - { - return false; - } - IEnumerator fiter = first.GetEnumerator (); - IEnumerator siter = second.GetEnumerator (); - - bool fnext = fiter.MoveNext (); - bool snext = siter.MoveNext (); - while (fnext && snext) - { - IEnumerable fenum = fiter.Current as IEnumerable; - IEnumerable senum = siter.Current as IEnumerable; - if (fenum != null && senum != null) - { - if (!Equals(fenum, senum)) - { - return false; - } - } - else if (fenum == null ^ senum == null) - { - return false; - } - else if (!Equals(fiter.Current, siter.Current)) - { - return false; - } - fnext = fiter.MoveNext(); - snext = siter.MoveNext(); - } - - return fnext == snext; - } - - /// - /// This returns a hashcode based on the value of the enumerable. - /// - public static int GetHashCode (IEnumerable enumerable) - { - if (enumerable == null) - { - return 0; - } - - int hashcode = 0; - foreach (object obj in enumerable) - { - IEnumerable enum2 = obj as IEnumerable; - int objHash = enum2 == null ? obj.GetHashCode () : GetHashCode (enum2); - unchecked - { - hashcode = (hashcode * 397) ^ (objHash); - } - } - return hashcode; - } - } -} diff --git a/lib/csharp/src/Collections/THashSet.cs b/lib/csharp/src/Collections/THashSet.cs deleted file mode 100644 index e29271a935d..00000000000 --- a/lib/csharp/src/Collections/THashSet.cs +++ /dev/null @@ -1,160 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -using System; -using System.Collections; -using System.Collections.Generic; - -#if SILVERLIGHT -using System.Runtime.Serialization; -#endif - -namespace Thrift.Collections -{ -#if SILVERLIGHT - [DataContract] -#else - [Serializable] -#endif - public class THashSet : ICollection - { -#if NET_2_0 || SILVERLIGHT -#if SILVERLIGHT - [DataMember] -#endif - TDictSet set = new TDictSet(); -#else - HashSet set = new HashSet(); -#endif - public int Count - { - get { return set.Count; } - } - - public bool IsReadOnly - { - get { return false; } - } - - public void Add(T item) - { - set.Add(item); - } - - public void Clear() - { - set.Clear(); - } - - public bool Contains(T item) - { - return set.Contains(item); - } - - public void CopyTo(T[] array, int arrayIndex) - { - set.CopyTo(array, arrayIndex); - } - - public IEnumerator GetEnumerator() - { - return set.GetEnumerator(); - } - - IEnumerator IEnumerable.GetEnumerator() - { - return ((IEnumerable)set).GetEnumerator(); - } - - public bool Remove(T item) - { - return set.Remove(item); - } - -#if NET_2_0 || SILVERLIGHT -#if SILVERLIGHT - [DataContract] -#endif - private class TDictSet : ICollection - { -#if SILVERLIGHT - [DataMember] -#endif - Dictionary> dict = new Dictionary>(); - - public int Count - { - get { return dict.Count; } - } - - public bool IsReadOnly - { - get { return false; } - } - - public IEnumerator GetEnumerator() - { - return ((IEnumerable)dict.Keys).GetEnumerator(); - } - - IEnumerator IEnumerable.GetEnumerator() - { - return dict.Keys.GetEnumerator(); - } - - public bool Add(V item) - { - if (!dict.ContainsKey(item)) - { - dict[item] = this; - return true; - } - - return false; - } - - void ICollection.Add(V item) - { - Add(item); - } - - public void Clear() - { - dict.Clear(); - } - - public bool Contains(V item) - { - return dict.ContainsKey(item); - } - - public void CopyTo(V[] array, int arrayIndex) - { - dict.Keys.CopyTo(array, arrayIndex); - } - - public bool Remove(V item) - { - return dict.Remove(item); - } - } -#endif - } - -} diff --git a/lib/csharp/src/Net35/ExtensionsNet35.cs b/lib/csharp/src/Net35/ExtensionsNet35.cs deleted file mode 100644 index 73a42328847..00000000000 --- a/lib/csharp/src/Net35/ExtensionsNet35.cs +++ /dev/null @@ -1,31 +0,0 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Text; - -#if (!NET45) -namespace Thrift -{ - static class StreamExtensionsNet35 - { - // CopyTo() has been added in 4.0 - public static long CopyTo(this Stream source, Stream target) - { - byte[] buffer = new byte[8192]; // multiple of 4096 - long nTotal = 0; - while (true) - { - int nRead = source.Read(buffer, 0, buffer.Length); - if (nRead <= 0) // done? - return nTotal; - - target.Write(buffer, 0, nRead); - nTotal += nRead; - } - } - } - -} -#endif - diff --git a/lib/csharp/src/Properties/AssemblyInfo.cs b/lib/csharp/src/Properties/AssemblyInfo.cs deleted file mode 100644 index 23fbc73889e..00000000000 --- a/lib/csharp/src/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,55 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -using System.Reflection; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; - -// General Information about an assembly is controlled through the following -// set of attributes. Change these attribute values to modify the information -// associated with an assembly. -[assembly: AssemblyTitle("Thrift")] -[assembly: AssemblyDescription("C# bindings for the Apache Thrift RPC system")] -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("The Apache Software Foundation")] -[assembly: AssemblyProduct("Thrift")] -[assembly: AssemblyCopyright("The Apache Software Foundation")] -[assembly: AssemblyTrademark("")] -[assembly: AssemblyCulture("")] -//@TODO where to put License information? - -// Setting ComVisible to false makes the types in this assembly not visible -// to COM components. If you need to access a type in this assembly from -// COM, set the ComVisible attribute to true on that type. -[assembly: ComVisible(false)] - -// The following GUID is for the ID of the typelib if this project is exposed to COM -[assembly: Guid("df3f8ef0-e0a3-4c86-a65b-8ec84e016b1d")] - -// Version information for an assembly consists of the following four values: -// -// Major Version -// Minor Version -// Build Number -// Revision -// -// You can specify all the values or you can default the Build and Revision Numbers -// by using the '*' as shown below: -[assembly: AssemblyVersion("0.12.0.1")] -[assembly: AssemblyFileVersion("0.12.0.1")] diff --git a/lib/csharp/src/Protocol/TAbstractBase.cs b/lib/csharp/src/Protocol/TAbstractBase.cs deleted file mode 100644 index f5a61cd44ed..00000000000 --- a/lib/csharp/src/Protocol/TAbstractBase.cs +++ /dev/null @@ -1,29 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -namespace Thrift.Protocol -{ - public interface TAbstractBase - { - /// - /// Writes the objects out to the protocol. - /// - void Write(TProtocol tProtocol); - } -} diff --git a/lib/csharp/src/Protocol/TBase64Utils.cs b/lib/csharp/src/Protocol/TBase64Utils.cs deleted file mode 100644 index 9eaaebddaa8..00000000000 --- a/lib/csharp/src/Protocol/TBase64Utils.cs +++ /dev/null @@ -1,100 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -using System; - -namespace Thrift.Protocol -{ - internal static class TBase64Utils - { - internal const string ENCODE_TABLE = - "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; - - internal static void encode(byte[] src, int srcOff, int len, byte[] dst, - int dstOff) - { - dst[dstOff] = (byte)ENCODE_TABLE[(src[srcOff] >> 2) & 0x3F]; - if (len == 3) - { - dst[dstOff + 1] = - (byte)ENCODE_TABLE[ - ((src[srcOff] << 4) & 0x30) | ((src[srcOff + 1] >> 4) & 0x0F)]; - dst[dstOff + 2] = - (byte)ENCODE_TABLE[ - ((src[srcOff + 1] << 2) & 0x3C) | ((src[srcOff + 2] >> 6) & 0x03)]; - dst[dstOff + 3] = - (byte)ENCODE_TABLE[src[srcOff + 2] & 0x3F]; - } - else if (len == 2) - { - dst[dstOff + 1] = - (byte)ENCODE_TABLE[ - ((src[srcOff] << 4) & 0x30) | ((src[srcOff + 1] >> 4) & 0x0F)]; - dst[dstOff + 2] = - (byte)ENCODE_TABLE[(src[srcOff + 1] << 2) & 0x3C]; - - } - else - { // len == 1) { - dst[dstOff + 1] = - (byte)ENCODE_TABLE[(src[srcOff] << 4) & 0x30]; - } - } - - private static int[] DECODE_TABLE = { - -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, - -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, - -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,62,-1,-1,-1,63, - 52,53,54,55,56,57,58,59,60,61,-1,-1,-1,-1,-1,-1, - -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14, - 15,16,17,18,19,20,21,22,23,24,25,-1,-1,-1,-1,-1, - -1,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40, - 41,42,43,44,45,46,47,48,49,50,51,-1,-1,-1,-1,-1, - -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, - -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, - -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, - -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, - -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, - -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, - -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, - -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, - }; - - internal static void decode(byte[] src, int srcOff, int len, byte[] dst, - int dstOff) - { - dst[dstOff] = (byte) - ((DECODE_TABLE[src[srcOff] & 0x0FF] << 2) | - (DECODE_TABLE[src[srcOff + 1] & 0x0FF] >> 4)); - if (len > 2) - { - dst[dstOff + 1] = (byte) - (((DECODE_TABLE[src[srcOff + 1] & 0x0FF] << 4) & 0xF0) | - (DECODE_TABLE[src[srcOff + 2] & 0x0FF] >> 2)); - if (len > 3) - { - dst[dstOff + 2] = (byte) - (((DECODE_TABLE[src[srcOff + 2] & 0x0FF] << 6) & 0xC0) | - DECODE_TABLE[src[srcOff + 3] & 0x0FF]); - } - } - } - - } -} diff --git a/lib/csharp/src/Protocol/TBinaryProtocol.cs b/lib/csharp/src/Protocol/TBinaryProtocol.cs deleted file mode 100644 index a4faa946f1b..00000000000 --- a/lib/csharp/src/Protocol/TBinaryProtocol.cs +++ /dev/null @@ -1,395 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - * - * Contains some contributions under the Thrift Software License. - * Please see doc/old-thrift-license.txt in the Thrift distribution for - * details. - */ - -using System; -using System.Text; -using Thrift.Transport; - -namespace Thrift.Protocol -{ - public class TBinaryProtocol : TProtocol - { - protected const uint VERSION_MASK = 0xffff0000; - protected const uint VERSION_1 = 0x80010000; - - protected bool strictRead_ = false; - protected bool strictWrite_ = true; - - #region BinaryProtocol Factory - - public class Factory : TProtocolFactory - { - protected bool strictRead_ = false; - protected bool strictWrite_ = true; - - public Factory() - : this(false, true) - { - } - - public Factory(bool strictRead, bool strictWrite) - { - strictRead_ = strictRead; - strictWrite_ = strictWrite; - } - - public TProtocol GetProtocol(TTransport trans) - { - return new TBinaryProtocol(trans, strictRead_, strictWrite_); - } - } - - #endregion - - public TBinaryProtocol(TTransport trans) - : this(trans, false, true) - { - } - - public TBinaryProtocol(TTransport trans, bool strictRead, bool strictWrite) - : base(trans) - { - strictRead_ = strictRead; - strictWrite_ = strictWrite; - } - - #region Write Methods - - public override void WriteMessageBegin(TMessage message) - { - if (strictWrite_) - { - uint version = VERSION_1 | (uint)(message.Type); - WriteI32((int)version); - WriteString(message.Name); - WriteI32(message.SeqID); - } - else - { - WriteString(message.Name); - WriteByte((sbyte)message.Type); - WriteI32(message.SeqID); - } - } - - public override void WriteMessageEnd() - { - } - - public override void WriteStructBegin(TStruct struc) - { - } - - public override void WriteStructEnd() - { - } - - public override void WriteFieldBegin(TField field) - { - WriteByte((sbyte)field.Type); - WriteI16(field.ID); - } - - public override void WriteFieldEnd() - { - } - - public override void WriteFieldStop() - { - WriteByte((sbyte)TType.Stop); - } - - public override void WriteMapBegin(TMap map) - { - WriteByte((sbyte)map.KeyType); - WriteByte((sbyte)map.ValueType); - WriteI32(map.Count); - } - - public override void WriteMapEnd() - { - } - - public override void WriteListBegin(TList list) - { - WriteByte((sbyte)list.ElementType); - WriteI32(list.Count); - } - - public override void WriteListEnd() - { - } - - public override void WriteSetBegin(TSet set) - { - WriteByte((sbyte)set.ElementType); - WriteI32(set.Count); - } - - public override void WriteSetEnd() - { - } - - public override void WriteBool(bool b) - { - WriteByte(b ? (sbyte)1 : (sbyte)0); - } - - private byte[] bout = new byte[1]; - public override void WriteByte(sbyte b) - { - bout[0] = (byte)b; - trans.Write(bout, 0, 1); - } - - private byte[] i16out = new byte[2]; - public override void WriteI16(short s) - { - i16out[0] = (byte)(0xff & (s >> 8)); - i16out[1] = (byte)(0xff & s); - trans.Write(i16out, 0, 2); - } - - private byte[] i32out = new byte[4]; - public override void WriteI32(int i32) - { - i32out[0] = (byte)(0xff & (i32 >> 24)); - i32out[1] = (byte)(0xff & (i32 >> 16)); - i32out[2] = (byte)(0xff & (i32 >> 8)); - i32out[3] = (byte)(0xff & i32); - trans.Write(i32out, 0, 4); - } - - private byte[] i64out = new byte[8]; - public override void WriteI64(long i64) - { - i64out[0] = (byte)(0xff & (i64 >> 56)); - i64out[1] = (byte)(0xff & (i64 >> 48)); - i64out[2] = (byte)(0xff & (i64 >> 40)); - i64out[3] = (byte)(0xff & (i64 >> 32)); - i64out[4] = (byte)(0xff & (i64 >> 24)); - i64out[5] = (byte)(0xff & (i64 >> 16)); - i64out[6] = (byte)(0xff & (i64 >> 8)); - i64out[7] = (byte)(0xff & i64); - trans.Write(i64out, 0, 8); - } - - public override void WriteDouble(double d) - { -#if !SILVERLIGHT - WriteI64(BitConverter.DoubleToInt64Bits(d)); -#else - var bytes = BitConverter.GetBytes(d); - WriteI64(BitConverter.ToInt64(bytes, 0)); -#endif - } - - public override void WriteBinary(byte[] b) - { - WriteI32(b.Length); - trans.Write(b, 0, b.Length); - } - - #endregion - - #region ReadMethods - - public override TMessage ReadMessageBegin() - { - TMessage message = new TMessage(); - int size = ReadI32(); - if (size < 0) - { - uint version = (uint)size & VERSION_MASK; - if (version != VERSION_1) - { - throw new TProtocolException(TProtocolException.BAD_VERSION, "Bad version in ReadMessageBegin: " + version); - } - message.Type = (TMessageType)(size & 0x000000ff); - message.Name = ReadString(); - message.SeqID = ReadI32(); - } - else - { - if (strictRead_) - { - throw new TProtocolException(TProtocolException.BAD_VERSION, "Missing version in readMessageBegin, old client?"); - } - message.Name = ReadStringBody(size); - message.Type = (TMessageType)ReadByte(); - message.SeqID = ReadI32(); - } - return message; - } - - public override void ReadMessageEnd() - { - } - - public override TStruct ReadStructBegin() - { - return new TStruct(); - } - - public override void ReadStructEnd() - { - } - - public override TField ReadFieldBegin() - { - TField field = new TField(); - field.Type = (TType)ReadByte(); - - if (field.Type != TType.Stop) - { - field.ID = ReadI16(); - } - - return field; - } - - public override void ReadFieldEnd() - { - } - - public override TMap ReadMapBegin() - { - TMap map = new TMap(); - map.KeyType = (TType)ReadByte(); - map.ValueType = (TType)ReadByte(); - map.Count = ReadI32(); - - return map; - } - - public override void ReadMapEnd() - { - } - - public override TList ReadListBegin() - { - TList list = new TList(); - list.ElementType = (TType)ReadByte(); - list.Count = ReadI32(); - - return list; - } - - public override void ReadListEnd() - { - } - - public override TSet ReadSetBegin() - { - TSet set = new TSet(); - set.ElementType = (TType)ReadByte(); - set.Count = ReadI32(); - - return set; - } - - public override void ReadSetEnd() - { - } - - public override bool ReadBool() - { - return ReadByte() == 1; - } - - private byte[] bin = new byte[1]; - public override sbyte ReadByte() - { - ReadAll(bin, 0, 1); - return (sbyte)bin[0]; - } - - private byte[] i16in = new byte[2]; - public override short ReadI16() - { - ReadAll(i16in, 0, 2); - return (short)(((i16in[0] & 0xff) << 8) | ((i16in[1] & 0xff))); - } - - private byte[] i32in = new byte[4]; - public override int ReadI32() - { - ReadAll(i32in, 0, 4); - return (int)(((i32in[0] & 0xff) << 24) | ((i32in[1] & 0xff) << 16) | ((i32in[2] & 0xff) << 8) | ((i32in[3] & 0xff))); - } - -#pragma warning disable 675 - - private byte[] i64in = new byte[8]; - public override long ReadI64() - { - ReadAll(i64in, 0, 8); - unchecked - { - return (long)( - ((long)(i64in[0] & 0xff) << 56) | - ((long)(i64in[1] & 0xff) << 48) | - ((long)(i64in[2] & 0xff) << 40) | - ((long)(i64in[3] & 0xff) << 32) | - ((long)(i64in[4] & 0xff) << 24) | - ((long)(i64in[5] & 0xff) << 16) | - ((long)(i64in[6] & 0xff) << 8) | - ((long)(i64in[7] & 0xff))); - } - } - -#pragma warning restore 675 - - public override double ReadDouble() - { -#if !SILVERLIGHT - return BitConverter.Int64BitsToDouble(ReadI64()); -#else - var value = ReadI64(); - var bytes = BitConverter.GetBytes(value); - return BitConverter.ToDouble(bytes, 0); -#endif - } - - public override byte[] ReadBinary() - { - int size = ReadI32(); - byte[] buf = new byte[size]; - trans.ReadAll(buf, 0, size); - return buf; - } - private string ReadStringBody(int size) - { - byte[] buf = new byte[size]; - trans.ReadAll(buf, 0, size); - return Encoding.UTF8.GetString(buf, 0, buf.Length); - } - - private int ReadAll(byte[] buf, int off, int len) - { - return trans.ReadAll(buf, off, len); - } - - #endregion - } -} diff --git a/lib/csharp/src/Protocol/TCompactProtocol.cs b/lib/csharp/src/Protocol/TCompactProtocol.cs deleted file mode 100644 index ff673975efa..00000000000 --- a/lib/csharp/src/Protocol/TCompactProtocol.cs +++ /dev/null @@ -1,849 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - * - * Contains some contributions under the Thrift Software License. - * Please see doc/old-thrift-license.txt in the Thrift distribution for - * details. - */ - -using System; -using System.Text; -using Thrift.Transport; -using System.Collections; -using System.IO; -using System.Collections.Generic; - -namespace Thrift.Protocol -{ - public class TCompactProtocol : TProtocol - { - private static TStruct ANONYMOUS_STRUCT = new TStruct(""); - private static TField TSTOP = new TField("", TType.Stop, (short)0); - - private static byte[] ttypeToCompactType = new byte[16]; - - private const byte PROTOCOL_ID = 0x82; - private const byte VERSION = 1; - private const byte VERSION_MASK = 0x1f; // 0001 1111 - private const byte TYPE_MASK = 0xE0; // 1110 0000 - private const byte TYPE_BITS = 0x07; // 0000 0111 - private const int TYPE_SHIFT_AMOUNT = 5; - - /// - /// All of the on-wire type codes. - /// - private static class Types - { - public const byte STOP = 0x00; - public const byte BOOLEAN_TRUE = 0x01; - public const byte BOOLEAN_FALSE = 0x02; - public const byte BYTE = 0x03; - public const byte I16 = 0x04; - public const byte I32 = 0x05; - public const byte I64 = 0x06; - public const byte DOUBLE = 0x07; - public const byte BINARY = 0x08; - public const byte LIST = 0x09; - public const byte SET = 0x0A; - public const byte MAP = 0x0B; - public const byte STRUCT = 0x0C; - } - - /// - /// Used to keep track of the last field for the current and previous structs, - /// so we can do the delta stuff. - /// - private Stack lastField_ = new Stack(15); - - private short lastFieldId_ = 0; - - /// - /// If we encounter a boolean field begin, save the TField here so it can - /// have the value incorporated. - /// - private Nullable booleanField_; - - /// - /// If we Read a field header, and it's a boolean field, save the boolean - /// value here so that ReadBool can use it. - /// - private Nullable boolValue_; - - - #region CompactProtocol Factory - - public class Factory : TProtocolFactory - { - public Factory() { } - - public TProtocol GetProtocol(TTransport trans) - { - return new TCompactProtocol(trans); - } - } - - #endregion - - public TCompactProtocol(TTransport trans) - : base(trans) - { - ttypeToCompactType[(int)TType.Stop] = Types.STOP; - ttypeToCompactType[(int)TType.Bool] = Types.BOOLEAN_TRUE; - ttypeToCompactType[(int)TType.Byte] = Types.BYTE; - ttypeToCompactType[(int)TType.I16] = Types.I16; - ttypeToCompactType[(int)TType.I32] = Types.I32; - ttypeToCompactType[(int)TType.I64] = Types.I64; - ttypeToCompactType[(int)TType.Double] = Types.DOUBLE; - ttypeToCompactType[(int)TType.String] = Types.BINARY; - ttypeToCompactType[(int)TType.List] = Types.LIST; - ttypeToCompactType[(int)TType.Set] = Types.SET; - ttypeToCompactType[(int)TType.Map] = Types.MAP; - ttypeToCompactType[(int)TType.Struct] = Types.STRUCT; - } - - public void reset() - { - lastField_.Clear(); - lastFieldId_ = 0; - } - - #region Write Methods - - /// - /// Writes a byte without any possibility of all that field header nonsense. - /// Used internally by other writing methods that know they need to Write a byte. - /// - private byte[] byteDirectBuffer = new byte[1]; - - private void WriteByteDirect(byte b) - { - byteDirectBuffer[0] = b; - trans.Write(byteDirectBuffer); - } - - /// - /// Writes a byte without any possibility of all that field header nonsense. - /// - private void WriteByteDirect(int n) - { - WriteByteDirect((byte)n); - } - - /// - /// Write an i32 as a varint. Results in 1-5 bytes on the wire. - /// TODO: make a permanent buffer like WriteVarint64? - /// - byte[] i32buf = new byte[5]; - - private void WriteVarint32(uint n) - { - int idx = 0; - while (true) - { - if ((n & ~0x7F) == 0) - { - i32buf[idx++] = (byte)n; - // WriteByteDirect((byte)n); - break; - // return; - } - else - { - i32buf[idx++] = (byte)((n & 0x7F) | 0x80); - // WriteByteDirect((byte)((n & 0x7F) | 0x80)); - n >>= 7; - } - } - trans.Write(i32buf, 0, idx); - } - - /// - /// Write a message header to the wire. Compact Protocol messages contain the - /// protocol version so we can migrate forwards in the future if need be. - /// - public override void WriteMessageBegin(TMessage message) - { - WriteByteDirect(PROTOCOL_ID); - WriteByteDirect((byte)((VERSION & VERSION_MASK) | ((((uint)message.Type) << TYPE_SHIFT_AMOUNT) & TYPE_MASK))); - WriteVarint32((uint)message.SeqID); - WriteString(message.Name); - } - - /// - /// Write a struct begin. This doesn't actually put anything on the wire. We - /// use it as an opportunity to put special placeholder markers on the field - /// stack so we can get the field id deltas correct. - /// - public override void WriteStructBegin(TStruct strct) - { - lastField_.Push(lastFieldId_); - lastFieldId_ = 0; - } - - /// - /// Write a struct end. This doesn't actually put anything on the wire. We use - /// this as an opportunity to pop the last field from the current struct off - /// of the field stack. - /// - public override void WriteStructEnd() - { - lastFieldId_ = lastField_.Pop(); - } - - /// - /// Write a field header containing the field id and field type. If the - /// difference between the current field id and the last one is small (< 15), - /// then the field id will be encoded in the 4 MSB as a delta. Otherwise, the - /// field id will follow the type header as a zigzag varint. - /// - public override void WriteFieldBegin(TField field) - { - if (field.Type == TType.Bool) - { - // we want to possibly include the value, so we'll wait. - booleanField_ = field; - } - else - { - WriteFieldBeginInternal(field, 0xFF); - } - } - - /// - /// The workhorse of WriteFieldBegin. It has the option of doing a - /// 'type override' of the type header. This is used specifically in the - /// boolean field case. - /// - private void WriteFieldBeginInternal(TField field, byte typeOverride) - { - // short lastField = lastField_.Pop(); - - // if there's a type override, use that. - byte typeToWrite = typeOverride == 0xFF ? getCompactType(field.Type) : typeOverride; - - // check if we can use delta encoding for the field id - if (field.ID > lastFieldId_ && field.ID - lastFieldId_ <= 15) - { - // Write them together - WriteByteDirect((field.ID - lastFieldId_) << 4 | typeToWrite); - } - else - { - // Write them separate - WriteByteDirect(typeToWrite); - WriteI16(field.ID); - } - - lastFieldId_ = field.ID; - // lastField_.push(field.id); - } - - /// - /// Write the STOP symbol so we know there are no more fields in this struct. - /// - public override void WriteFieldStop() - { - WriteByteDirect(Types.STOP); - } - - /// - /// Write a map header. If the map is empty, omit the key and value type - /// headers, as we don't need any additional information to skip it. - /// - public override void WriteMapBegin(TMap map) - { - if (map.Count == 0) - { - WriteByteDirect(0); - } - else - { - WriteVarint32((uint)map.Count); - WriteByteDirect(getCompactType(map.KeyType) << 4 | getCompactType(map.ValueType)); - } - } - - /// - /// Write a list header. - /// - public override void WriteListBegin(TList list) - { - WriteCollectionBegin(list.ElementType, list.Count); - } - - /// - /// Write a set header. - /// - public override void WriteSetBegin(TSet set) - { - WriteCollectionBegin(set.ElementType, set.Count); - } - - /// - /// Write a boolean value. Potentially, this could be a boolean field, in - /// which case the field header info isn't written yet. If so, decide what the - /// right type header is for the value and then Write the field header. - /// Otherwise, Write a single byte. - /// - public override void WriteBool(Boolean b) - { - if (booleanField_ != null) - { - // we haven't written the field header yet - WriteFieldBeginInternal(booleanField_.Value, b ? Types.BOOLEAN_TRUE : Types.BOOLEAN_FALSE); - booleanField_ = null; - } - else - { - // we're not part of a field, so just Write the value. - WriteByteDirect(b ? Types.BOOLEAN_TRUE : Types.BOOLEAN_FALSE); - } - } - - /// - /// Write a byte. Nothing to see here! - /// - public override void WriteByte(sbyte b) - { - WriteByteDirect((byte)b); - } - - /// - /// Write an I16 as a zigzag varint. - /// - public override void WriteI16(short i16) - { - WriteVarint32(intToZigZag(i16)); - } - - /// - /// Write an i32 as a zigzag varint. - /// - public override void WriteI32(int i32) - { - WriteVarint32(intToZigZag(i32)); - } - - /// - /// Write an i64 as a zigzag varint. - /// - public override void WriteI64(long i64) - { - WriteVarint64(longToZigzag(i64)); - } - - /// - /// Write a double to the wire as 8 bytes. - /// - public override void WriteDouble(double dub) - { - byte[] data = new byte[] { 0, 0, 0, 0, 0, 0, 0, 0 }; - fixedLongToBytes(BitConverter.DoubleToInt64Bits(dub), data, 0); - trans.Write(data); - } - - /// - /// Write a string to the wire with a varint size preceding. - /// - public override void WriteString(string str) - { - byte[] bytes = UTF8Encoding.UTF8.GetBytes(str); - WriteBinary(bytes, 0, bytes.Length); - } - - /// - /// Write a byte array, using a varint for the size. - /// - public override void WriteBinary(byte[] bin) - { - WriteBinary(bin, 0, bin.Length); - } - - private void WriteBinary(byte[] buf, int offset, int length) - { - WriteVarint32((uint)length); - trans.Write(buf, offset, length); - } - - // - // These methods are called by structs, but don't actually have any wire - // output or purpose. - // - - public override void WriteMessageEnd() { } - public override void WriteMapEnd() { } - public override void WriteListEnd() { } - public override void WriteSetEnd() { } - public override void WriteFieldEnd() { } - - // - // Internal writing methods - // - - /// - /// Abstract method for writing the start of lists and sets. List and sets on - /// the wire differ only by the type indicator. - /// - protected void WriteCollectionBegin(TType elemType, int size) - { - if (size <= 14) - { - WriteByteDirect(size << 4 | getCompactType(elemType)); - } - else - { - WriteByteDirect(0xf0 | getCompactType(elemType)); - WriteVarint32((uint)size); - } - } - - /// - /// Write an i64 as a varint. Results in 1-10 bytes on the wire. - /// - byte[] varint64out = new byte[10]; - private void WriteVarint64(ulong n) - { - int idx = 0; - while (true) - { - if ((n & ~(ulong)0x7FL) == 0) - { - varint64out[idx++] = (byte)n; - break; - } - else - { - varint64out[idx++] = ((byte)((n & 0x7F) | 0x80)); - n >>= 7; - } - } - trans.Write(varint64out, 0, idx); - } - - /// - /// Convert l into a zigzag long. This allows negative numbers to be - /// represented compactly as a varint. - /// - private ulong longToZigzag(long n) - { - return (ulong)(n << 1) ^ (ulong)(n >> 63); - } - - /// - /// Convert n into a zigzag int. This allows negative numbers to be - /// represented compactly as a varint. - /// - private uint intToZigZag(int n) - { - return (uint)(n << 1) ^ (uint)(n >> 31); - } - - /// - /// Convert a long into little-endian bytes in buf starting at off and going - /// until off+7. - /// - private void fixedLongToBytes(long n, byte[] buf, int off) - { - buf[off + 0] = (byte)(n & 0xff); - buf[off + 1] = (byte)((n >> 8) & 0xff); - buf[off + 2] = (byte)((n >> 16) & 0xff); - buf[off + 3] = (byte)((n >> 24) & 0xff); - buf[off + 4] = (byte)((n >> 32) & 0xff); - buf[off + 5] = (byte)((n >> 40) & 0xff); - buf[off + 6] = (byte)((n >> 48) & 0xff); - buf[off + 7] = (byte)((n >> 56) & 0xff); - } - - #endregion - - #region ReadMethods - - /// - /// Read a message header. - /// - public override TMessage ReadMessageBegin() - { - byte protocolId = (byte)ReadByte(); - if (protocolId != PROTOCOL_ID) - { - throw new TProtocolException("Expected protocol id " + PROTOCOL_ID.ToString("X") + " but got " + protocolId.ToString("X")); - } - byte versionAndType = (byte)ReadByte(); - byte version = (byte)(versionAndType & VERSION_MASK); - if (version != VERSION) - { - throw new TProtocolException("Expected version " + VERSION + " but got " + version); - } - byte type = (byte)((versionAndType >> TYPE_SHIFT_AMOUNT) & TYPE_BITS); - int seqid = (int)ReadVarint32(); - string messageName = ReadString(); - return new TMessage(messageName, (TMessageType)type, seqid); - } - - /// - /// Read a struct begin. There's nothing on the wire for this, but it is our - /// opportunity to push a new struct begin marker onto the field stack. - /// - public override TStruct ReadStructBegin() - { - lastField_.Push(lastFieldId_); - lastFieldId_ = 0; - return ANONYMOUS_STRUCT; - } - - /// - /// Doesn't actually consume any wire data, just removes the last field for - /// this struct from the field stack. - /// - public override void ReadStructEnd() - { - // consume the last field we Read off the wire. - lastFieldId_ = lastField_.Pop(); - } - - /// - /// Read a field header off the wire. - /// - public override TField ReadFieldBegin() - { - byte type = (byte)ReadByte(); - - // if it's a stop, then we can return immediately, as the struct is over. - if (type == Types.STOP) - { - return TSTOP; - } - - short fieldId; - - // mask off the 4 MSB of the type header. it could contain a field id delta. - short modifier = (short)((type & 0xf0) >> 4); - if (modifier == 0) - { - // not a delta. look ahead for the zigzag varint field id. - fieldId = ReadI16(); - } - else - { - // has a delta. add the delta to the last Read field id. - fieldId = (short)(lastFieldId_ + modifier); - } - - TField field = new TField("", getTType((byte)(type & 0x0f)), fieldId); - - // if this happens to be a boolean field, the value is encoded in the type - if (isBoolType(type)) - { - // save the boolean value in a special instance variable. - boolValue_ = (byte)(type & 0x0f) == Types.BOOLEAN_TRUE ? true : false; - } - - // push the new field onto the field stack so we can keep the deltas going. - lastFieldId_ = field.ID; - return field; - } - - /// - /// Read a map header off the wire. If the size is zero, skip Reading the key - /// and value type. This means that 0-length maps will yield TMaps without the - /// "correct" types. - /// - public override TMap ReadMapBegin() - { - int size = (int)ReadVarint32(); - byte keyAndValueType = size == 0 ? (byte)0 : (byte)ReadByte(); - return new TMap(getTType((byte)(keyAndValueType >> 4)), getTType((byte)(keyAndValueType & 0xf)), size); - } - - /// - /// Read a list header off the wire. If the list size is 0-14, the size will - /// be packed into the element type header. If it's a longer list, the 4 MSB - /// of the element type header will be 0xF, and a varint will follow with the - /// true size. - /// - public override TList ReadListBegin() - { - byte size_and_type = (byte)ReadByte(); - int size = (size_and_type >> 4) & 0x0f; - if (size == 15) - { - size = (int)ReadVarint32(); - } - TType type = getTType(size_and_type); - return new TList(type, size); - } - - /// - /// Read a set header off the wire. If the set size is 0-14, the size will - /// be packed into the element type header. If it's a longer set, the 4 MSB - /// of the element type header will be 0xF, and a varint will follow with the - /// true size. - /// - public override TSet ReadSetBegin() - { - return new TSet(ReadListBegin()); - } - - /// - /// Read a boolean off the wire. If this is a boolean field, the value should - /// already have been Read during ReadFieldBegin, so we'll just consume the - /// pre-stored value. Otherwise, Read a byte. - /// - public override Boolean ReadBool() - { - if (boolValue_ != null) - { - bool result = boolValue_.Value; - boolValue_ = null; - return result; - } - return ReadByte() == Types.BOOLEAN_TRUE; - } - - byte[] byteRawBuf = new byte[1]; - /// - /// Read a single byte off the wire. Nothing interesting here. - /// - public override sbyte ReadByte() - { - trans.ReadAll(byteRawBuf, 0, 1); - return (sbyte)byteRawBuf[0]; - } - - /// - /// Read an i16 from the wire as a zigzag varint. - /// - public override short ReadI16() - { - return (short)zigzagToInt(ReadVarint32()); - } - - /// - /// Read an i32 from the wire as a zigzag varint. - /// - public override int ReadI32() - { - return zigzagToInt(ReadVarint32()); - } - - /// - /// Read an i64 from the wire as a zigzag varint. - /// - public override long ReadI64() - { - return zigzagToLong(ReadVarint64()); - } - - /// - /// No magic here - just Read a double off the wire. - /// - public override double ReadDouble() - { - byte[] longBits = new byte[8]; - trans.ReadAll(longBits, 0, 8); - return BitConverter.Int64BitsToDouble(bytesToLong(longBits)); - } - - /// - /// Reads a byte[] (via ReadBinary), and then UTF-8 decodes it. - /// - public override string ReadString() - { - int length = (int)ReadVarint32(); - - if (length == 0) - { - return ""; - } - - return Encoding.UTF8.GetString(ReadBinary(length)); - } - - /// - /// Read a byte[] from the wire. - /// - public override byte[] ReadBinary() - { - int length = (int)ReadVarint32(); - if (length == 0) return new byte[0]; - - byte[] buf = new byte[length]; - trans.ReadAll(buf, 0, length); - return buf; - } - - /// - /// Read a byte[] of a known length from the wire. - /// - private byte[] ReadBinary(int length) - { - if (length == 0) return new byte[0]; - - byte[] buf = new byte[length]; - trans.ReadAll(buf, 0, length); - return buf; - } - - // - // These methods are here for the struct to call, but don't have any wire - // encoding. - // - public override void ReadMessageEnd() { } - public override void ReadFieldEnd() { } - public override void ReadMapEnd() { } - public override void ReadListEnd() { } - public override void ReadSetEnd() { } - - // - // Internal Reading methods - // - - /// - /// Read an i32 from the wire as a varint. The MSB of each byte is set - /// if there is another byte to follow. This can Read up to 5 bytes. - /// - private uint ReadVarint32() - { - uint result = 0; - int shift = 0; - while (true) - { - byte b = (byte)ReadByte(); - result |= (uint)(b & 0x7f) << shift; - if ((b & 0x80) != 0x80) break; - shift += 7; - } - return result; - } - - /// - /// Read an i64 from the wire as a proper varint. The MSB of each byte is set - /// if there is another byte to follow. This can Read up to 10 bytes. - /// - private ulong ReadVarint64() - { - int shift = 0; - ulong result = 0; - while (true) - { - byte b = (byte)ReadByte(); - result |= (ulong)(b & 0x7f) << shift; - if ((b & 0x80) != 0x80) break; - shift += 7; - } - - return result; - } - - #endregion - - // - // encoding helpers - // - - /// - /// Convert from zigzag int to int. - /// - private int zigzagToInt(uint n) - { - return (int)(n >> 1) ^ (-(int)(n & 1)); - } - - /// - /// Convert from zigzag long to long. - /// - private long zigzagToLong(ulong n) - { - return (long)(n >> 1) ^ (-(long)(n & 1)); - } - - /// - /// Note that it's important that the mask bytes are long literals, - /// otherwise they'll default to ints, and when you shift an int left 56 bits, - /// you just get a messed up int. - /// - private long bytesToLong(byte[] bytes) - { - return - ((bytes[7] & 0xffL) << 56) | - ((bytes[6] & 0xffL) << 48) | - ((bytes[5] & 0xffL) << 40) | - ((bytes[4] & 0xffL) << 32) | - ((bytes[3] & 0xffL) << 24) | - ((bytes[2] & 0xffL) << 16) | - ((bytes[1] & 0xffL) << 8) | - ((bytes[0] & 0xffL)); - } - - // - // type testing and converting - // - - private Boolean isBoolType(byte b) - { - int lowerNibble = b & 0x0f; - return lowerNibble == Types.BOOLEAN_TRUE || lowerNibble == Types.BOOLEAN_FALSE; - } - - /// - /// Given a TCompactProtocol.Types constant, convert it to its corresponding - /// TType value. - /// - private TType getTType(byte type) - { - switch ((byte)(type & 0x0f)) - { - case Types.STOP: - return TType.Stop; - case Types.BOOLEAN_FALSE: - case Types.BOOLEAN_TRUE: - return TType.Bool; - case Types.BYTE: - return TType.Byte; - case Types.I16: - return TType.I16; - case Types.I32: - return TType.I32; - case Types.I64: - return TType.I64; - case Types.DOUBLE: - return TType.Double; - case Types.BINARY: - return TType.String; - case Types.LIST: - return TType.List; - case Types.SET: - return TType.Set; - case Types.MAP: - return TType.Map; - case Types.STRUCT: - return TType.Struct; - default: - throw new TProtocolException("don't know what type: " + (byte)(type & 0x0f)); - } - } - - /// - /// Given a TType value, find the appropriate TCompactProtocol.Types constant. - /// - private byte getCompactType(TType ttype) - { - return ttypeToCompactType[(int)ttype]; - } - } -} diff --git a/lib/csharp/src/Protocol/TJSONProtocol.cs b/lib/csharp/src/Protocol/TJSONProtocol.cs deleted file mode 100644 index 9dbdea9aae0..00000000000 --- a/lib/csharp/src/Protocol/TJSONProtocol.cs +++ /dev/null @@ -1,1124 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -using System; -using System.IO; -using System.Text; -using System.Collections.Generic; - -using Thrift.Transport; -using System.Globalization; - -namespace Thrift.Protocol -{ - /// - /// JSON protocol implementation for thrift. - /// - /// This is a full-featured protocol supporting Write and Read. - /// - /// Please see the C++ class header for a detailed description of the - /// protocol's wire format. - /// - /// Adapted from the Java version. - /// - public class TJSONProtocol : TProtocol - { - /// - /// Factory for JSON protocol objects. - /// - public class Factory : TProtocolFactory - { - public TProtocol GetProtocol(TTransport trans) - { - return new TJSONProtocol(trans); - } - } - - private static byte[] COMMA = new byte[] { (byte)',' }; - private static byte[] COLON = new byte[] { (byte)':' }; - private static byte[] LBRACE = new byte[] { (byte)'{' }; - private static byte[] RBRACE = new byte[] { (byte)'}' }; - private static byte[] LBRACKET = new byte[] { (byte)'[' }; - private static byte[] RBRACKET = new byte[] { (byte)']' }; - private static byte[] QUOTE = new byte[] { (byte)'"' }; - private static byte[] BACKSLASH = new byte[] { (byte)'\\' }; - - private byte[] ESCSEQ = new byte[] { (byte)'\\', (byte)'u', (byte)'0', (byte)'0' }; - - private const long VERSION = 1; - private byte[] JSON_CHAR_TABLE = { - 0, 0, 0, 0, 0, 0, 0, 0,(byte)'b',(byte)'t',(byte)'n', 0,(byte)'f',(byte)'r', 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 1, 1,(byte)'"', 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - }; - - private char[] ESCAPE_CHARS = "\"\\/bfnrt".ToCharArray(); - - private byte[] ESCAPE_CHAR_VALS = { - (byte)'"', (byte)'\\', (byte)'/', (byte)'\b', (byte)'\f', (byte)'\n', (byte)'\r', (byte)'\t', - }; - - private const int DEF_STRING_SIZE = 16; - - private static byte[] NAME_BOOL = new byte[] { (byte)'t', (byte)'f' }; - private static byte[] NAME_BYTE = new byte[] { (byte)'i', (byte)'8' }; - private static byte[] NAME_I16 = new byte[] { (byte)'i', (byte)'1', (byte)'6' }; - private static byte[] NAME_I32 = new byte[] { (byte)'i', (byte)'3', (byte)'2' }; - private static byte[] NAME_I64 = new byte[] { (byte)'i', (byte)'6', (byte)'4' }; - private static byte[] NAME_DOUBLE = new byte[] { (byte)'d', (byte)'b', (byte)'l' }; - private static byte[] NAME_STRUCT = new byte[] { (byte)'r', (byte)'e', (byte)'c' }; - private static byte[] NAME_STRING = new byte[] { (byte)'s', (byte)'t', (byte)'r' }; - private static byte[] NAME_MAP = new byte[] { (byte)'m', (byte)'a', (byte)'p' }; - private static byte[] NAME_LIST = new byte[] { (byte)'l', (byte)'s', (byte)'t' }; - private static byte[] NAME_SET = new byte[] { (byte)'s', (byte)'e', (byte)'t' }; - - private static byte[] GetTypeNameForTypeID(TType typeID) - { - switch (typeID) - { - case TType.Bool: - return NAME_BOOL; - case TType.Byte: - return NAME_BYTE; - case TType.I16: - return NAME_I16; - case TType.I32: - return NAME_I32; - case TType.I64: - return NAME_I64; - case TType.Double: - return NAME_DOUBLE; - case TType.String: - return NAME_STRING; - case TType.Struct: - return NAME_STRUCT; - case TType.Map: - return NAME_MAP; - case TType.Set: - return NAME_SET; - case TType.List: - return NAME_LIST; - default: - throw new TProtocolException(TProtocolException.NOT_IMPLEMENTED, - "Unrecognized type"); - } - } - - private static TType GetTypeIDForTypeName(byte[] name) - { - TType result = TType.Stop; - if (name.Length > 1) - { - switch (name[0]) - { - case (byte)'d': - result = TType.Double; - break; - case (byte)'i': - switch (name[1]) - { - case (byte)'8': - result = TType.Byte; - break; - case (byte)'1': - result = TType.I16; - break; - case (byte)'3': - result = TType.I32; - break; - case (byte)'6': - result = TType.I64; - break; - } - break; - case (byte)'l': - result = TType.List; - break; - case (byte)'m': - result = TType.Map; - break; - case (byte)'r': - result = TType.Struct; - break; - case (byte)'s': - if (name[1] == (byte)'t') - { - result = TType.String; - } - else if (name[1] == (byte)'e') - { - result = TType.Set; - } - break; - case (byte)'t': - result = TType.Bool; - break; - } - } - if (result == TType.Stop) - { - throw new TProtocolException(TProtocolException.NOT_IMPLEMENTED, - "Unrecognized type"); - } - return result; - } - - /// - /// Base class for tracking JSON contexts that may require - /// inserting/Reading additional JSON syntax characters - /// This base context does nothing. - /// - protected class JSONBaseContext - { - protected TJSONProtocol proto; - - public JSONBaseContext(TJSONProtocol proto) - { - this.proto = proto; - } - - public virtual void Write() { } - - public virtual void Read() { } - - public virtual bool EscapeNumbers() { return false; } - } - - /// - /// Context for JSON lists. Will insert/Read commas before each item except - /// for the first one - /// - protected class JSONListContext : JSONBaseContext - { - public JSONListContext(TJSONProtocol protocol) - : base(protocol) - { - - } - - private bool first = true; - - public override void Write() - { - if (first) - { - first = false; - } - else - { - proto.trans.Write(COMMA); - } - } - - public override void Read() - { - if (first) - { - first = false; - } - else - { - proto.ReadJSONSyntaxChar(COMMA); - } - } - } - - /// - /// Context for JSON records. Will insert/Read colons before the value portion - /// of each record pair, and commas before each key except the first. In - /// addition, will indicate that numbers in the key position need to be - /// escaped in quotes (since JSON keys must be strings). - /// - protected class JSONPairContext : JSONBaseContext - { - public JSONPairContext(TJSONProtocol proto) - : base(proto) - { - - } - - private bool first = true; - private bool colon = true; - - public override void Write() - { - if (first) - { - first = false; - colon = true; - } - else - { - proto.trans.Write(colon ? COLON : COMMA); - colon = !colon; - } - } - - public override void Read() - { - if (first) - { - first = false; - colon = true; - } - else - { - proto.ReadJSONSyntaxChar(colon ? COLON : COMMA); - colon = !colon; - } - } - - public override bool EscapeNumbers() - { - return colon; - } - } - - /// - /// Holds up to one byte from the transport - /// - protected class LookaheadReader - { - protected TJSONProtocol proto; - - public LookaheadReader(TJSONProtocol proto) - { - this.proto = proto; - } - - private bool hasData; - private byte[] data = new byte[1]; - - /// - /// Return and consume the next byte to be Read, either taking it from the - /// data buffer if present or getting it from the transport otherwise. - /// - public byte Read() - { - if (hasData) - { - hasData = false; - } - else - { - proto.trans.ReadAll(data, 0, 1); - } - return data[0]; - } - - /// - /// Return the next byte to be Read without consuming, filling the data - /// buffer if it has not been filled alReady. - /// - public byte Peek() - { - if (!hasData) - { - proto.trans.ReadAll(data, 0, 1); - } - hasData = true; - return data[0]; - } - } - - // Default encoding - protected Encoding utf8Encoding = UTF8Encoding.UTF8; - - // Stack of nested contexts that we may be in - protected Stack contextStack = new Stack(); - - // Current context that we are in - protected JSONBaseContext context; - - // Reader that manages a 1-byte buffer - protected LookaheadReader reader; - - /// - /// Push a new JSON context onto the stack. - /// - protected void PushContext(JSONBaseContext c) - { - contextStack.Push(context); - context = c; - } - - /// - /// Pop the last JSON context off the stack - /// - protected void PopContext() - { - context = contextStack.Pop(); - } - - /// - /// TJSONProtocol Constructor - /// - public TJSONProtocol(TTransport trans) - : base(trans) - { - context = new JSONBaseContext(this); - reader = new LookaheadReader(this); - } - - // Temporary buffer used by several methods - private byte[] tempBuffer = new byte[4]; - - /// - /// Read a byte that must match b[0]; otherwise an exception is thrown. - /// Marked protected to avoid synthetic accessor in JSONListContext.Read - /// and JSONPairContext.Read - /// - protected void ReadJSONSyntaxChar(byte[] b) - { - byte ch = reader.Read(); - if (ch != b[0]) - { - throw new TProtocolException(TProtocolException.INVALID_DATA, - "Unexpected character:" + (char)ch); - } - } - - /// - /// Convert a byte containing a hex char ('0'-'9' or 'a'-'f') into its - /// corresponding hex value - /// - private static byte HexVal(byte ch) - { - if ((ch >= '0') && (ch <= '9')) - { - return (byte)((char)ch - '0'); - } - else if ((ch >= 'a') && (ch <= 'f')) - { - ch += 10; - return (byte)((char)ch - 'a'); - } - else - { - throw new TProtocolException(TProtocolException.INVALID_DATA, - "Expected hex character"); - } - } - - /// - /// Convert a byte containing a hex value to its corresponding hex character - /// - private static byte HexChar(byte val) - { - val &= 0x0F; - if (val < 10) - { - return (byte)((char)val + '0'); - } - else - { - val -= 10; - return (byte)((char)val + 'a'); - } - } - - /// - /// Write the bytes in array buf as a JSON characters, escaping as needed - /// - private void WriteJSONString(byte[] b) - { - context.Write(); - trans.Write(QUOTE); - int len = b.Length; - for (int i = 0; i < len; i++) - { - if ((b[i] & 0x00FF) >= 0x30) - { - if (b[i] == BACKSLASH[0]) - { - trans.Write(BACKSLASH); - trans.Write(BACKSLASH); - } - else - { - trans.Write(b, i, 1); - } - } - else - { - tempBuffer[0] = JSON_CHAR_TABLE[b[i]]; - if (tempBuffer[0] == 1) - { - trans.Write(b, i, 1); - } - else if (tempBuffer[0] > 1) - { - trans.Write(BACKSLASH); - trans.Write(tempBuffer, 0, 1); - } - else - { - trans.Write(ESCSEQ); - tempBuffer[0] = HexChar((byte)(b[i] >> 4)); - tempBuffer[1] = HexChar(b[i]); - trans.Write(tempBuffer, 0, 2); - } - } - } - trans.Write(QUOTE); - } - - /// - /// Write out number as a JSON value. If the context dictates so, it will be - /// wrapped in quotes to output as a JSON string. - /// - private void WriteJSONInteger(long num) - { - context.Write(); - string str = num.ToString(); - - bool escapeNum = context.EscapeNumbers(); - if (escapeNum) - trans.Write(QUOTE); - - trans.Write(utf8Encoding.GetBytes(str)); - - if (escapeNum) - trans.Write(QUOTE); - } - - /// - /// Write out a double as a JSON value. If it is NaN or infinity or if the - /// context dictates escaping, Write out as JSON string. - /// - private void WriteJSONDouble(double num) - { - context.Write(); - string str = num.ToString("G17", CultureInfo.InvariantCulture); - bool special = false; - - switch (str[0]) - { - case 'N': // NaN - case 'I': // Infinity - special = true; - break; - case '-': - if (str[1] == 'I') - { // -Infinity - special = true; - } - break; - } - - bool escapeNum = special || context.EscapeNumbers(); - - if (escapeNum) - trans.Write(QUOTE); - - trans.Write(utf8Encoding.GetBytes(str)); - - if (escapeNum) - trans.Write(QUOTE); - } - /// - /// Write out contents of byte array b as a JSON string with base-64 encoded - /// data - /// - private void WriteJSONBase64(byte[] b) - { - context.Write(); - trans.Write(QUOTE); - - int len = b.Length; - int off = 0; - - while (len >= 3) - { - // Encode 3 bytes at a time - TBase64Utils.encode(b, off, 3, tempBuffer, 0); - trans.Write(tempBuffer, 0, 4); - off += 3; - len -= 3; - } - if (len > 0) - { - // Encode remainder - TBase64Utils.encode(b, off, len, tempBuffer, 0); - trans.Write(tempBuffer, 0, len + 1); - } - - trans.Write(QUOTE); - } - - private void WriteJSONObjectStart() - { - context.Write(); - trans.Write(LBRACE); - PushContext(new JSONPairContext(this)); - } - - private void WriteJSONObjectEnd() - { - PopContext(); - trans.Write(RBRACE); - } - - private void WriteJSONArrayStart() - { - context.Write(); - trans.Write(LBRACKET); - PushContext(new JSONListContext(this)); - } - - private void WriteJSONArrayEnd() - { - PopContext(); - trans.Write(RBRACKET); - } - - public override void WriteMessageBegin(TMessage message) - { - WriteJSONArrayStart(); - WriteJSONInteger(VERSION); - - byte[] b = utf8Encoding.GetBytes(message.Name); - WriteJSONString(b); - - WriteJSONInteger((long)message.Type); - WriteJSONInteger(message.SeqID); - } - - public override void WriteMessageEnd() - { - WriteJSONArrayEnd(); - } - - public override void WriteStructBegin(TStruct str) - { - WriteJSONObjectStart(); - } - - public override void WriteStructEnd() - { - WriteJSONObjectEnd(); - } - - public override void WriteFieldBegin(TField field) - { - WriteJSONInteger(field.ID); - WriteJSONObjectStart(); - WriteJSONString(GetTypeNameForTypeID(field.Type)); - } - - public override void WriteFieldEnd() - { - WriteJSONObjectEnd(); - } - - public override void WriteFieldStop() { } - - public override void WriteMapBegin(TMap map) - { - WriteJSONArrayStart(); - WriteJSONString(GetTypeNameForTypeID(map.KeyType)); - WriteJSONString(GetTypeNameForTypeID(map.ValueType)); - WriteJSONInteger(map.Count); - WriteJSONObjectStart(); - } - - public override void WriteMapEnd() - { - WriteJSONObjectEnd(); - WriteJSONArrayEnd(); - } - - public override void WriteListBegin(TList list) - { - WriteJSONArrayStart(); - WriteJSONString(GetTypeNameForTypeID(list.ElementType)); - WriteJSONInteger(list.Count); - } - - public override void WriteListEnd() - { - WriteJSONArrayEnd(); - } - - public override void WriteSetBegin(TSet set) - { - WriteJSONArrayStart(); - WriteJSONString(GetTypeNameForTypeID(set.ElementType)); - WriteJSONInteger(set.Count); - } - - public override void WriteSetEnd() - { - WriteJSONArrayEnd(); - } - - public override void WriteBool(bool b) - { - WriteJSONInteger(b ? (long)1 : (long)0); - } - - public override void WriteByte(sbyte b) - { - WriteJSONInteger((long)b); - } - - public override void WriteI16(short i16) - { - WriteJSONInteger((long)i16); - } - - public override void WriteI32(int i32) - { - WriteJSONInteger((long)i32); - } - - public override void WriteI64(long i64) - { - WriteJSONInteger(i64); - } - - public override void WriteDouble(double dub) - { - WriteJSONDouble(dub); - } - - public override void WriteString(string str) - { - byte[] b = utf8Encoding.GetBytes(str); - WriteJSONString(b); - } - - public override void WriteBinary(byte[] bin) - { - WriteJSONBase64(bin); - } - - /** - * Reading methods. - */ - - /// - /// Read in a JSON string, unescaping as appropriate.. Skip Reading from the - /// context if skipContext is true. - /// - private byte[] ReadJSONString(bool skipContext) - { - MemoryStream buffer = new MemoryStream(); - List codeunits = new List(); - - - if (!skipContext) - { - context.Read(); - } - ReadJSONSyntaxChar(QUOTE); - while (true) - { - byte ch = reader.Read(); - if (ch == QUOTE[0]) - { - break; - } - - // escaped? - if (ch != ESCSEQ[0]) - { - buffer.Write(new byte[] { (byte)ch }, 0, 1); - continue; - } - - // distinguish between \uXXXX and \? - ch = reader.Read(); - if (ch != ESCSEQ[1]) // control chars like \n - { - int off = Array.IndexOf(ESCAPE_CHARS, (char)ch); - if (off == -1) - { - throw new TProtocolException(TProtocolException.INVALID_DATA, - "Expected control char"); - } - ch = ESCAPE_CHAR_VALS[off]; - buffer.Write(new byte[] { (byte)ch }, 0, 1); - continue; - } - - - // it's \uXXXX - trans.ReadAll(tempBuffer, 0, 4); - var wch = (short)((HexVal((byte)tempBuffer[0]) << 12) + - (HexVal((byte)tempBuffer[1]) << 8) + - (HexVal((byte)tempBuffer[2]) << 4) + - HexVal(tempBuffer[3])); - if (Char.IsHighSurrogate((char)wch)) - { - if (codeunits.Count > 0) - { - throw new TProtocolException(TProtocolException.INVALID_DATA, - "Expected low surrogate char"); - } - codeunits.Add((char)wch); - } - else if (Char.IsLowSurrogate((char)wch)) - { - if (codeunits.Count == 0) - { - throw new TProtocolException(TProtocolException.INVALID_DATA, - "Expected high surrogate char"); - } - codeunits.Add((char)wch); - var tmp = utf8Encoding.GetBytes(codeunits.ToArray()); - buffer.Write(tmp, 0, tmp.Length); - codeunits.Clear(); - } - else - { - var tmp = utf8Encoding.GetBytes(new char[] { (char)wch }); - buffer.Write(tmp, 0, tmp.Length); - } - } - - - if (codeunits.Count > 0) - { - throw new TProtocolException(TProtocolException.INVALID_DATA, - "Expected low surrogate char"); - } - - return buffer.ToArray(); - } - - /// - /// Return true if the given byte could be a valid part of a JSON number. - /// - private bool IsJSONNumeric(byte b) - { - switch (b) - { - case (byte)'+': - case (byte)'-': - case (byte)'.': - case (byte)'0': - case (byte)'1': - case (byte)'2': - case (byte)'3': - case (byte)'4': - case (byte)'5': - case (byte)'6': - case (byte)'7': - case (byte)'8': - case (byte)'9': - case (byte)'E': - case (byte)'e': - return true; - } - return false; - } - - /// - /// Read in a sequence of characters that are all valid in JSON numbers. Does - /// not do a complete regex check to validate that this is actually a number. - /// - private string ReadJSONNumericChars() - { - StringBuilder strbld = new StringBuilder(); - while (true) - { - byte ch = reader.Peek(); - if (!IsJSONNumeric(ch)) - { - break; - } - strbld.Append((char)reader.Read()); - } - return strbld.ToString(); - } - - /// - /// Read in a JSON number. If the context dictates, Read in enclosing quotes. - /// - private long ReadJSONInteger() - { - context.Read(); - if (context.EscapeNumbers()) - { - ReadJSONSyntaxChar(QUOTE); - } - - string str = ReadJSONNumericChars(); - if (context.EscapeNumbers()) - { - ReadJSONSyntaxChar(QUOTE); - } - - try - { - return Int64.Parse(str); - } - catch (FormatException fex) - { - throw new TProtocolException(TProtocolException.INVALID_DATA, - "Bad data encounted in numeric data", fex); - } - } - - /// - /// Read in a JSON double value. Throw if the value is not wrapped in quotes - /// when expected or if wrapped in quotes when not expected. - /// - private double ReadJSONDouble() - { - context.Read(); - if (reader.Peek() == QUOTE[0]) - { - byte[] arr = ReadJSONString(true); - double dub = Double.Parse(utf8Encoding.GetString(arr, 0, arr.Length), CultureInfo.InvariantCulture); - - if (!context.EscapeNumbers() && !Double.IsNaN(dub) && !Double.IsInfinity(dub)) - { - // Throw exception -- we should not be in a string in this case - throw new TProtocolException(TProtocolException.INVALID_DATA, - "Numeric data unexpectedly quoted"); - } - return dub; - } - else - { - if (context.EscapeNumbers()) - { - // This will throw - we should have had a quote if escapeNum == true - ReadJSONSyntaxChar(QUOTE); - } - try - { - return Double.Parse(ReadJSONNumericChars(), CultureInfo.InvariantCulture); - } - catch (FormatException fex) - { - throw new TProtocolException(TProtocolException.INVALID_DATA, - "Bad data encounted in numeric data", fex); - } - } - } - - /// - /// Read in a JSON string containing base-64 encoded data and decode it. - /// - private byte[] ReadJSONBase64() - { - byte[] b = ReadJSONString(false); - int len = b.Length; - int off = 0; - int size = 0; - // reduce len to ignore fill bytes - while ((len > 0) && (b[len - 1] == '=')) - { - --len; - } - // read & decode full byte triplets = 4 source bytes - while (len > 4) - { - // Decode 4 bytes at a time - TBase64Utils.decode(b, off, 4, b, size); // NB: decoded in place - off += 4; - len -= 4; - size += 3; - } - // Don't decode if we hit the end or got a single leftover byte (invalid - // base64 but legal for skip of regular string type) - if (len > 1) - { - // Decode remainder - TBase64Utils.decode(b, off, len, b, size); // NB: decoded in place - size += len - 1; - } - // Sadly we must copy the byte[] (any way around this?) - byte[] result = new byte[size]; - Array.Copy(b, 0, result, 0, size); - return result; - } - - private void ReadJSONObjectStart() - { - context.Read(); - ReadJSONSyntaxChar(LBRACE); - PushContext(new JSONPairContext(this)); - } - - private void ReadJSONObjectEnd() - { - ReadJSONSyntaxChar(RBRACE); - PopContext(); - } - - private void ReadJSONArrayStart() - { - context.Read(); - ReadJSONSyntaxChar(LBRACKET); - PushContext(new JSONListContext(this)); - } - - private void ReadJSONArrayEnd() - { - ReadJSONSyntaxChar(RBRACKET); - PopContext(); - } - - public override TMessage ReadMessageBegin() - { - TMessage message = new TMessage(); - ReadJSONArrayStart(); - if (ReadJSONInteger() != VERSION) - { - throw new TProtocolException(TProtocolException.BAD_VERSION, - "Message contained bad version."); - } - - var buf = ReadJSONString(false); - message.Name = utf8Encoding.GetString(buf, 0, buf.Length); - message.Type = (TMessageType)ReadJSONInteger(); - message.SeqID = (int)ReadJSONInteger(); - return message; - } - - public override void ReadMessageEnd() - { - ReadJSONArrayEnd(); - } - - public override TStruct ReadStructBegin() - { - ReadJSONObjectStart(); - return new TStruct(); - } - - public override void ReadStructEnd() - { - ReadJSONObjectEnd(); - } - - public override TField ReadFieldBegin() - { - TField field = new TField(); - byte ch = reader.Peek(); - if (ch == RBRACE[0]) - { - field.Type = TType.Stop; - } - else - { - field.ID = (short)ReadJSONInteger(); - ReadJSONObjectStart(); - field.Type = GetTypeIDForTypeName(ReadJSONString(false)); - } - return field; - } - - public override void ReadFieldEnd() - { - ReadJSONObjectEnd(); - } - - public override TMap ReadMapBegin() - { - TMap map = new TMap(); - ReadJSONArrayStart(); - map.KeyType = GetTypeIDForTypeName(ReadJSONString(false)); - map.ValueType = GetTypeIDForTypeName(ReadJSONString(false)); - map.Count = (int)ReadJSONInteger(); - ReadJSONObjectStart(); - return map; - } - - public override void ReadMapEnd() - { - ReadJSONObjectEnd(); - ReadJSONArrayEnd(); - } - - public override TList ReadListBegin() - { - TList list = new TList(); - ReadJSONArrayStart(); - list.ElementType = GetTypeIDForTypeName(ReadJSONString(false)); - list.Count = (int)ReadJSONInteger(); - return list; - } - - public override void ReadListEnd() - { - ReadJSONArrayEnd(); - } - - public override TSet ReadSetBegin() - { - TSet set = new TSet(); - ReadJSONArrayStart(); - set.ElementType = GetTypeIDForTypeName(ReadJSONString(false)); - set.Count = (int)ReadJSONInteger(); - return set; - } - - public override void ReadSetEnd() - { - ReadJSONArrayEnd(); - } - - public override bool ReadBool() - { - return (ReadJSONInteger() == 0 ? false : true); - } - - public override sbyte ReadByte() - { - return (sbyte)ReadJSONInteger(); - } - - public override short ReadI16() - { - return (short)ReadJSONInteger(); - } - - public override int ReadI32() - { - return (int)ReadJSONInteger(); - } - - public override long ReadI64() - { - return (long)ReadJSONInteger(); - } - - public override double ReadDouble() - { - return ReadJSONDouble(); - } - - public override string ReadString() - { - var buf = ReadJSONString(false); - return utf8Encoding.GetString(buf, 0, buf.Length); - } - - public override byte[] ReadBinary() - { - return ReadJSONBase64(); - } - - } -} diff --git a/lib/csharp/src/Protocol/TList.cs b/lib/csharp/src/Protocol/TList.cs deleted file mode 100644 index 0c8f2144a44..00000000000 --- a/lib/csharp/src/Protocol/TList.cs +++ /dev/null @@ -1,54 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - * - * Contains some contributions under the Thrift Software License. - * Please see doc/old-thrift-license.txt in the Thrift distribution for - * details. - */ - -using System; -using System.Collections.Generic; -using System.Text; - -namespace Thrift.Protocol -{ - public struct TList - { - private TType elementType; - private int count; - - public TList(TType elementType, int count) - :this() - { - this.elementType = elementType; - this.count = count; - } - - public TType ElementType - { - get { return elementType; } - set { elementType = value; } - } - - public int Count - { - get { return count; } - set { count = value; } - } - } -} diff --git a/lib/csharp/src/Protocol/TMap.cs b/lib/csharp/src/Protocol/TMap.cs deleted file mode 100644 index aba9d3a95ec..00000000000 --- a/lib/csharp/src/Protocol/TMap.cs +++ /dev/null @@ -1,62 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - * - * Contains some contributions under the Thrift Software License. - * Please see doc/old-thrift-license.txt in the Thrift distribution for - * details. - */ - -using System; -using System.Collections.Generic; -using System.Text; - -namespace Thrift.Protocol -{ - public struct TMap - { - private TType keyType; - private TType valueType; - private int count; - - public TMap(TType keyType, TType valueType, int count) - :this() - { - this.keyType = keyType; - this.valueType = valueType; - this.count = count; - } - - public TType KeyType - { - get { return keyType; } - set { keyType = value; } - } - - public TType ValueType - { - get { return valueType; } - set { valueType = value; } - } - - public int Count - { - get { return count; } - set { count = value; } - } - } -} diff --git a/lib/csharp/src/Protocol/TMessage.cs b/lib/csharp/src/Protocol/TMessage.cs deleted file mode 100644 index 348263c375d..00000000000 --- a/lib/csharp/src/Protocol/TMessage.cs +++ /dev/null @@ -1,62 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - * - * Contains some contributions under the Thrift Software License. - * Please see doc/old-thrift-license.txt in the Thrift distribution for - * details. - */ - -using System; -using System.Collections.Generic; -using System.Text; - -namespace Thrift.Protocol -{ - public struct TMessage - { - private string name; - private TMessageType type; - private int seqID; - - public TMessage(string name, TMessageType type, int seqid) - :this() - { - this.name = name; - this.type = type; - this.seqID = seqid; - } - - public string Name - { - get { return name; } - set { name = value; } - } - - public TMessageType Type - { - get { return type; } - set { type = value; } - } - - public int SeqID - { - get { return seqID; } - set { seqID = value; } - } - } -} diff --git a/lib/csharp/src/Protocol/TMultiplexedProcessor.cs b/lib/csharp/src/Protocol/TMultiplexedProcessor.cs deleted file mode 100644 index aa91c527fed..00000000000 --- a/lib/csharp/src/Protocol/TMultiplexedProcessor.cs +++ /dev/null @@ -1,183 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - * - * Contains some contributions under the Thrift Software License. - * Please see doc/old-thrift-license.txt in the Thrift distribution for - * details. - */ - -using System; -using System.Text; -using Thrift.Transport; -using System.Collections.Generic; -using System.IO; - -namespace Thrift.Protocol -{ - /// - /// is a allowing a single - /// to provide multiple services. - /// - /// To do so, you instantiate the processor and then register additional processors with it, - /// as shown in the following example: - /// - /// - /// TMultiplexedProcessor processor = new TMultiplexedProcessor(); - /// - /// processor.registerProcessor( - /// "Calculator", - /// new Calculator.Processor(new CalculatorHandler())); - /// - /// processor.registerProcessor( - /// "WeatherReport", - /// new WeatherReport.Processor(new WeatherReportHandler())); - /// - /// TServerTransport t = new TServerSocket(9090); - /// TSimpleServer server = new TSimpleServer(processor, t); - /// - /// server.serve(); - /// - /// - public class TMultiplexedProcessor : TProcessor - { - private Dictionary ServiceProcessorMap = new Dictionary(); - - /// - /// 'Register' a service with this TMultiplexedProcessor. This allows us to broker - /// requests to individual services by using the service name to select them at request time. - /// - /// Args: - /// - serviceName Name of a service, has to be identical to the name - /// declared in the Thrift IDL, e.g. "WeatherReport". - /// - processor Implementation of a service, usually referred to as "handlers", - /// e.g. WeatherReportHandler implementing WeatherReport.Iface. - /// - public void RegisterProcessor(string serviceName, TProcessor processor) - { - ServiceProcessorMap.Add(serviceName, processor); - } - - - private void Fail(TProtocol oprot, TMessage message, TApplicationException.ExceptionType extype, string etxt) - { - TApplicationException appex = new TApplicationException(extype, etxt); - - TMessage newMessage = new TMessage(message.Name, TMessageType.Exception, message.SeqID); - - oprot.WriteMessageBegin(newMessage); - appex.Write(oprot); - oprot.WriteMessageEnd(); - oprot.Transport.Flush(); - } - - - /// - /// This implementation of process performs the following steps: - /// - /// - Read the beginning of the message. - /// - Extract the service name from the message. - /// - Using the service name to locate the appropriate processor. - /// - Dispatch to the processor, with a decorated instance of TProtocol - /// that allows readMessageBegin() to return the original TMessage. - /// - /// Throws an exception if - /// - the message type is not CALL or ONEWAY, - /// - the service name was not found in the message, or - /// - the service name has not been RegisterProcessor()ed. - /// - public bool Process(TProtocol iprot, TProtocol oprot) - { - /* Use the actual underlying protocol (e.g. TBinaryProtocol) to read the - message header. This pulls the message "off the wire", which we'll - deal with at the end of this method. */ - - try - { - TMessage message = iprot.ReadMessageBegin(); - - if ((message.Type != TMessageType.Call) && (message.Type != TMessageType.Oneway)) - { - Fail(oprot, message, - TApplicationException.ExceptionType.InvalidMessageType, - "Message type CALL or ONEWAY expected"); - return false; - } - - // Extract the service name - int index = message.Name.IndexOf(TMultiplexedProtocol.SEPARATOR); - if (index < 0) - { - Fail(oprot, message, - TApplicationException.ExceptionType.InvalidProtocol, - "Service name not found in message name: " + message.Name + ". " + - "Did you forget to use a TMultiplexProtocol in your client?"); - return false; - } - - // Create a new TMessage, something that can be consumed by any TProtocol - string serviceName = message.Name.Substring(0, index); - TProcessor actualProcessor; - if (!ServiceProcessorMap.TryGetValue(serviceName, out actualProcessor)) - { - Fail(oprot, message, - TApplicationException.ExceptionType.InternalError, - "Service name not found: " + serviceName + ". " + - "Did you forget to call RegisterProcessor()?"); - return false; - } - - // Create a new TMessage, removing the service name - TMessage newMessage = new TMessage( - message.Name.Substring(serviceName.Length + TMultiplexedProtocol.SEPARATOR.Length), - message.Type, - message.SeqID); - - // Dispatch processing to the stored processor - return actualProcessor.Process(new StoredMessageProtocol(iprot, newMessage), oprot); - - } - catch (IOException) - { - return false; // similar to all other processors - } - - } - - /// - /// Our goal was to work with any protocol. In order to do that, we needed - /// to allow them to call readMessageBegin() and get a TMessage in exactly - /// the standard format, without the service name prepended to TMessage.name. - /// - private class StoredMessageProtocol : TProtocolDecorator - { - TMessage MsgBegin; - - public StoredMessageProtocol(TProtocol protocol, TMessage messageBegin) - : base(protocol) - { - this.MsgBegin = messageBegin; - } - - public override TMessage ReadMessageBegin() - { - return MsgBegin; - } - } - - } -} diff --git a/lib/csharp/src/Protocol/TMultiplexedProtocol.cs b/lib/csharp/src/Protocol/TMultiplexedProtocol.cs deleted file mode 100644 index 1bd420fcc20..00000000000 --- a/lib/csharp/src/Protocol/TMultiplexedProtocol.cs +++ /dev/null @@ -1,103 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - * - * Contains some contributions under the Thrift Software License. - * Please see doc/old-thrift-license.txt in the Thrift distribution for - * details. - */ - -using System; -using System.Text; -using Thrift.Transport; -using System.Collections.Generic; - -namespace Thrift.Protocol -{ - - /// - /// TMultiplexedProtocol is a protocol-independent concrete decorator that allows a Thrift - /// client to communicate with a multiplexing Thrift server, by prepending the service name - /// to the function name during function calls. - /// - /// NOTE: THIS IS NOT TO BE USED BY SERVERS. - /// On the server, use TMultiplexedProcessor to handle requests from a multiplexing client. - /// - /// This example uses a single socket transport to invoke two services: - /// - /// TSocket transport = new TSocket("localhost", 9090); - /// transport.open(); - /// - /// TBinaryProtocol protocol = new TBinaryProtocol(transport); - /// - /// TMultiplexedProtocol mp = new TMultiplexedProtocol(protocol, "Calculator"); - /// Calculator.Client service = new Calculator.Client(mp); - /// - /// TMultiplexedProtocol mp2 = new TMultiplexedProtocol(protocol, "WeatherReport"); - /// WeatherReport.Client service2 = new WeatherReport.Client(mp2); - /// - /// System.out.println(service.add(2,2)); - /// System.out.println(service2.getTemperature()); - /// - /// - public class TMultiplexedProtocol : TProtocolDecorator - { - - /// - /// Used to delimit the service name from the function name. - /// - public static string SEPARATOR = ":"; - - private string ServiceName; - - /// - /// Wrap the specified protocol, allowing it to be used to communicate with a - /// multiplexing server. The is required as it is - /// prepended to the message header so that the multiplexing server can broker - /// the function call to the proper service. - /// - /// Your communication protocol of choice, e.g. . - /// The service name of the service communicating via this protocol. - public TMultiplexedProtocol(TProtocol protocol, string serviceName) - : base(protocol) - { - ServiceName = serviceName; - } - - /// - /// Prepends the service name to the function name, separated by TMultiplexedProtocol.SEPARATOR. - /// - /// The original message. - public override void WriteMessageBegin(TMessage tMessage) - { - switch (tMessage.Type) - { - case TMessageType.Call: - case TMessageType.Oneway: - base.WriteMessageBegin(new TMessage( - ServiceName + SEPARATOR + tMessage.Name, - tMessage.Type, - tMessage.SeqID)); - break; - - default: - base.WriteMessageBegin(tMessage); - break; - } - } - } -} diff --git a/lib/csharp/src/Protocol/TProtocol.cs b/lib/csharp/src/Protocol/TProtocol.cs deleted file mode 100644 index dd7a6e06277..00000000000 --- a/lib/csharp/src/Protocol/TProtocol.cs +++ /dev/null @@ -1,142 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - * - * Contains some contributions under the Thrift Software License. - * Please see doc/old-thrift-license.txt in the Thrift distribution for - * details. - */ - -using System; -using System.Text; -using Thrift.Transport; - -namespace Thrift.Protocol -{ - public abstract class TProtocol : IDisposable - { - private const int DEFAULT_RECURSION_DEPTH = 64; - - protected TTransport trans; - protected int recursionLimit; - protected int recursionDepth; - - protected TProtocol(TTransport trans) - { - this.trans = trans; - this.recursionLimit = DEFAULT_RECURSION_DEPTH; - this.recursionDepth = 0; - } - - public TTransport Transport - { - get { return trans; } - } - - public int RecursionLimit - { - get { return recursionLimit; } - set { recursionLimit = value; } - } - - public void IncrementRecursionDepth() - { - if (recursionDepth < recursionLimit) - ++recursionDepth; - else - throw new TProtocolException(TProtocolException.DEPTH_LIMIT, "Depth limit exceeded"); - } - - public void DecrementRecursionDepth() - { - --recursionDepth; - } - - #region " IDisposable Support " - private bool _IsDisposed; - - // IDisposable - public void Dispose() - { - Dispose(true); - } - - protected virtual void Dispose(bool disposing) - { - if (!_IsDisposed) - { - if (disposing) - { - if (trans is IDisposable) - (trans as IDisposable).Dispose(); - } - } - _IsDisposed = true; - } - #endregion - - public abstract void WriteMessageBegin(TMessage message); - public abstract void WriteMessageEnd(); - public abstract void WriteStructBegin(TStruct struc); - public abstract void WriteStructEnd(); - public abstract void WriteFieldBegin(TField field); - public abstract void WriteFieldEnd(); - public abstract void WriteFieldStop(); - public abstract void WriteMapBegin(TMap map); - public abstract void WriteMapEnd(); - public abstract void WriteListBegin(TList list); - public abstract void WriteListEnd(); - public abstract void WriteSetBegin(TSet set); - public abstract void WriteSetEnd(); - public abstract void WriteBool(bool b); - public abstract void WriteByte(sbyte b); - public abstract void WriteI16(short i16); - public abstract void WriteI32(int i32); - public abstract void WriteI64(long i64); - public abstract void WriteDouble(double d); - public virtual void WriteString(string s) - { - WriteBinary(Encoding.UTF8.GetBytes(s)); - } - public abstract void WriteBinary(byte[] b); - - public abstract TMessage ReadMessageBegin(); - public abstract void ReadMessageEnd(); - public abstract TStruct ReadStructBegin(); - public abstract void ReadStructEnd(); - public abstract TField ReadFieldBegin(); - public abstract void ReadFieldEnd(); - public abstract TMap ReadMapBegin(); - public abstract void ReadMapEnd(); - public abstract TList ReadListBegin(); - public abstract void ReadListEnd(); - public abstract TSet ReadSetBegin(); - public abstract void ReadSetEnd(); - public abstract bool ReadBool(); - public abstract sbyte ReadByte(); - public abstract short ReadI16(); - public abstract int ReadI32(); - public abstract long ReadI64(); - public abstract double ReadDouble(); - public virtual string ReadString() - { - var buf = ReadBinary(); - return Encoding.UTF8.GetString(buf, 0, buf.Length); - } - public abstract byte[] ReadBinary(); - } -} diff --git a/lib/csharp/src/Protocol/TProtocolDecorator.cs b/lib/csharp/src/Protocol/TProtocolDecorator.cs deleted file mode 100644 index 86000027f51..00000000000 --- a/lib/csharp/src/Protocol/TProtocolDecorator.cs +++ /dev/null @@ -1,261 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - * - * Contains some contributions under the Thrift Software License. - * Please see doc/old-thrift-license.txt in the Thrift distribution for - * details. - */ - -using System; -using System.Text; -using Thrift.Transport; -using System.Collections.Generic; - -namespace Thrift.Protocol -{ - /// - /// forwards all requests to an enclosed instance, - /// providing a way to author concise concrete decorator subclasses. While it has - /// no abstract methods, it is marked abstract as a reminder that by itself, - /// it does not modify the behaviour of the enclosed . - /// - /// See p.175 of Design Patterns (by Gamma et al.) - /// - /// - public abstract class TProtocolDecorator : TProtocol - { - private TProtocol WrappedProtocol; - - /// - /// Encloses the specified protocol. - /// - /// All operations will be forward to this protocol. Must be non-null. - public TProtocolDecorator(TProtocol protocol) - : base(protocol.Transport) - { - - WrappedProtocol = protocol; - } - - public override void WriteMessageBegin(TMessage tMessage) - { - WrappedProtocol.WriteMessageBegin(tMessage); - } - - public override void WriteMessageEnd() - { - WrappedProtocol.WriteMessageEnd(); - } - - public override void WriteStructBegin(TStruct tStruct) - { - WrappedProtocol.WriteStructBegin(tStruct); - } - - public override void WriteStructEnd() - { - WrappedProtocol.WriteStructEnd(); - } - - public override void WriteFieldBegin(TField tField) - { - WrappedProtocol.WriteFieldBegin(tField); - } - - public override void WriteFieldEnd() - { - WrappedProtocol.WriteFieldEnd(); - } - - public override void WriteFieldStop() - { - WrappedProtocol.WriteFieldStop(); - } - - public override void WriteMapBegin(TMap tMap) - { - WrappedProtocol.WriteMapBegin(tMap); - } - - public override void WriteMapEnd() - { - WrappedProtocol.WriteMapEnd(); - } - - public override void WriteListBegin(TList tList) - { - WrappedProtocol.WriteListBegin(tList); - } - - public override void WriteListEnd() - { - WrappedProtocol.WriteListEnd(); - } - - public override void WriteSetBegin(TSet tSet) - { - WrappedProtocol.WriteSetBegin(tSet); - } - - public override void WriteSetEnd() - { - WrappedProtocol.WriteSetEnd(); - } - - public override void WriteBool(bool b) - { - WrappedProtocol.WriteBool(b); - } - - public override void WriteByte(sbyte b) - { - WrappedProtocol.WriteByte(b); - } - - public override void WriteI16(short i) - { - WrappedProtocol.WriteI16(i); - } - - public override void WriteI32(int i) - { - WrappedProtocol.WriteI32(i); - } - - public override void WriteI64(long l) - { - WrappedProtocol.WriteI64(l); - } - - public override void WriteDouble(double v) - { - WrappedProtocol.WriteDouble(v); - } - - public override void WriteString(string s) - { - WrappedProtocol.WriteString(s); - } - - public override void WriteBinary(byte[] bytes) - { - WrappedProtocol.WriteBinary(bytes); - } - - public override TMessage ReadMessageBegin() - { - return WrappedProtocol.ReadMessageBegin(); - } - - public override void ReadMessageEnd() - { - WrappedProtocol.ReadMessageEnd(); - } - - public override TStruct ReadStructBegin() - { - return WrappedProtocol.ReadStructBegin(); - } - - public override void ReadStructEnd() - { - WrappedProtocol.ReadStructEnd(); - } - - public override TField ReadFieldBegin() - { - return WrappedProtocol.ReadFieldBegin(); - } - - public override void ReadFieldEnd() - { - WrappedProtocol.ReadFieldEnd(); - } - - public override TMap ReadMapBegin() - { - return WrappedProtocol.ReadMapBegin(); - } - - public override void ReadMapEnd() - { - WrappedProtocol.ReadMapEnd(); - } - - public override TList ReadListBegin() - { - return WrappedProtocol.ReadListBegin(); - } - - public override void ReadListEnd() - { - WrappedProtocol.ReadListEnd(); - } - - public override TSet ReadSetBegin() - { - return WrappedProtocol.ReadSetBegin(); - } - - public override void ReadSetEnd() - { - WrappedProtocol.ReadSetEnd(); - } - - public override bool ReadBool() - { - return WrappedProtocol.ReadBool(); - } - - public override sbyte ReadByte() - { - return WrappedProtocol.ReadByte(); - } - - public override short ReadI16() - { - return WrappedProtocol.ReadI16(); - } - - public override int ReadI32() - { - return WrappedProtocol.ReadI32(); - } - - public override long ReadI64() - { - return WrappedProtocol.ReadI64(); - } - - public override double ReadDouble() - { - return WrappedProtocol.ReadDouble(); - } - - public override string ReadString() - { - return WrappedProtocol.ReadString(); - } - - public override byte[] ReadBinary() - { - return WrappedProtocol.ReadBinary(); - } - } - -} diff --git a/lib/csharp/src/Protocol/TProtocolException.cs b/lib/csharp/src/Protocol/TProtocolException.cs deleted file mode 100644 index c0f007e0ce1..00000000000 --- a/lib/csharp/src/Protocol/TProtocolException.cs +++ /dev/null @@ -1,67 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - * - * Contains some contributions under the Thrift Software License. - * Please see doc/old-thrift-license.txt in the Thrift distribution for - * details. - */ - -using System; - -namespace Thrift.Protocol -{ - public class TProtocolException : TException - { - public const int UNKNOWN = 0; - public const int INVALID_DATA = 1; - public const int NEGATIVE_SIZE = 2; - public const int SIZE_LIMIT = 3; - public const int BAD_VERSION = 4; - public const int NOT_IMPLEMENTED = 5; - public const int DEPTH_LIMIT = 6; - - protected int type_ = UNKNOWN; - - public TProtocolException() - : base() - { - } - - public TProtocolException(int type) - : base() - { - type_ = type; - } - - public TProtocolException(int type, string message, Exception inner = null) - : base(message, inner) - { - type_ = type; - } - - public TProtocolException(string message, Exception inner = null) - : base(message, inner) - { - } - - public int getType() - { - return type_; - } - } -} diff --git a/lib/csharp/src/Protocol/TProtocolFactory.cs b/lib/csharp/src/Protocol/TProtocolFactory.cs deleted file mode 100644 index 71360a19ae9..00000000000 --- a/lib/csharp/src/Protocol/TProtocolFactory.cs +++ /dev/null @@ -1,33 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - * - * Contains some contributions under the Thrift Software License. - * Please see doc/old-thrift-license.txt in the Thrift distribution for - * details. - */ - -using System; -using Thrift.Transport; - -namespace Thrift.Protocol -{ - public interface TProtocolFactory - { - TProtocol GetProtocol(TTransport trans); - } -} diff --git a/lib/csharp/src/Protocol/TProtocolUtil.cs b/lib/csharp/src/Protocol/TProtocolUtil.cs deleted file mode 100644 index d995c6ce76b..00000000000 --- a/lib/csharp/src/Protocol/TProtocolUtil.cs +++ /dev/null @@ -1,108 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - * - * Contains some contributions under the Thrift Software License. - * Please see doc/old-thrift-license.txt in the Thrift distribution for - * details. - */ - -using System; - -namespace Thrift.Protocol -{ - public static class TProtocolUtil - { - public static void Skip(TProtocol prot, TType type) - { - prot.IncrementRecursionDepth(); - try - { - switch (type) - { - case TType.Bool: - prot.ReadBool(); - break; - case TType.Byte: - prot.ReadByte(); - break; - case TType.I16: - prot.ReadI16(); - break; - case TType.I32: - prot.ReadI32(); - break; - case TType.I64: - prot.ReadI64(); - break; - case TType.Double: - prot.ReadDouble(); - break; - case TType.String: - // Don't try to decode the string, just skip it. - prot.ReadBinary(); - break; - case TType.Struct: - prot.ReadStructBegin(); - while (true) - { - TField field = prot.ReadFieldBegin(); - if (field.Type == TType.Stop) - { - break; - } - Skip(prot, field.Type); - prot.ReadFieldEnd(); - } - prot.ReadStructEnd(); - break; - case TType.Map: - TMap map = prot.ReadMapBegin(); - for (int i = 0; i < map.Count; i++) - { - Skip(prot, map.KeyType); - Skip(prot, map.ValueType); - } - prot.ReadMapEnd(); - break; - case TType.Set: - TSet set = prot.ReadSetBegin(); - for (int i = 0; i < set.Count; i++) - { - Skip(prot, set.ElementType); - } - prot.ReadSetEnd(); - break; - case TType.List: - TList list = prot.ReadListBegin(); - for (int i = 0; i < list.Count; i++) - { - Skip(prot, list.ElementType); - } - prot.ReadListEnd(); - break; - default: - throw new TProtocolException(TProtocolException.INVALID_DATA, "Unknown data type " + type.ToString("d")); - } - } - finally - { - prot.DecrementRecursionDepth(); - } - } - } -} diff --git a/lib/csharp/src/Protocol/TSet.cs b/lib/csharp/src/Protocol/TSet.cs deleted file mode 100644 index a918ab53729..00000000000 --- a/lib/csharp/src/Protocol/TSet.cs +++ /dev/null @@ -1,59 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - * - * Contains some contributions under the Thrift Software License. - * Please see doc/old-thrift-license.txt in the Thrift distribution for - * details. - */ - -using System; -using System.Collections.Generic; -using System.Text; - -namespace Thrift.Protocol -{ - public struct TSet - { - private TType elementType; - private int count; - - public TSet(TType elementType, int count) - :this() - { - this.elementType = elementType; - this.count = count; - } - - public TSet(TList list) - : this(list.ElementType, list.Count) - { - } - - public TType ElementType - { - get { return elementType; } - set { elementType = value; } - } - - public int Count - { - get { return count; } - set { count = value; } - } - } -} diff --git a/lib/csharp/src/Protocol/TStruct.cs b/lib/csharp/src/Protocol/TStruct.cs deleted file mode 100644 index f4844a4c9d0..00000000000 --- a/lib/csharp/src/Protocol/TStruct.cs +++ /dev/null @@ -1,46 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - * - * Contains some contributions under the Thrift Software License. - * Please see doc/old-thrift-license.txt in the Thrift distribution for - * details. - */ - -using System; -using System.Collections.Generic; -using System.Text; - -namespace Thrift.Protocol -{ - public struct TStruct - { - private string name; - - public TStruct(string name) - :this() - { - this.name = name; - } - - public string Name - { - get { return name; } - set { name = value; } - } - } -} diff --git a/lib/csharp/src/Protocol/TType.cs b/lib/csharp/src/Protocol/TType.cs deleted file mode 100644 index 9ce915e0897..00000000000 --- a/lib/csharp/src/Protocol/TType.cs +++ /dev/null @@ -1,44 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - * - * Contains some contributions under the Thrift Software License. - * Please see doc/old-thrift-license.txt in the Thrift distribution for - * details. - */ - -using System; - -namespace Thrift.Protocol -{ - public enum TType : byte - { - Stop = 0, - Void = 1, - Bool = 2, - Byte = 3, - Double = 4, - I16 = 6, - I32 = 8, - I64 = 10, - String = 11, - Struct = 12, - Map = 13, - Set = 14, - List = 15 - } -} diff --git a/lib/csharp/src/Server/TServer.cs b/lib/csharp/src/Server/TServer.cs deleted file mode 100644 index 2bc04f3a045..00000000000 --- a/lib/csharp/src/Server/TServer.cs +++ /dev/null @@ -1,155 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - * - * Contains some contributions under the Thrift Software License. - * Please see doc/old-thrift-license.txt in the Thrift distribution for - * details. - */ - -using System; -using Thrift.Protocol; -using Thrift.Transport; -using System.IO; - -namespace Thrift.Server -{ - public abstract class TServer - { - //Attributes - protected TProcessorFactory processorFactory; - protected TServerTransport serverTransport; - protected TTransportFactory inputTransportFactory; - protected TTransportFactory outputTransportFactory; - protected TProtocolFactory inputProtocolFactory; - protected TProtocolFactory outputProtocolFactory; - protected TServerEventHandler serverEventHandler = null; - - //Methods - public void setEventHandler(TServerEventHandler seh) - { - serverEventHandler = seh; - } - public TServerEventHandler getEventHandler() - { - return serverEventHandler; - } - - //Log delegation - public delegate void LogDelegate(string str); - private LogDelegate _logDelegate; - protected LogDelegate logDelegate - { - get { return _logDelegate; } - set { _logDelegate = (value != null) ? value : DefaultLogDelegate; } - } - protected static void DefaultLogDelegate(string s) - { - Console.Error.WriteLine(s); - } - - //Construction - public TServer(TProcessor processor, - TServerTransport serverTransport) - : this(processor, serverTransport, - new TTransportFactory(), - new TTransportFactory(), - new TBinaryProtocol.Factory(), - new TBinaryProtocol.Factory(), - DefaultLogDelegate) - { - } - - public TServer(TProcessor processor, - TServerTransport serverTransport, - LogDelegate logDelegate) - : this(processor, - serverTransport, - new TTransportFactory(), - new TTransportFactory(), - new TBinaryProtocol.Factory(), - new TBinaryProtocol.Factory(), - logDelegate) - { - } - - public TServer(TProcessor processor, - TServerTransport serverTransport, - TTransportFactory transportFactory) - : this(processor, - serverTransport, - transportFactory, - transportFactory, - new TBinaryProtocol.Factory(), - new TBinaryProtocol.Factory(), - DefaultLogDelegate) - { - } - - public TServer(TProcessor processor, - TServerTransport serverTransport, - TTransportFactory transportFactory, - TProtocolFactory protocolFactory) - : this(processor, - serverTransport, - transportFactory, - transportFactory, - protocolFactory, - protocolFactory, - DefaultLogDelegate) - { - } - - public TServer(TProcessor processor, - TServerTransport serverTransport, - TTransportFactory inputTransportFactory, - TTransportFactory outputTransportFactory, - TProtocolFactory inputProtocolFactory, - TProtocolFactory outputProtocolFactory, - LogDelegate logDelegate) - { - this.processorFactory = new TSingletonProcessorFactory(processor); - this.serverTransport = serverTransport; - this.inputTransportFactory = inputTransportFactory; - this.outputTransportFactory = outputTransportFactory; - this.inputProtocolFactory = inputProtocolFactory; - this.outputProtocolFactory = outputProtocolFactory; - this.logDelegate = (logDelegate != null) ? logDelegate : DefaultLogDelegate; - } - - public TServer(TProcessorFactory processorFactory, - TServerTransport serverTransport, - TTransportFactory inputTransportFactory, - TTransportFactory outputTransportFactory, - TProtocolFactory inputProtocolFactory, - TProtocolFactory outputProtocolFactory, - LogDelegate logDelegate) - { - this.processorFactory = processorFactory; - this.serverTransport = serverTransport; - this.inputTransportFactory = inputTransportFactory; - this.outputTransportFactory = outputTransportFactory; - this.inputProtocolFactory = inputProtocolFactory; - this.outputProtocolFactory = outputProtocolFactory; - this.logDelegate = (logDelegate != null) ? logDelegate : DefaultLogDelegate; - } - - //Abstract Interface - public abstract void Serve(); - public abstract void Stop(); - } -} diff --git a/lib/csharp/src/Server/TServerEventHandler.cs b/lib/csharp/src/Server/TServerEventHandler.cs deleted file mode 100644 index e81efc6af9b..00000000000 --- a/lib/csharp/src/Server/TServerEventHandler.cs +++ /dev/null @@ -1,53 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - * - * Contains some contributions under the Thrift Software License. - * Please see doc/old-thrift-license.txt in the Thrift distribution for - * details. - */ - -using System; - -namespace Thrift.Server -{ - /// - /// Interface implemented by server users to handle events from the server. - /// - public interface TServerEventHandler - { - /// - /// Called before the server begins. - /// - void preServe(); - - /// - /// Called when a new client has connected and is about to being processing. - /// - object createContext(Thrift.Protocol.TProtocol input, Thrift.Protocol.TProtocol output); - - /// - /// Called when a client has finished request-handling to delete server context. - /// - void deleteContext(object serverContext, Thrift.Protocol.TProtocol input, Thrift.Protocol.TProtocol output); - - /// - /// Called when a client is about to call the processor. - /// - void processContext(object serverContext, Thrift.Transport.TTransport transport); - }; -} diff --git a/lib/csharp/src/Server/TSimpleServer.cs b/lib/csharp/src/Server/TSimpleServer.cs deleted file mode 100644 index 4e7ea96f4af..00000000000 --- a/lib/csharp/src/Server/TSimpleServer.cs +++ /dev/null @@ -1,180 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - * - * Contains some contributions under the Thrift Software License. - * Please see doc/old-thrift-license.txt in the Thrift distribution for - * details. - */ - -using System; -using Thrift.Transport; -using Thrift.Protocol; - -namespace Thrift.Server -{ - /// - /// Simple single-threaded server for testing. - /// - public class TSimpleServer : TServer - { - private bool stop = false; - - public TSimpleServer(TProcessor processor, - TServerTransport serverTransport) - : base(processor, serverTransport, new TTransportFactory(), new TTransportFactory(), new TBinaryProtocol.Factory(), new TBinaryProtocol.Factory(), DefaultLogDelegate) - { - } - - public TSimpleServer(TProcessor processor, - TServerTransport serverTransport, - LogDelegate logDel) - : base(processor, serverTransport, new TTransportFactory(), new TTransportFactory(), new TBinaryProtocol.Factory(), new TBinaryProtocol.Factory(), logDel) - { - } - - public TSimpleServer(TProcessor processor, - TServerTransport serverTransport, - TTransportFactory transportFactory) - : base(processor, - serverTransport, - transportFactory, - transportFactory, - new TBinaryProtocol.Factory(), - new TBinaryProtocol.Factory(), - DefaultLogDelegate) - { - } - - public TSimpleServer(TProcessor processor, - TServerTransport serverTransport, - TTransportFactory transportFactory, - TProtocolFactory protocolFactory) - : base(processor, - serverTransport, - transportFactory, - transportFactory, - protocolFactory, - protocolFactory, - DefaultLogDelegate) - { - } - - public TSimpleServer(TProcessorFactory processorFactory, - TServerTransport serverTransport, - TTransportFactory transportFactory, - TProtocolFactory protocolFactory) - : base(processorFactory, - serverTransport, - transportFactory, - transportFactory, - protocolFactory, - protocolFactory, - DefaultLogDelegate) - { - } - - public override void Serve() - { - try - { - serverTransport.Listen(); - } - catch (TTransportException ttx) - { - logDelegate(ttx.ToString()); - return; - } - - //Fire the preServe server event when server is up but before any client connections - if (serverEventHandler != null) - serverEventHandler.preServe(); - - while (!stop) - { - TProcessor processor = null; - TTransport client = null; - TTransport inputTransport = null; - TTransport outputTransport = null; - TProtocol inputProtocol = null; - TProtocol outputProtocol = null; - object connectionContext = null; - try - { - using (client = serverTransport.Accept()) - { - processor = processorFactory.GetProcessor(client); - if (client != null) - { - using (inputTransport = inputTransportFactory.GetTransport(client)) - { - using (outputTransport = outputTransportFactory.GetTransport(client)) - { - inputProtocol = inputProtocolFactory.GetProtocol(inputTransport); - outputProtocol = outputProtocolFactory.GetProtocol(outputTransport); - - //Recover event handler (if any) and fire createContext server event when a client connects - if (serverEventHandler != null) - connectionContext = serverEventHandler.createContext(inputProtocol, outputProtocol); - - //Process client requests until client disconnects - while (!stop) - { - if (!inputTransport.Peek()) - break; - - //Fire processContext server event - //N.B. This is the pattern implemented in C++ and the event fires provisionally. - //That is to say it may be many minutes between the event firing and the client request - //actually arriving or the client may hang up without ever makeing a request. - if (serverEventHandler != null) - serverEventHandler.processContext(connectionContext, inputTransport); - //Process client request (blocks until transport is readable) - if (!processor.Process(inputProtocol, outputProtocol)) - break; - } - } - } - } - } - } - catch (TTransportException ttx) - { - if (!stop || ttx.Type != TTransportException.ExceptionType.Interrupted) - { - logDelegate(ttx.ToString()); - } - } - catch (Exception x) - { - //Unexpected - logDelegate(x.ToString()); - } - - //Fire deleteContext server event after client disconnects - if (serverEventHandler != null) - serverEventHandler.deleteContext(connectionContext, inputProtocol, outputProtocol); - } - } - - public override void Stop() - { - stop = true; - serverTransport.Close(); - } - } -} diff --git a/lib/csharp/src/Server/TThreadedServer.cs b/lib/csharp/src/Server/TThreadedServer.cs deleted file mode 100644 index cc051a33bee..00000000000 --- a/lib/csharp/src/Server/TThreadedServer.cs +++ /dev/null @@ -1,282 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -using System; -using System.Collections.Generic; -using System.Threading; -using Thrift.Collections; -using Thrift.Protocol; -using Thrift.Transport; - -namespace Thrift.Server -{ - /// - /// Server that uses C# threads (as opposed to the ThreadPool) when handling requests. - /// - public class TThreadedServer : TServer - { - private const int DEFAULT_MAX_THREADS = 100; - private volatile bool stop = false; - private readonly int maxThreads; - - private Queue clientQueue; - private THashSet clientThreads; - private object clientLock; - private Thread workerThread; - - public int ClientThreadsCount - { - get { return clientThreads.Count; } - } - - public TThreadedServer(TProcessor processor, TServerTransport serverTransport) - : this(new TSingletonProcessorFactory(processor), serverTransport, - new TTransportFactory(), new TTransportFactory(), - new TBinaryProtocol.Factory(), new TBinaryProtocol.Factory(), - DEFAULT_MAX_THREADS, DefaultLogDelegate) - { - } - - public TThreadedServer(TProcessor processor, TServerTransport serverTransport, LogDelegate logDelegate) - : this(new TSingletonProcessorFactory(processor), serverTransport, - new TTransportFactory(), new TTransportFactory(), - new TBinaryProtocol.Factory(), new TBinaryProtocol.Factory(), - DEFAULT_MAX_THREADS, logDelegate) - { - } - - - public TThreadedServer(TProcessor processor, - TServerTransport serverTransport, - TTransportFactory transportFactory, - TProtocolFactory protocolFactory) - : this(new TSingletonProcessorFactory(processor), serverTransport, - transportFactory, transportFactory, - protocolFactory, protocolFactory, - DEFAULT_MAX_THREADS, DefaultLogDelegate) - { - } - - public TThreadedServer(TProcessorFactory processorFactory, - TServerTransport serverTransport, - TTransportFactory transportFactory, - TProtocolFactory protocolFactory) - : this(processorFactory, serverTransport, - transportFactory, transportFactory, - protocolFactory, protocolFactory, - DEFAULT_MAX_THREADS, DefaultLogDelegate) - { - } - public TThreadedServer(TProcessorFactory processorFactory, - TServerTransport serverTransport, - TTransportFactory inputTransportFactory, - TTransportFactory outputTransportFactory, - TProtocolFactory inputProtocolFactory, - TProtocolFactory outputProtocolFactory, - int maxThreads, LogDelegate logDel) - : base(processorFactory, serverTransport, inputTransportFactory, outputTransportFactory, - inputProtocolFactory, outputProtocolFactory, logDel) - { - this.maxThreads = maxThreads; - clientQueue = new Queue(); - clientLock = new object(); - clientThreads = new THashSet(); - } - - /// - /// Use new Thread for each new client connection. block until numConnections < maxThreads. - /// - public override void Serve() - { - try - { - //start worker thread - workerThread = new Thread(new ThreadStart(Execute)); - workerThread.Start(); - serverTransport.Listen(); - } - catch (TTransportException ttx) - { - logDelegate("Error, could not listen on ServerTransport: " + ttx); - return; - } - - //Fire the preServe server event when server is up but before any client connections - if (serverEventHandler != null) - serverEventHandler.preServe(); - - while (!stop) - { - int failureCount = 0; - try - { - TTransport client = serverTransport.Accept(); - lock (clientLock) - { - clientQueue.Enqueue(client); - Monitor.Pulse(clientLock); - } - } - catch (TTransportException ttx) - { - if (!stop || ttx.Type != TTransportException.ExceptionType.Interrupted) - { - ++failureCount; - logDelegate(ttx.ToString()); - } - - } - } - - if (stop) - { - try - { - serverTransport.Close(); - } - catch (TTransportException ttx) - { - logDelegate("TServeTransport failed on close: " + ttx.Message); - } - stop = false; - } - } - - /// - /// Loops on processing a client forever - /// - private void Execute() - { - while (!stop) - { - TTransport client; - Thread t; - lock (clientLock) - { - //don't dequeue if too many connections - while (clientThreads.Count >= maxThreads) - { - Monitor.Wait(clientLock); - } - - while (clientQueue.Count == 0) - { - Monitor.Wait(clientLock); - } - - client = clientQueue.Dequeue(); - t = new Thread(new ParameterizedThreadStart(ClientWorker)); - clientThreads.Add(t); - } - //start processing requests from client on new thread - t.Start(client); - } - } - - private void ClientWorker(object context) - { - using (TTransport client = (TTransport)context) - { - TProcessor processor = processorFactory.GetProcessor(client); - TTransport inputTransport = null; - TTransport outputTransport = null; - TProtocol inputProtocol = null; - TProtocol outputProtocol = null; - object connectionContext = null; - try - { - try - { - inputTransport = inputTransportFactory.GetTransport(client); - outputTransport = outputTransportFactory.GetTransport(client); - inputProtocol = inputProtocolFactory.GetProtocol(inputTransport); - outputProtocol = outputProtocolFactory.GetProtocol(outputTransport); - - //Recover event handler (if any) and fire createContext server event when a client connects - if (serverEventHandler != null) - connectionContext = serverEventHandler.createContext(inputProtocol, outputProtocol); - - //Process client requests until client disconnects - while (!stop) - { - if (!inputTransport.Peek()) - break; - - //Fire processContext server event - //N.B. This is the pattern implemented in C++ and the event fires provisionally. - //That is to say it may be many minutes between the event firing and the client request - //actually arriving or the client may hang up without ever makeing a request. - if (serverEventHandler != null) - serverEventHandler.processContext(connectionContext, inputTransport); - //Process client request (blocks until transport is readable) - if (!processor.Process(inputProtocol, outputProtocol)) - break; - } - } - catch (TTransportException) - { - //Usually a client disconnect, expected - } - catch (Exception x) - { - //Unexpected - logDelegate("Error: " + x); - } - - //Fire deleteContext server event after client disconnects - if (serverEventHandler != null) - serverEventHandler.deleteContext(connectionContext, inputProtocol, outputProtocol); - - lock (clientLock) - { - clientThreads.Remove(Thread.CurrentThread); - Monitor.Pulse(clientLock); - } - - } - finally - { - //Close transports - if (inputTransport != null) - inputTransport.Close(); - if (outputTransport != null) - outputTransport.Close(); - - // disposable stuff should be disposed - if (inputProtocol != null) - inputProtocol.Dispose(); - if (outputProtocol != null) - outputProtocol.Dispose(); - } - } - } - - public override void Stop() - { - stop = true; - serverTransport.Close(); - //clean up all the threads myself - workerThread.Abort(); - foreach (Thread t in clientThreads) - { - t.Abort(); - } - } - } -} diff --git a/lib/csharp/src/TApplicationException.cs b/lib/csharp/src/TApplicationException.cs deleted file mode 100644 index 8dd7ae57822..00000000000 --- a/lib/csharp/src/TApplicationException.cs +++ /dev/null @@ -1,146 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - * - * Contains some contributions under the Thrift Software License. - * Please see doc/old-thrift-license.txt in the Thrift distribution for - * details. - */ - -using System; -using Thrift.Protocol; - -namespace Thrift -{ - public class TApplicationException : TException - { - protected ExceptionType type; - - public TApplicationException() - { - } - - public TApplicationException(ExceptionType type) - { - this.type = type; - } - - public TApplicationException(ExceptionType type, string message) - : base(message, null) // TApplicationException is serializable, but we never serialize InnerException - { - this.type = type; - } - - public static TApplicationException Read(TProtocol iprot) - { - TField field; - - string message = null; - ExceptionType type = ExceptionType.Unknown; - - iprot.ReadStructBegin(); - while (true) - { - field = iprot.ReadFieldBegin(); - if (field.Type == TType.Stop) - { - break; - } - - switch (field.ID) - { - case 1: - if (field.Type == TType.String) - { - message = iprot.ReadString(); - } - else - { - TProtocolUtil.Skip(iprot, field.Type); - } - break; - case 2: - if (field.Type == TType.I32) - { - type = (ExceptionType)iprot.ReadI32(); - } - else - { - TProtocolUtil.Skip(iprot, field.Type); - } - break; - default: - TProtocolUtil.Skip(iprot, field.Type); - break; - } - - iprot.ReadFieldEnd(); - } - - iprot.ReadStructEnd(); - - return new TApplicationException(type, message); - } - - public void Write(TProtocol oprot) - { - TStruct struc = new TStruct("TApplicationException"); - TField field = new TField(); - - oprot.WriteStructBegin(struc); - - if (!string.IsNullOrEmpty(Message)) - { - field.Name = "message"; - field.Type = TType.String; - field.ID = 1; - oprot.WriteFieldBegin(field); - oprot.WriteString(Message); - oprot.WriteFieldEnd(); - } - - field.Name = "type"; - field.Type = TType.I32; - field.ID = 2; - oprot.WriteFieldBegin(field); - oprot.WriteI32((int)type); - oprot.WriteFieldEnd(); - oprot.WriteFieldStop(); - oprot.WriteStructEnd(); - } - - public enum ExceptionType - { - Unknown, - UnknownMethod, - InvalidMessageType, - WrongMethodName, - BadSequenceID, - MissingResult, - InternalError, - ProtocolError, - InvalidTransform, - InvalidProtocol, - UnsupportedClientType - } - - public ExceptionType Type - { - get { return type; } - } - } -} diff --git a/lib/csharp/src/TAsyncProcessor.cs b/lib/csharp/src/TAsyncProcessor.cs deleted file mode 100644 index ab432255ba5..00000000000 --- a/lib/csharp/src/TAsyncProcessor.cs +++ /dev/null @@ -1,38 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -using System.Threading.Tasks; -using Thrift.Protocol; - -namespace Thrift -{ - /// - /// Processes a message asynchronously. - /// - public interface TAsyncProcessor - { - /// - /// Processes the next part of the message. - /// - /// The input protocol. - /// The output protocol. - /// true if there's more to process, false otherwise. - Task ProcessAsync(TProtocol iprot, TProtocol oprot); - } -} diff --git a/lib/csharp/src/TControllingHandler.cs b/lib/csharp/src/TControllingHandler.cs deleted file mode 100644 index 7b5203a5f2d..00000000000 --- a/lib/csharp/src/TControllingHandler.cs +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -using System; -using Thrift.Server; - -namespace Thrift -{ - public interface TControllingHandler - { - TServer server { get; set; } - } -} diff --git a/lib/csharp/src/TException.cs b/lib/csharp/src/TException.cs deleted file mode 100644 index aa9a210c977..00000000000 --- a/lib/csharp/src/TException.cs +++ /dev/null @@ -1,40 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - * - * Contains some contributions under the Thrift Software License. - * Please see doc/old-thrift-license.txt in the Thrift distribution for - * details. - */ - -using System; - -namespace Thrift -{ - public class TException : Exception - { - public TException() - { - } - - public TException(string message, Exception inner) - : base(message, inner) - { - } - - } -} diff --git a/lib/csharp/src/TProcessor.cs b/lib/csharp/src/TProcessor.cs deleted file mode 100644 index 71ce7550892..00000000000 --- a/lib/csharp/src/TProcessor.cs +++ /dev/null @@ -1,33 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - * - * Contains some contributions under the Thrift Software License. - * Please see doc/old-thrift-license.txt in the Thrift distribution for - * details. - */ - -using System; -using Thrift.Protocol; - -namespace Thrift -{ - public interface TProcessor - { - bool Process(TProtocol iprot, TProtocol oprot); - } -} diff --git a/lib/csharp/src/TProcessorFactory.cs b/lib/csharp/src/TProcessorFactory.cs deleted file mode 100644 index fdf631bc77e..00000000000 --- a/lib/csharp/src/TProcessorFactory.cs +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -using System; -using Thrift.Server; -using Thrift.Transport; - -namespace Thrift -{ - public interface TProcessorFactory - { - TProcessor GetProcessor(TTransport trans, TServer server = null); - } -} diff --git a/lib/csharp/src/TPrototypeProcessorFactory.cs b/lib/csharp/src/TPrototypeProcessorFactory.cs deleted file mode 100644 index 0b47261fbbe..00000000000 --- a/lib/csharp/src/TPrototypeProcessorFactory.cs +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using Thrift.Server; -using Thrift.Transport; - -namespace Thrift -{ - public class TPrototypeProcessorFactory : TProcessorFactory where P : TProcessor - { - object[] handlerArgs = null; - - public TPrototypeProcessorFactory() - { - handlerArgs = new object[0]; - } - - public TPrototypeProcessorFactory(params object[] handlerArgs) - { - this.handlerArgs = handlerArgs; - } - - public TProcessor GetProcessor(TTransport trans, TServer server = null) - { - H handler = (H)Activator.CreateInstance(typeof(H), handlerArgs); - - TControllingHandler handlerServerRef = handler as TControllingHandler; - if (handlerServerRef != null) - { - handlerServerRef.server = server; - } - return Activator.CreateInstance(typeof(P), new object[] { handler }) as TProcessor; - } - } -} diff --git a/lib/csharp/src/TSingletonProcessorFactory.cs b/lib/csharp/src/TSingletonProcessorFactory.cs deleted file mode 100644 index ed2897ba362..00000000000 --- a/lib/csharp/src/TSingletonProcessorFactory.cs +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using Thrift.Server; -using Thrift.Transport; - -namespace Thrift -{ - public class TSingletonProcessorFactory : TProcessorFactory - { - private readonly TProcessor processor_; - - public TSingletonProcessorFactory(TProcessor processor) - { - processor_ = processor; - } - - public TProcessor GetProcessor(TTransport trans, TServer server = null) - { - return processor_; - } - } -} diff --git a/lib/csharp/src/Thrift.45.csproj b/lib/csharp/src/Thrift.45.csproj deleted file mode 100644 index 455916f8338..00000000000 --- a/lib/csharp/src/Thrift.45.csproj +++ /dev/null @@ -1,129 +0,0 @@ - - - - - Debug - AnyCPU - {EBCE35DA-CF6A-42BC-A357-A9C09B534299} - Library - Properties - Thrift - Thrift45 - v4.5 - 512 - - - true - full - false - bin\Debug\ - TRACE;DEBUG;NET45 - prompt - 4 - - - pdbonly - true - bin\Release\ - TRACE;NET45 - prompt - 4 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/lib/csharp/src/Thrift.csproj b/lib/csharp/src/Thrift.csproj deleted file mode 100644 index ede152d7841..00000000000 --- a/lib/csharp/src/Thrift.csproj +++ /dev/null @@ -1,156 +0,0 @@ - - - - - Debug - AnyCPU - {499EB63C-D74C-47E8-AE48-A2FC94538E9D} - 9.0.21022 - 2.0 - Library - false - Thrift - v3.5 - 512 - Thrift - - - 3.5 - - publish\ - true - Disk - false - Foreground - 7 - Days - false - false - true - 0 - 0.12.0.%2a - false - false - true - - - true - full - false - bin\Debug\ - DEBUG;TRACE - prompt - 4 - AllRules.ruleset - - - pdbonly - true - bin\Release\ - TRACE - prompt - 4 - AllRules.ruleset - - - - - 3.5 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - False - .NET Framework 3.5 SP1 Client Profile - false - - - False - .NET Framework 3.5 SP1 - true - - - False - Windows Installer 3.1 - true - - - - - - - diff --git a/lib/csharp/src/Thrift.sln b/lib/csharp/src/Thrift.sln deleted file mode 100644 index a29e46882ab..00000000000 --- a/lib/csharp/src/Thrift.sln +++ /dev/null @@ -1,47 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 11.00 -# Visual Studio 2010 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Thrift", "Thrift.csproj", "{499EB63C-D74C-47E8-AE48-A2FC94538E9D}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ThriftTest", "..\test\ThriftTest\ThriftTest.csproj", "{48DD757F-CA95-4DD7-BDA4-58DB6F108C2C}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ThriftMSBuildTask", "..\ThriftMSBuildTask\ThriftMSBuildTask.csproj", "{EC0A0231-66EA-4593-A792-C6CA3BB8668E}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Thrift.45", "Thrift.45.csproj", "{EBCE35DA-CF6A-42BC-A357-A9C09B534299}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ThriftMVCTest", "..\test\ThriftMVCTest\ThriftMVCTest.csproj", "{891B4487-C7BA-427E-BBC8-4C596C229A10}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Release|Any CPU = Release|Any CPU - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {499EB63C-D74C-47E8-AE48-A2FC94538E9D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {499EB63C-D74C-47E8-AE48-A2FC94538E9D}.Debug|Any CPU.Build.0 = Debug|Any CPU - {499EB63C-D74C-47E8-AE48-A2FC94538E9D}.Release|Any CPU.ActiveCfg = Release|Any CPU - {499EB63C-D74C-47E8-AE48-A2FC94538E9D}.Release|Any CPU.Build.0 = Release|Any CPU - {48DD757F-CA95-4DD7-BDA4-58DB6F108C2C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {48DD757F-CA95-4DD7-BDA4-58DB6F108C2C}.Debug|Any CPU.Build.0 = Debug|Any CPU - {48DD757F-CA95-4DD7-BDA4-58DB6F108C2C}.Release|Any CPU.ActiveCfg = Release|Any CPU - {48DD757F-CA95-4DD7-BDA4-58DB6F108C2C}.Release|Any CPU.Build.0 = Release|Any CPU - {EC0A0231-66EA-4593-A792-C6CA3BB8668E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {EC0A0231-66EA-4593-A792-C6CA3BB8668E}.Debug|Any CPU.Build.0 = Debug|Any CPU - {EC0A0231-66EA-4593-A792-C6CA3BB8668E}.Release|Any CPU.ActiveCfg = Release|Any CPU - {EC0A0231-66EA-4593-A792-C6CA3BB8668E}.Release|Any CPU.Build.0 = Release|Any CPU - {EBCE35DA-CF6A-42BC-A357-A9C09B534299}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {EBCE35DA-CF6A-42BC-A357-A9C09B534299}.Debug|Any CPU.Build.0 = Debug|Any CPU - {EBCE35DA-CF6A-42BC-A357-A9C09B534299}.Release|Any CPU.ActiveCfg = Release|Any CPU - {EBCE35DA-CF6A-42BC-A357-A9C09B534299}.Release|Any CPU.Build.0 = Release|Any CPU - {891B4487-C7BA-427E-BBC8-4C596C229A10}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {891B4487-C7BA-427E-BBC8-4C596C229A10}.Debug|Any CPU.Build.0 = Debug|Any CPU - {891B4487-C7BA-427E-BBC8-4C596C229A10}.Release|Any CPU.ActiveCfg = Release|Any CPU - {891B4487-C7BA-427E-BBC8-4C596C229A10}.Release|Any CPU.Build.0 = Release|Any CPU - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection - GlobalSection(MonoDevelopProperties) = preSolution - StartupItem = Thrift.csproj - EndGlobalSection -EndGlobal diff --git a/lib/csharp/src/Transport/TBufferedTransport.cs b/lib/csharp/src/Transport/TBufferedTransport.cs deleted file mode 100644 index 88709881086..00000000000 --- a/lib/csharp/src/Transport/TBufferedTransport.cs +++ /dev/null @@ -1,194 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -using System; -using System.IO; - -namespace Thrift.Transport -{ - public class TBufferedTransport : TTransport, IDisposable - { - private readonly int bufSize; - private readonly MemoryStream inputBuffer = new MemoryStream(0); - private readonly MemoryStream outputBuffer = new MemoryStream(0); - private readonly TTransport transport; - - public TBufferedTransport(TTransport transport, int bufSize = 1024) - { - if (transport == null) - throw new ArgumentNullException("transport"); - if (bufSize <= 0) - throw new ArgumentException("bufSize", "Buffer size must be a positive number."); - this.transport = transport; - this.bufSize = bufSize; - } - - public TTransport UnderlyingTransport - { - get - { - CheckNotDisposed(); - return transport; - } - } - - public override bool IsOpen - { - get - { - // We can legitimately throw here but be nice a bit. - // CheckNotDisposed(); - return !_IsDisposed && transport.IsOpen; - } - } - - public override void Open() - { - CheckNotDisposed(); - transport.Open(); - } - - public override void Close() - { - CheckNotDisposed(); - transport.Close(); - } - - public override int Read(byte[] buf, int off, int len) - { - CheckNotDisposed(); - ValidateBufferArgs(buf, off, len); - if (!IsOpen) - throw new TTransportException(TTransportException.ExceptionType.NotOpen); - - if (inputBuffer.Capacity < bufSize) - inputBuffer.Capacity = bufSize; - - while (true) - { - int got = inputBuffer.Read(buf, off, len); - if (got > 0) - return got; - - inputBuffer.Seek(0, SeekOrigin.Begin); - inputBuffer.SetLength(inputBuffer.Capacity); - int filled = transport.Read(inputBuffer.GetBuffer(), 0, (int)inputBuffer.Length); - inputBuffer.SetLength(filled); - if (filled == 0) - return 0; - } - } - - public override void Write(byte[] buf, int off, int len) - { - CheckNotDisposed(); - ValidateBufferArgs(buf, off, len); - if (!IsOpen) - throw new TTransportException(TTransportException.ExceptionType.NotOpen); - // Relative offset from "off" argument - int offset = 0; - if (outputBuffer.Length > 0) - { - int capa = (int)(outputBuffer.Capacity - outputBuffer.Length); - int writeSize = capa <= len ? capa : len; - outputBuffer.Write(buf, off, writeSize); - offset += writeSize; - if (writeSize == capa) - { - transport.Write(outputBuffer.GetBuffer(), 0, (int)outputBuffer.Length); - outputBuffer.SetLength(0); - } - } - while (len - offset >= bufSize) - { - transport.Write(buf, off + offset, bufSize); - offset += bufSize; - } - int remain = len - offset; - if (remain > 0) - { - if (outputBuffer.Capacity < bufSize) - outputBuffer.Capacity = bufSize; - outputBuffer.Write(buf, off + offset, remain); - } - } - - private void InternalFlush() - { - if (!IsOpen) - throw new TTransportException(TTransportException.ExceptionType.NotOpen); - if (outputBuffer.Length > 0) - { - transport.Write(outputBuffer.GetBuffer(), 0, (int)outputBuffer.Length); - outputBuffer.SetLength(0); - } - } - - public override void Flush() - { - CheckNotDisposed(); - InternalFlush(); - - transport.Flush(); - } - - public override IAsyncResult BeginFlush(AsyncCallback callback, object state) - { - CheckNotDisposed(); - InternalFlush(); - - return transport.BeginFlush( callback, state); - } - - public override void EndFlush(IAsyncResult asyncResult) - { - transport.EndFlush( asyncResult); - } - - - - protected void CheckNotDisposed() - { - if (_IsDisposed) - throw new ObjectDisposedException("TBufferedTransport"); - } - - #region " IDisposable Support " - protected bool _IsDisposed { get; private set; } - - // IDisposable - protected override void Dispose(bool disposing) - { - if (!_IsDisposed) - { - if (disposing) - { - if (inputBuffer != null) - inputBuffer.Dispose(); - if (outputBuffer != null) - outputBuffer.Dispose(); - if (transport != null) - transport.Dispose(); - } - } - _IsDisposed = true; - } - #endregion - } -} diff --git a/lib/csharp/src/Transport/TFramedTransport.cs b/lib/csharp/src/Transport/TFramedTransport.cs deleted file mode 100644 index a746a3223a5..00000000000 --- a/lib/csharp/src/Transport/TFramedTransport.cs +++ /dev/null @@ -1,205 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -using System; -using System.IO; - -namespace Thrift.Transport -{ - public class TFramedTransport : TTransport, IDisposable - { - private readonly TTransport transport; - private readonly MemoryStream writeBuffer = new MemoryStream(1024); - private readonly MemoryStream readBuffer = new MemoryStream(1024); - - private const int HeaderSize = 4; - private readonly byte[] headerBuf = new byte[HeaderSize]; - - public class Factory : TTransportFactory - { - public override TTransport GetTransport(TTransport trans) - { - return new TFramedTransport(trans); - } - } - - public TFramedTransport(TTransport transport) - { - if (transport == null) - throw new ArgumentNullException("transport"); - this.transport = transport; - InitWriteBuffer(); - } - - public override void Open() - { - CheckNotDisposed(); - transport.Open(); - } - - public override bool IsOpen - { - get - { - // We can legitimately throw here but be nice a bit. - // CheckNotDisposed(); - return !_IsDisposed && transport.IsOpen; - } - } - - public override void Close() - { - CheckNotDisposed(); - transport.Close(); - } - - public override int Read(byte[] buf, int off, int len) - { - CheckNotDisposed(); - ValidateBufferArgs(buf, off, len); - if (!IsOpen) - throw new TTransportException(TTransportException.ExceptionType.NotOpen); - int got = readBuffer.Read(buf, off, len); - if (got > 0) - { - return got; - } - - // Read another frame of data - ReadFrame(); - - return readBuffer.Read(buf, off, len); - } - - private void ReadFrame() - { - transport.ReadAll(headerBuf, 0, HeaderSize); - int size = DecodeFrameSize(headerBuf); - - readBuffer.SetLength(size); - readBuffer.Seek(0, SeekOrigin.Begin); - byte[] buff = readBuffer.GetBuffer(); - transport.ReadAll(buff, 0, size); - } - - public override void Write(byte[] buf, int off, int len) - { - CheckNotDisposed(); - ValidateBufferArgs(buf, off, len); - if (!IsOpen) - throw new TTransportException(TTransportException.ExceptionType.NotOpen); - if (writeBuffer.Length + (long)len > (long)int.MaxValue) - Flush(); - writeBuffer.Write(buf, off, len); - } - - private void InternalFlush() - { - CheckNotDisposed(); - if (!IsOpen) - throw new TTransportException(TTransportException.ExceptionType.NotOpen); - byte[] buf = writeBuffer.GetBuffer(); - int len = (int)writeBuffer.Length; - int data_len = len - HeaderSize; - if (data_len < 0) - throw new System.InvalidOperationException(); // logic error actually - - // Inject message header into the reserved buffer space - EncodeFrameSize(data_len, buf); - - // Send the entire message at once - transport.Write(buf, 0, len); - - InitWriteBuffer(); - } - - public override void Flush() - { - CheckNotDisposed(); - InternalFlush(); - - transport.Flush(); - } - - public override IAsyncResult BeginFlush(AsyncCallback callback, object state) - { - CheckNotDisposed(); - InternalFlush(); - - return transport.BeginFlush( callback, state); - } - - public override void EndFlush(IAsyncResult asyncResult) - { - transport.EndFlush( asyncResult); - } - - private void InitWriteBuffer() - { - // Reserve space for message header to be put right before sending it out - writeBuffer.SetLength(HeaderSize); - writeBuffer.Seek(0, SeekOrigin.End); - } - - private static void EncodeFrameSize(int frameSize, byte[] buf) - { - buf[0] = (byte)(0xff & (frameSize >> 24)); - buf[1] = (byte)(0xff & (frameSize >> 16)); - buf[2] = (byte)(0xff & (frameSize >> 8)); - buf[3] = (byte)(0xff & (frameSize)); - } - - private static int DecodeFrameSize(byte[] buf) - { - return - ((buf[0] & 0xff) << 24) | - ((buf[1] & 0xff) << 16) | - ((buf[2] & 0xff) << 8) | - ((buf[3] & 0xff)); - } - - - private void CheckNotDisposed() - { - if (_IsDisposed) - throw new ObjectDisposedException("TFramedTransport"); - } - - #region " IDisposable Support " - private bool _IsDisposed; - - // IDisposable - protected override void Dispose(bool disposing) - { - if (!_IsDisposed) - { - if (disposing) - { - if (readBuffer != null) - readBuffer.Dispose(); - if (writeBuffer != null) - writeBuffer.Dispose(); - if (transport != null) - transport.Dispose(); - } - } - _IsDisposed = true; - } - #endregion - } -} diff --git a/lib/csharp/src/Transport/THttpClient.cs b/lib/csharp/src/Transport/THttpClient.cs deleted file mode 100644 index 667fc2526c4..00000000000 --- a/lib/csharp/src/Transport/THttpClient.cs +++ /dev/null @@ -1,475 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - * - * - */ - -using System; -using System.Collections.Generic; -using System.IO; -using System.Net; -using System.Threading; -using System.Linq; -using System.Security.Cryptography.X509Certificates; -using System.IO.Compression; - -namespace Thrift.Transport -{ - public class THttpClient : TTransport, IDisposable - { - private readonly Uri uri; - private readonly X509Certificate[] certificates; - private Stream inputStream; - private MemoryStream outputStream = new MemoryStream(); - - // Timeouts in milliseconds - private int connectTimeout = 30000; - - private int readTimeout = 30000; - - private IDictionary customHeaders = new Dictionary(); - -#if !SILVERLIGHT - private IWebProxy proxy = WebRequest.DefaultWebProxy; -#endif - - public THttpClient(Uri u) - : this(u, Enumerable.Empty()) - { - } - - public THttpClient(Uri u, IEnumerable certificates) - { - uri = u; - this.certificates = (certificates ?? Enumerable.Empty()).ToArray(); - } - - public int ConnectTimeout - { - set - { - connectTimeout = value; - } - } - - public int ReadTimeout - { - set - { - readTimeout = value; - } - } - - public IDictionary CustomHeaders - { - get - { - return customHeaders; - } - } - -#if !SILVERLIGHT - public IWebProxy Proxy - { - set - { - proxy = value; - } - } -#endif - - public override bool IsOpen - { - get - { - return true; - } - } - - public override void Open() - { - } - - public override void Close() - { - if (inputStream != null) - { - inputStream.Close(); - inputStream = null; - } - if (outputStream != null) - { - outputStream.Close(); - outputStream = null; - } - } - - public override int Read(byte[] buf, int off, int len) - { - if (inputStream == null) - { - throw new TTransportException(TTransportException.ExceptionType.NotOpen, "No request has been sent"); - } - - try - { - int ret = inputStream.Read(buf, off, len); - - if (ret == -1) - { - throw new TTransportException(TTransportException.ExceptionType.EndOfFile, "No more data available"); - } - - return ret; - } - catch (IOException iox) - { - throw new TTransportException(TTransportException.ExceptionType.Unknown, iox.ToString(), iox); - } - } - - public override void Write(byte[] buf, int off, int len) - { - outputStream.Write(buf, off, len); - } - -#if !SILVERLIGHT - public override void Flush() - { - try - { - SendRequest(); - } - finally - { - outputStream = new MemoryStream(); - } - } - - private void SendRequest() - { - try - { - HttpWebRequest connection = CreateRequest(); - connection.Headers.Add("Accept-Encoding", "gzip, deflate"); - - byte[] data = outputStream.ToArray(); - connection.ContentLength = data.Length; - - using (Stream requestStream = connection.GetRequestStream()) - { - requestStream.Write(data, 0, data.Length); - - // Resolve HTTP hang that can happens after successive calls by making sure - // that we release the response and response stream. To support this, we copy - // the response to a memory stream. - using (var response = connection.GetResponse()) - { - using (var responseStream = response.GetResponseStream()) - { - // Copy the response to a memory stream so that we can - // cleanly close the response and response stream. - inputStream = new MemoryStream(); - byte[] buffer = new byte[8192]; // multiple of 4096 - int bytesRead; - while ((bytesRead = responseStream.Read(buffer, 0, buffer.Length)) > 0) - { - inputStream.Write(buffer, 0, bytesRead); - } - inputStream.Seek(0, 0); - } - - var encodings = response.Headers.GetValues("Content-Encoding"); - if (encodings != null) - { - foreach (var encoding in encodings) - { - switch (encoding) - { - case "gzip": - DecompressGZipped(ref inputStream); - break; - case "deflate": - DecompressDeflated(ref inputStream); - break; - default: - break; - } - } - } - } - } - } - catch (IOException iox) - { - throw new TTransportException(TTransportException.ExceptionType.Unknown, iox.ToString(), iox); - } - catch (WebException wx) - { - throw new TTransportException(TTransportException.ExceptionType.Unknown, "Couldn't connect to server: " + wx, wx); - } - } - - private void DecompressDeflated(ref Stream inputStream) - { - var tmp = new MemoryStream(); - using (var decomp = new DeflateStream(inputStream, CompressionMode.Decompress)) - { - decomp.CopyTo(tmp); - } - inputStream.Dispose(); - inputStream = tmp; - inputStream.Seek(0, 0); - } - - private void DecompressGZipped(ref Stream inputStream) - { - var tmp = new MemoryStream(); - using (var decomp = new GZipStream(inputStream, CompressionMode.Decompress)) - { - decomp.CopyTo(tmp); - } - inputStream.Dispose(); - inputStream = tmp; - inputStream.Seek(0, 0); - } -#endif - private HttpWebRequest CreateRequest() - { - HttpWebRequest connection = (HttpWebRequest)WebRequest.Create(uri); - - -#if !SILVERLIGHT - // Adding certificates through code is not supported with WP7 Silverlight - // see "Windows Phone 7 and Certificates_FINAL_121610.pdf" - connection.ClientCertificates.AddRange(certificates); - - if (connectTimeout > 0) - { - connection.Timeout = connectTimeout; - } - if (readTimeout > 0) - { - connection.ReadWriteTimeout = readTimeout; - } -#endif - // Make the request - connection.ContentType = "application/x-thrift"; - connection.Accept = "application/x-thrift"; - connection.UserAgent = "C#/THttpClient"; - connection.Method = "POST"; -#if !SILVERLIGHT - connection.ProtocolVersion = HttpVersion.Version10; -#endif - - //add custom headers here - foreach (KeyValuePair item in customHeaders) - { -#if !SILVERLIGHT - connection.Headers.Add(item.Key, item.Value); -#else - connection.Headers[item.Key] = item.Value; -#endif - } - -#if !SILVERLIGHT - connection.Proxy = proxy; -#endif - - return connection; - } - - public override IAsyncResult BeginFlush(AsyncCallback callback, object state) - { - // Extract request and reset buffer - var data = outputStream.ToArray(); - - //requestBuffer_ = new MemoryStream(); - - try - { - // Create connection object - var flushAsyncResult = new FlushAsyncResult(callback, state); - flushAsyncResult.Connection = CreateRequest(); - - flushAsyncResult.Data = data; - - - flushAsyncResult.Connection.BeginGetRequestStream(GetRequestStreamCallback, flushAsyncResult); - return flushAsyncResult; - - } - catch (IOException iox) - { - throw new TTransportException(iox.ToString(), iox); - } - } - - public override void EndFlush(IAsyncResult asyncResult) - { - try - { - var flushAsyncResult = (FlushAsyncResult)asyncResult; - - if (!flushAsyncResult.IsCompleted) - { - var waitHandle = flushAsyncResult.AsyncWaitHandle; - waitHandle.WaitOne(); // blocking INFINITEly - waitHandle.Close(); - } - - if (flushAsyncResult.AsyncException != null) - { - throw flushAsyncResult.AsyncException; - } - } - finally - { - outputStream = new MemoryStream(); - } - - } - - private void GetRequestStreamCallback(IAsyncResult asynchronousResult) - { - var flushAsyncResult = (FlushAsyncResult)asynchronousResult.AsyncState; - try - { - var reqStream = flushAsyncResult.Connection.EndGetRequestStream(asynchronousResult); - reqStream.Write(flushAsyncResult.Data, 0, flushAsyncResult.Data.Length); - reqStream.Flush(); - reqStream.Close(); - - // Start the asynchronous operation to get the response - flushAsyncResult.Connection.BeginGetResponse(GetResponseCallback, flushAsyncResult); - } - catch (Exception exception) - { - flushAsyncResult.AsyncException = new TTransportException(exception.ToString(), exception); - flushAsyncResult.UpdateStatusToComplete(); - flushAsyncResult.NotifyCallbackWhenAvailable(); - } - } - - private void GetResponseCallback(IAsyncResult asynchronousResult) - { - var flushAsyncResult = (FlushAsyncResult)asynchronousResult.AsyncState; - try - { - inputStream = flushAsyncResult.Connection.EndGetResponse(asynchronousResult).GetResponseStream(); - } - catch (Exception exception) - { - flushAsyncResult.AsyncException = new TTransportException(exception.ToString(), exception); - } - flushAsyncResult.UpdateStatusToComplete(); - flushAsyncResult.NotifyCallbackWhenAvailable(); - } - - // Based on http://msmvps.com/blogs/luisabreu/archive/2009/06/15/multithreading-implementing-the-iasyncresult-interface.aspx - class FlushAsyncResult : IAsyncResult - { - private volatile Boolean _isCompleted; - private ManualResetEvent _evt; - private readonly AsyncCallback _cbMethod; - private readonly object _state; - - public FlushAsyncResult(AsyncCallback cbMethod, object state) - { - _cbMethod = cbMethod; - _state = state; - } - - internal byte[] Data { get; set; } - internal HttpWebRequest Connection { get; set; } - internal TTransportException AsyncException { get; set; } - - public object AsyncState - { - get { return _state; } - } - public WaitHandle AsyncWaitHandle - { - get { return GetEvtHandle(); } - } - public bool CompletedSynchronously - { - get { return false; } - } - public bool IsCompleted - { - get { return _isCompleted; } - } - private readonly object _locker = new object(); - private ManualResetEvent GetEvtHandle() - { - lock (_locker) - { - if (_evt == null) - { - _evt = new ManualResetEvent(false); - } - if (_isCompleted) - { - _evt.Set(); - } - } - return _evt; - } - internal void UpdateStatusToComplete() - { - _isCompleted = true; //1. set _iscompleted to true - lock (_locker) - { - if (_evt != null) - { - _evt.Set(); //2. set the event, when it exists - } - } - } - - internal void NotifyCallbackWhenAvailable() - { - if (_cbMethod != null) - { - _cbMethod(this); - } - } - } - - #region " IDisposable Support " - private bool _IsDisposed; - - // IDisposable - protected override void Dispose(bool disposing) - { - if (!_IsDisposed) - { - if (disposing) - { - if (inputStream != null) - inputStream.Dispose(); - if (outputStream != null) - outputStream.Dispose(); - } - } - _IsDisposed = true; - } - #endregion - } -} diff --git a/lib/csharp/src/Transport/THttpHandler.cs b/lib/csharp/src/Transport/THttpHandler.cs deleted file mode 100644 index 4115ef95aaa..00000000000 --- a/lib/csharp/src/Transport/THttpHandler.cs +++ /dev/null @@ -1,102 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - * - * - */ - -using System; -using System.Web; -using System.Net; -using System.IO; - -using Thrift.Protocol; - -namespace Thrift.Transport -{ - public class THttpHandler : IHttpHandler - { - protected TProcessor processor; - - protected TProtocolFactory inputProtocolFactory; - protected TProtocolFactory outputProtocolFactory; - - protected const string contentType = "application/x-thrift"; - protected System.Text.Encoding encoding = System.Text.Encoding.UTF8; - - public THttpHandler(TProcessor processor) - : this(processor, new TBinaryProtocol.Factory()) - { - - } - - public THttpHandler(TProcessor processor, TProtocolFactory protocolFactory) - : this(processor, protocolFactory, protocolFactory) - { - - } - - public THttpHandler(TProcessor processor, TProtocolFactory inputProtocolFactory, TProtocolFactory outputProtocolFactory) - { - this.processor = processor; - this.inputProtocolFactory = inputProtocolFactory; - this.outputProtocolFactory = outputProtocolFactory; - } - - public void ProcessRequest(HttpListenerContext context) - { - context.Response.ContentType = contentType; - context.Response.ContentEncoding = encoding; - ProcessRequest(context.Request.InputStream, context.Response.OutputStream); - } - - public void ProcessRequest(HttpContext context) - { - context.Response.ContentType = contentType; - context.Response.ContentEncoding = encoding; - ProcessRequest(context.Request.InputStream, context.Response.OutputStream); - } - - public void ProcessRequest(Stream input, Stream output) - { - TTransport transport = new TStreamTransport(input,output); - - try - { - var inputProtocol = inputProtocolFactory.GetProtocol(transport); - var outputProtocol = outputProtocolFactory.GetProtocol(transport); - - while (processor.Process(inputProtocol, outputProtocol)) - { - } - } - catch (TTransportException) - { - // Client died, just move on - } - finally - { - transport.Close(); - } - } - - public bool IsReusable - { - get { return true; } - } - } -} diff --git a/lib/csharp/src/Transport/THttpTaskAsyncHandler.cs b/lib/csharp/src/Transport/THttpTaskAsyncHandler.cs deleted file mode 100644 index e491f32cb98..00000000000 --- a/lib/csharp/src/Transport/THttpTaskAsyncHandler.cs +++ /dev/null @@ -1,97 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - * - * Contains some contributions under the Thrift Software License. - * Please see doc/old-thrift-license.txt in the Thrift distribution for - * details. - */ - -using System.Threading.Tasks; -using System.Web; -using Thrift.Protocol; - -namespace Thrift.Transport -{ - /// - /// An async task based HTTP handler for processing thrift services. - /// - public class THttpTaskAsyncHandler : HttpTaskAsyncHandler - { - private readonly TAsyncProcessor _processor; - private readonly TProtocolFactory _inputProtocolFactory; - private readonly TProtocolFactory _outputProtocolFactory; - - /// - /// Initializes a new instance of the class - /// using the for both input and output streams. - /// - /// The async processor implementation. - public THttpTaskAsyncHandler(TAsyncProcessor processor) - : this(processor, new TBinaryProtocol.Factory()) - { - } - - /// - /// Initializes a new instance of the class - /// using for both input and output streams. - /// - /// The async processor implementation. - /// The protocol factory. - public THttpTaskAsyncHandler(TAsyncProcessor processor, TProtocolFactory protocolFactory) - : this(processor, protocolFactory, protocolFactory) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// The async processor implementation. - /// The input protocol factory. - /// The output protocol factory. - public THttpTaskAsyncHandler(TAsyncProcessor processor, TProtocolFactory inputProtocolFactory, - TProtocolFactory outputProtocolFactory) - { - _processor = processor; - _inputProtocolFactory = inputProtocolFactory; - _outputProtocolFactory = outputProtocolFactory; - } - - public override async Task ProcessRequestAsync(HttpContext context) - { - var transport = new TStreamTransport(context.Request.InputStream, context.Response.OutputStream); - - try - { - var input = _inputProtocolFactory.GetProtocol(transport); - var output = _outputProtocolFactory.GetProtocol(transport); - - while (await _processor.ProcessAsync(input, output)) - { - } - } - catch (TTransportException) - { - // Client died, just move on - } - finally - { - transport.Close(); - } - } - } -} diff --git a/lib/csharp/src/Transport/TMemoryBuffer.cs b/lib/csharp/src/Transport/TMemoryBuffer.cs deleted file mode 100644 index 303d083299e..00000000000 --- a/lib/csharp/src/Transport/TMemoryBuffer.cs +++ /dev/null @@ -1,117 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -using System; -using System.IO; -using System.Reflection; -using Thrift.Protocol; - -namespace Thrift.Transport -{ - public class TMemoryBuffer : TTransport - { - - private readonly MemoryStream byteStream; - - public TMemoryBuffer() - { - byteStream = new MemoryStream(); - } - - public TMemoryBuffer(byte[] buf) - { - byteStream = new MemoryStream(buf); - } - - public override void Open() - { - /** do nothing **/ - } - - public override void Close() - { - /** do nothing **/ - } - - public override int Read(byte[] buf, int off, int len) - { - return byteStream.Read(buf, off, len); - } - - public override void Write(byte[] buf, int off, int len) - { - byteStream.Write(buf, off, len); - } - - public byte[] GetBuffer() - { - return byteStream.ToArray(); - } - - - public override bool IsOpen - { - get { return true; } - } - - public static byte[] Serialize(TAbstractBase s) - { - var t = new TMemoryBuffer(); - var p = new TBinaryProtocol(t); - - s.Write(p); - - return t.GetBuffer(); - } - - public static T DeSerialize(byte[] buf) where T : TAbstractBase - { - var trans = new TMemoryBuffer(buf); - var p = new TBinaryProtocol(trans); - if (typeof(TBase).IsAssignableFrom(typeof(T))) - { - var method = typeof(T).GetMethod("Read", BindingFlags.Instance | BindingFlags.Public); - var t = Activator.CreateInstance(); - method.Invoke(t, new object[] { p }); - return t; - } - else - { - var method = typeof(T).GetMethod("Read", BindingFlags.Static | BindingFlags.Public); - return (T)method.Invoke(null, new object[] { p }); - } - } - - private bool _IsDisposed; - - // IDisposable - protected override void Dispose(bool disposing) - { - if (!_IsDisposed) - { - if (disposing) - { - if (byteStream != null) - byteStream.Dispose(); - } - } - _IsDisposed = true; - } - } -} diff --git a/lib/csharp/src/Transport/TNamedPipeClientTransport.cs b/lib/csharp/src/Transport/TNamedPipeClientTransport.cs deleted file mode 100644 index 49a50aa5b02..00000000000 --- a/lib/csharp/src/Transport/TNamedPipeClientTransport.cs +++ /dev/null @@ -1,111 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - * - * Contains some contributions under the Thrift Software License. - * Please see doc/old-thrift-license.txt in the Thrift distribution for - * details. - */ - -using System; -using System.IO.Pipes; -using System.Threading; - -namespace Thrift.Transport -{ - public class TNamedPipeClientTransport : TTransport - { - private NamedPipeClientStream client; - private string ServerName; - private string PipeName; - private int ConnectTimeout; - - public TNamedPipeClientTransport(string pipe, int timeout = Timeout.Infinite) - { - ServerName = "."; - PipeName = pipe; - ConnectTimeout = timeout; - } - - public TNamedPipeClientTransport(string server, string pipe, int timeout = Timeout.Infinite) - { - ServerName = (server != "") ? server : "."; - PipeName = pipe; - ConnectTimeout = timeout; - } - - public override bool IsOpen - { - get { return client != null && client.IsConnected; } - } - - public override void Open() - { - if (IsOpen) - { - throw new TTransportException(TTransportException.ExceptionType.AlreadyOpen); - } - client = new NamedPipeClientStream(ServerName, PipeName, PipeDirection.InOut, PipeOptions.None); - client.Connect(ConnectTimeout); - } - - public override void Close() - { - if (client != null) - { - client.Close(); - client = null; - } - } - - public override int Read(byte[] buf, int off, int len) - { - if (client == null) - { - throw new TTransportException(TTransportException.ExceptionType.NotOpen); - } - - return client.Read(buf, off, len); - } - - public override void Write(byte[] buf, int off, int len) - { - if (client == null) - { - throw new TTransportException(TTransportException.ExceptionType.NotOpen); - } - - // if necessary, send the data in chunks - // there's a system limit around 0x10000 bytes that we hit otherwise - // MSDN: "Pipe write operations across a network are limited to 65,535 bytes per write. For more information regarding pipes, see the Remarks section." - var nBytes = Math.Min(len, 15 * 4096); // 16 would exceed the limit - while (nBytes > 0) - { - client.Write(buf, off, nBytes); - - off += nBytes; - len -= nBytes; - nBytes = Math.Min(len, nBytes); - } - } - - protected override void Dispose(bool disposing) - { - client.Dispose(); - } - } -} diff --git a/lib/csharp/src/Transport/TNamedPipeServerTransport.cs b/lib/csharp/src/Transport/TNamedPipeServerTransport.cs deleted file mode 100644 index 32215cfc10f..00000000000 --- a/lib/csharp/src/Transport/TNamedPipeServerTransport.cs +++ /dev/null @@ -1,296 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - * - * Contains some contributions under the Thrift Software License. - * Please see doc/old-thrift-license.txt in the Thrift distribution for - * details. - */ - -using System; -using System.IO.Pipes; -using System.Threading; -using System.Security.Principal; - -namespace Thrift.Transport -{ - public class TNamedPipeServerTransport : TServerTransport - { - /// - /// This is the address of the Pipe on the localhost. - /// - private readonly string pipeAddress; - private NamedPipeServerStream stream = null; - private bool asyncMode = true; - - public TNamedPipeServerTransport(string pipeAddress) - { - this.pipeAddress = pipeAddress; - } - - public override void Listen() - { - // nothing to do here - } - - public override void Close() - { - if (stream != null) - { - try - { - stream.Close(); - stream.Dispose(); - } - finally - { - stream = null; - } - } - } - - private void EnsurePipeInstance() - { - if (stream == null) - { - var direction = PipeDirection.InOut; - var maxconn = NamedPipeServerStream.MaxAllowedServerInstances; - var mode = PipeTransmissionMode.Byte; - var options = asyncMode ? PipeOptions.Asynchronous : PipeOptions.None; - const int INBUF_SIZE = 4096; - const int OUTBUF_SIZE = 4096; - - // security - var security = new PipeSecurity(); - security.AddAccessRule( - new PipeAccessRule( - new SecurityIdentifier(WellKnownSidType.WorldSid, null), - PipeAccessRights.Read | PipeAccessRights.Write | PipeAccessRights.Synchronize | PipeAccessRights.CreateNewInstance, - System.Security.AccessControl.AccessControlType.Allow - ) - ); - - try - { - stream = new NamedPipeServerStream(pipeAddress, direction, maxconn, mode, options, INBUF_SIZE, OUTBUF_SIZE, security); - } - catch (NotImplementedException) // Mono still does not support async, fallback to sync - { - if (asyncMode) - { - options &= (~PipeOptions.Asynchronous); - stream = new NamedPipeServerStream(pipeAddress, direction, maxconn, mode, options, INBUF_SIZE, OUTBUF_SIZE, security); - asyncMode = false; - } - else - { - throw; - } - } - - } - } - - protected override TTransport AcceptImpl() - { - try - { - EnsurePipeInstance(); - - if (asyncMode) - { - var evt = new ManualResetEvent(false); - Exception eOuter = null; - - stream.BeginWaitForConnection(asyncResult => - { - try - { - if (stream != null) - stream.EndWaitForConnection(asyncResult); - else - eOuter = new TTransportException(TTransportException.ExceptionType.Interrupted); - } - catch (Exception e) - { - if (stream != null) - eOuter = e; - else - eOuter = new TTransportException(TTransportException.ExceptionType.Interrupted, e.Message, e); - } - evt.Set(); - }, null); - - evt.WaitOne(); - - if (eOuter != null) - throw eOuter; // rethrow exception - } - else - { - stream.WaitForConnection(); - } - - var trans = new ServerTransport(stream,asyncMode); - stream = null; // pass ownership to ServerTransport - return trans; - } - catch (TTransportException) - { - Close(); - throw; - } - catch (Exception e) - { - Close(); - throw new TTransportException(TTransportException.ExceptionType.NotOpen, e.Message, e); - } - } - - private class ServerTransport : TTransport - { - private NamedPipeServerStream stream; - private bool asyncMode; - - public ServerTransport(NamedPipeServerStream stream, bool asyncMode) - { - this.stream = stream; - this.asyncMode = asyncMode; - } - - public override bool IsOpen - { - get { return stream != null && stream.IsConnected; } - } - - public override void Open() - { - } - - public override void Close() - { - if (stream != null) - stream.Close(); - } - - public override int Read(byte[] buf, int off, int len) - { - if (stream == null) - { - throw new TTransportException(TTransportException.ExceptionType.NotOpen); - } - - if (asyncMode) - { - Exception eOuter = null; - var evt = new ManualResetEvent(false); - int retval = 0; - - stream.BeginRead(buf, off, len, asyncResult => - { - try - { - if (stream != null) - retval = stream.EndRead(asyncResult); - else - eOuter = new TTransportException(TTransportException.ExceptionType.Interrupted); - } - catch (Exception e) - { - if (stream != null) - eOuter = e; - else - eOuter = new TTransportException(TTransportException.ExceptionType.Interrupted, e.Message, e); - } - evt.Set(); - }, null); - - evt.WaitOne(); - - if (eOuter != null) - throw eOuter; // rethrow exception - else - return retval; - } - else - { - return stream.Read(buf, off, len); - } - } - - public override void Write(byte[] buf, int off, int len) - { - if (stream == null) - { - throw new TTransportException(TTransportException.ExceptionType.NotOpen); - } - - // if necessary, send the data in chunks - // there's a system limit around 0x10000 bytes that we hit otherwise - // MSDN: "Pipe write operations across a network are limited to 65,535 bytes per write. For more information regarding pipes, see the Remarks section." - var nBytes = Math.Min(len, 15 * 4096); // 16 would exceed the limit - while (nBytes > 0) - { - - if (asyncMode) - { - Exception eOuter = null; - var evt = new ManualResetEvent(false); - - stream.BeginWrite(buf, off, nBytes, asyncResult => - { - try - { - if (stream != null) - stream.EndWrite(asyncResult); - else - eOuter = new TTransportException(TTransportException.ExceptionType.Interrupted); - } - catch (Exception e) - { - if (stream != null) - eOuter = e; - else - eOuter = new TTransportException(TTransportException.ExceptionType.Interrupted, e.Message, e); - } - evt.Set(); - }, null); - - evt.WaitOne(); - - if (eOuter != null) - throw eOuter; // rethrow exception - } - else - { - stream.Write(buf, off, nBytes); - } - - off += nBytes; - len -= nBytes; - nBytes = Math.Min(len, nBytes); - } - } - - protected override void Dispose(bool disposing) - { - if (stream != null) - stream.Dispose(); - } - } - } -} \ No newline at end of file diff --git a/lib/csharp/src/Transport/TServerSocket.cs b/lib/csharp/src/Transport/TServerSocket.cs deleted file mode 100644 index d8ec62ab3f4..00000000000 --- a/lib/csharp/src/Transport/TServerSocket.cs +++ /dev/null @@ -1,176 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - * - * Contains some contributions under the Thrift Software License. - * Please see doc/old-thrift-license.txt in the Thrift distribution for - * details. - */ - -using System; -using System.Net.Sockets; - - -namespace Thrift.Transport -{ - public class TServerSocket : TServerTransport - { - /// - /// Underlying server with socket. - /// - private TcpListener server = null; - - /// - /// Port to listen on. - /// - private int port = 0; - - /// - /// Timeout for client sockets from accept. - /// - private int clientTimeout = 0; - - /// - /// Whether or not to wrap new TSocket connections in buffers. - /// - private bool useBufferedSockets = false; - - /// - /// Creates a server socket from underlying socket object. - /// - public TServerSocket(TcpListener listener) - : this(listener, 0) - { - } - - /// - /// Creates a server socket from underlying socket object. - /// - public TServerSocket(TcpListener listener, int clientTimeout) - { - this.server = listener; - this.clientTimeout = clientTimeout; - } - - /// - /// Creates just a port listening server socket. - /// - public TServerSocket(int port) - : this(port, 0) - { - } - - /// - /// Creates just a port listening server socket. - /// - public TServerSocket(int port, int clientTimeout) - : this(port, clientTimeout, false) - { - } - - public TServerSocket(int port, int clientTimeout, bool useBufferedSockets) - { - this.port = port; - this.clientTimeout = clientTimeout; - this.useBufferedSockets = useBufferedSockets; - try - { - // Make server socket - this.server = TSocketVersionizer.CreateTcpListener(this.port); - this.server.Server.NoDelay = true; - } - catch (Exception ex) - { - server = null; - throw new TTransportException("Could not create ServerSocket on port " + this.port + ".", ex); - } - } - - public override void Listen() - { - // Make sure not to block on accept - if (server != null) - { - try - { - server.Start(); - } - catch (SocketException sx) - { - throw new TTransportException("Could not accept on listening socket: " + sx.Message, sx); - } - } - } - - protected override TTransport AcceptImpl() - { - if (server == null) - { - throw new TTransportException(TTransportException.ExceptionType.NotOpen, "No underlying server socket."); - } - try - { - TSocket result2 = null; - TcpClient result = server.AcceptTcpClient(); - try - { - result2 = new TSocket(result); - result2.Timeout = clientTimeout; - if (useBufferedSockets) - { - TBufferedTransport result3 = new TBufferedTransport(result2); - return result3; - } - else - { - return result2; - } - } - catch (System.Exception) - { - // If a TSocket was successfully created, then let - // it do proper cleanup of the TcpClient object. - if (result2 != null) - result2.Dispose(); - else // Otherwise, clean it up ourselves. - ((IDisposable)result).Dispose(); - throw; - } - } - catch (Exception ex) - { - throw new TTransportException(ex.ToString(), ex); - } - } - - public override void Close() - { - if (server != null) - { - try - { - server.Stop(); - } - catch (Exception ex) - { - throw new TTransportException("WARNING: Could not close server socket: " + ex, ex); - } - server = null; - } - } - } -} diff --git a/lib/csharp/src/Transport/TServerTransport.cs b/lib/csharp/src/Transport/TServerTransport.cs deleted file mode 100644 index e63880be1a0..00000000000 --- a/lib/csharp/src/Transport/TServerTransport.cs +++ /dev/null @@ -1,44 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - * - * Contains some contributions under the Thrift Software License. - * Please see doc/old-thrift-license.txt in the Thrift distribution for - * details. - */ - -using System; - -namespace Thrift.Transport -{ - public abstract class TServerTransport - { - public abstract void Listen(); - public abstract void Close(); - protected abstract TTransport AcceptImpl(); - - public TTransport Accept() - { - TTransport transport = AcceptImpl(); - if (transport == null) - { - throw new TTransportException("accept() may not return NULL"); - } - return transport; - } - } -} diff --git a/lib/csharp/src/Transport/TSilverlightSocket.cs b/lib/csharp/src/Transport/TSilverlightSocket.cs deleted file mode 100644 index 40469ab40ce..00000000000 --- a/lib/csharp/src/Transport/TSilverlightSocket.cs +++ /dev/null @@ -1,393 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - * - * Contains some contributions under the Thrift Software License. - * Please see doc/old-thrift-license.txt in the Thrift distribution for - * details. - */ - -/* only for silverlight */ -#if SILVERLIGHT - -using System; -using System.Net.Sockets; -using System.IO; -using System.Net; -using System.Threading; - -namespace Thrift.Transport -{ - public class TSilverlightSocket : TTransport - { - Socket socket = null; - static ManualResetEvent readAsyncComplete = new ManualResetEvent(false); - public event EventHandler connectHandler = null; - - // memory stream for write cache. - private MemoryStream outputStream = new MemoryStream(); - - private string host = null; - private int port = 0; - private int timeout = 0; - - // constructor - public TSilverlightSocket(string host, int port) - : this(host, port, 0) - { - } - - // constructor - public TSilverlightSocket(string host, int port, int timeout) - { - this.host = host; - this.port = port; - this.timeout = timeout; - - InitSocket(); - } - - private void InitSocket() - { - // Create a stream-based, TCP socket using the InterNetwork Address Family. - socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); - socket.NoDelay = true; - } - - public int Timeout - { - set - { - timeout = value; - } - } - - public string Host - { - get - { - return host; - } - } - - public int Port - { - get - { - return port; - } - } - - public override bool IsOpen - { - get - { - if (socket == null) - { - return false; - } - - return socket.Connected; - } - } - - public override void Open() - { - if (IsOpen) - { - throw new TTransportException(TTransportException.ExceptionType.AlreadyOpen, "Socket already connected"); - } - - if (string.IsNullOrEmpty(host)) - { - throw new TTransportException(TTransportException.ExceptionType.NotOpen, "Cannot open null host"); - } - - if (port <= 0) - { - throw new TTransportException(TTransportException.ExceptionType.NotOpen, "Cannot open without port"); - } - - if (socket == null) - { - InitSocket(); - } - - if (timeout == 0) // no timeout -> infinite - { - timeout = 10000; // set a default timeout for WP. - } - - { - // Create DnsEndPoint. The hostName and port are passed in to this method. - DnsEndPoint hostEntry = new DnsEndPoint(this.host, this.port); - - // Create a SocketAsyncEventArgs object to be used in the connection request - SocketAsyncEventArgs socketEventArg = new SocketAsyncEventArgs(); - socketEventArg.RemoteEndPoint = hostEntry; - - // Inline event handler for the Completed event. - // Note: This event handler was implemented inline in order to make this method self-contained. - socketEventArg.Completed += new EventHandler(delegate(object s, SocketAsyncEventArgs e) - { - if (connectHandler != null) - { - connectHandler(this, e); - } - }); - - // Make an asynchronous Connect request over the socket - socket.ConnectAsync(socketEventArg); - } - } - - public override int Read(byte[] buf, int off, int len) - { - bool _timeout = true; - string _error = null; - int _recvBytes = -1; - - if (socket == null) - { - throw new TTransportException(TTransportException.ExceptionType.NotOpen, "Socket is not open"); - } - - // Create SocketAsyncEventArgs context object - SocketAsyncEventArgs socketEventArg = new SocketAsyncEventArgs(); - socketEventArg.RemoteEndPoint = socket.RemoteEndPoint; - - // Setup the buffer to receive the data - socketEventArg.SetBuffer(buf, off, len); - - // Inline event handler for the Completed event. - // Note: This even handler was implemented inline in order to make - // this method self-contained. - socketEventArg.Completed += new EventHandler(delegate(object s, SocketAsyncEventArgs e) - { - _timeout = false; - - if (e.SocketError == SocketError.Success) - { - _recvBytes = e.BytesTransferred; - } - else - { - _error = e.SocketError.ToString(); - } - - readAsyncComplete.Set(); - }); - - // Sets the state of the event to nonsignaled, causing threads to block - readAsyncComplete.Reset(); - - // Make an asynchronous Receive request over the socket - socket.ReceiveAsync(socketEventArg); - - // Block the UI thread for a maximum of TIMEOUT_MILLISECONDS milliseconds. - // If no response comes back within this time then proceed - readAsyncComplete.WaitOne(this.timeout); - - if (_timeout) - { - throw new TTransportException(TTransportException.ExceptionType.TimedOut, "Socket recv timeout"); - } - - if (_error != null) - { - throw new TTransportException(TTransportException.ExceptionType.Unknown, _error); - } - - return _recvBytes; - } - - public override void Write(byte[] buf, int off, int len) - { - outputStream.Write(buf, off, len); - } - - private void beginFlush_Completed(object sender, SocketAsyncEventArgs e) - { - FlushAsyncResult flushAsyncResult = e.UserToken as FlushAsyncResult; - flushAsyncResult.UpdateStatusToComplete(); - flushAsyncResult.NotifyCallbackWhenAvailable(); - - if (e.SocketError != SocketError.Success) - { - throw new TTransportException(TTransportException.ExceptionType.Unknown, e.SocketError.ToString()); - } - } - - public override IAsyncResult BeginFlush(AsyncCallback callback, object state) - { - // Extract request and reset buffer - byte[] data = outputStream.ToArray(); - - FlushAsyncResult flushAsyncResult = new FlushAsyncResult(callback, state); - - SocketAsyncEventArgs socketEventArg = new SocketAsyncEventArgs(); - socketEventArg.RemoteEndPoint = socket.RemoteEndPoint; - socketEventArg.UserToken = flushAsyncResult; - - socketEventArg.Completed += beginFlush_Completed; - socketEventArg.SetBuffer(data, 0, data.Length); - - socket.SendAsync(socketEventArg); - - return flushAsyncResult; - } - - public override void EndFlush(IAsyncResult asyncResult) - { - try - { - var flushAsyncResult = (FlushAsyncResult)asyncResult; - - if (!flushAsyncResult.IsCompleted) - { - var waitHandle = flushAsyncResult.AsyncWaitHandle; - waitHandle.WaitOne(); - waitHandle.Close(); - } - - if (flushAsyncResult.AsyncException != null) - { - throw flushAsyncResult.AsyncException; - } - } - finally - { - outputStream = new MemoryStream(); - } - } - - // Copy from impl from THttpClient.cs - // Based on http://msmvps.com/blogs/luisabreu/archive/2009/06/15/multithreading-implementing-the-iasyncresult-interface.aspx - class FlushAsyncResult : IAsyncResult - { - private volatile Boolean _isCompleted; - private ManualResetEvent _evt; - private readonly AsyncCallback _cbMethod; - private readonly object _state; - - public FlushAsyncResult(AsyncCallback cbMethod, object state) - { - _cbMethod = cbMethod; - _state = state; - } - - internal byte[] Data { get; set; } - internal Socket Connection { get; set; } - internal TTransportException AsyncException { get; set; } - - public object AsyncState - { - get { return _state; } - } - - public WaitHandle AsyncWaitHandle - { - get { return GetEvtHandle(); } - } - - public bool CompletedSynchronously - { - get { return false; } - } - - public bool IsCompleted - { - get { return _isCompleted; } - } - - private readonly object _locker = new object(); - - private ManualResetEvent GetEvtHandle() - { - lock (_locker) - { - if (_evt == null) - { - _evt = new ManualResetEvent(false); - } - if (_isCompleted) - { - _evt.Set(); - } - } - return _evt; - } - - internal void UpdateStatusToComplete() - { - _isCompleted = true; //1. set _iscompleted to true - lock (_locker) - { - if (_evt != null) - { - _evt.Set(); //2. set the event, when it exists - } - } - } - - internal void NotifyCallbackWhenAvailable() - { - if (_cbMethod != null) - { - _cbMethod(this); - } - } - } - - public override void Close() - { - if (socket != null) - { - socket.Close(); - socket = null; - } - } - -#region " IDisposable Support " - private bool _IsDisposed; - - // IDisposable - protected override void Dispose(bool disposing) - { - if (!_IsDisposed) - { - if (disposing) - { - if (outputStream != null) - { - outputStream.Dispose(); - } - outputStream = null; - if (socket != null) - { - ((IDisposable)socket).Dispose(); - } - } - } - _IsDisposed = true; - } -#endregion - } -} - - -#endif diff --git a/lib/csharp/src/Transport/TSocket.cs b/lib/csharp/src/Transport/TSocket.cs deleted file mode 100644 index d8fa335ad07..00000000000 --- a/lib/csharp/src/Transport/TSocket.cs +++ /dev/null @@ -1,245 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - * - * Contains some contributions under the Thrift Software License. - * Please see doc/old-thrift-license.txt in the Thrift distribution for - * details. - */ - -using System; -using System.Net.Sockets; - -namespace Thrift.Transport -{ - public class TSocket : TStreamTransport - { - private TcpClient client = null; - private string host = null; - private int port = 0; - private int timeout = 0; - - public TSocket(TcpClient client) - { - this.client = client; - - if (IsOpen) - { - inputStream = client.GetStream(); - outputStream = client.GetStream(); - } - } - - public TSocket(string host, int port) - : this(host, port, 0) - { - } - - public TSocket(string host, int port, int timeout) - { - this.host = host; - this.port = port; - this.timeout = timeout; - - InitSocket(); - } - - private void InitSocket() - { - this.client = TSocketVersionizer.CreateTcpClient(); - this.client.ReceiveTimeout = client.SendTimeout = timeout; - this.client.Client.NoDelay = true; - } - - public int Timeout - { - set - { - client.ReceiveTimeout = client.SendTimeout = timeout = value; - } - } - - public TcpClient TcpClient - { - get - { - return client; - } - } - - public string Host - { - get - { - return host; - } - } - - public int Port - { - get - { - return port; - } - } - - public override bool IsOpen - { - get - { - if (client == null) - { - return false; - } - - return client.Connected; - } - } - - public override void Open() - { - if (IsOpen) - { - throw new TTransportException(TTransportException.ExceptionType.AlreadyOpen, "Socket already connected"); - } - - if (string.IsNullOrEmpty(host)) - { - throw new TTransportException(TTransportException.ExceptionType.NotOpen, "Cannot open null host"); - } - - if (port <= 0) - { - throw new TTransportException(TTransportException.ExceptionType.NotOpen, "Cannot open without port"); - } - - if (client == null) - { - InitSocket(); - } - - if (timeout == 0) // no timeout -> infinite - { - client.Connect(host, port); - } - else // we have a timeout -> use it - { - ConnectHelper hlp = new ConnectHelper(client); - IAsyncResult asyncres = client.BeginConnect(host, port, new AsyncCallback(ConnectCallback), hlp); - bool bConnected = asyncres.AsyncWaitHandle.WaitOne(timeout) && client.Connected; - if (!bConnected) - { - lock (hlp.Mutex) - { - if (hlp.CallbackDone) - { - asyncres.AsyncWaitHandle.Close(); - client.Close(); - } - else - { - hlp.DoCleanup = true; - client = null; - } - } - throw new TTransportException(TTransportException.ExceptionType.TimedOut, "Connect timed out"); - } - } - - inputStream = client.GetStream(); - outputStream = client.GetStream(); - } - - - static void ConnectCallback(IAsyncResult asyncres) - { - ConnectHelper hlp = asyncres.AsyncState as ConnectHelper; - lock (hlp.Mutex) - { - hlp.CallbackDone = true; - - try - { - if (hlp.Client.Client != null) - hlp.Client.EndConnect(asyncres); - } - catch (Exception) - { - // catch that away - } - - if (hlp.DoCleanup) - { - try - { - asyncres.AsyncWaitHandle.Close(); - } - catch (Exception) { } - - try - { - if (hlp.Client is IDisposable) - ((IDisposable)hlp.Client).Dispose(); - } - catch (Exception) { } - hlp.Client = null; - } - } - } - - private class ConnectHelper - { - public object Mutex = new object(); - public bool DoCleanup = false; - public bool CallbackDone = false; - public TcpClient Client; - public ConnectHelper(TcpClient client) - { - Client = client; - } - } - - public override void Close() - { - base.Close(); - if (client != null) - { - client.Close(); - client = null; - } - } - - #region " IDisposable Support " - private bool _IsDisposed; - - // IDisposable - protected override void Dispose(bool disposing) - { - if (!_IsDisposed) - { - if (disposing) - { - if (client != null) - ((IDisposable)client).Dispose(); - base.Dispose(disposing); - } - } - _IsDisposed = true; - } - #endregion - } -} diff --git a/lib/csharp/src/Transport/TSocketVersionizer.cs b/lib/csharp/src/Transport/TSocketVersionizer.cs deleted file mode 100644 index bf4c0e49dc5..00000000000 --- a/lib/csharp/src/Transport/TSocketVersionizer.cs +++ /dev/null @@ -1,78 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - * - * Contains some contributions under the Thrift Software License. - * Please see doc/old-thrift-license.txt in the Thrift distribution for - * details. - */ - -using System; -using System.Collections.Generic; -using System.Linq; -using System.Net.Sockets; -using System.Reflection; -using System.Text; -#if NET45 -using System.Threading.Tasks; -#endif - -namespace Thrift.Transport -{ - /// - /// PropertyInfo for the DualMode property of the System.Net.Sockets.Socket class. Used to determine if the sockets are capable of - /// automatic IPv4 and IPv6 handling. If DualMode is present the sockets automatically handle IPv4 and IPv6 connections. - /// If the DualMode is not available the system configuration determines whether IPv4 or IPv6 is used. - /// - internal static class TSocketVersionizer - { - /// - /// Creates a TcpClient according to the capabilitites of the used framework - /// - internal static TcpClient CreateTcpClient() - { - TcpClient client = null; - -#if NET45 - client = new TcpClient(AddressFamily.InterNetworkV6); - client.Client.DualMode = true; -#else - client = new TcpClient(AddressFamily.InterNetwork); -#endif - - return client; - } - - /// - /// Creates a TcpListener according to the capabilitites of the used framework. - /// - internal static TcpListener CreateTcpListener(Int32 port) - { - TcpListener listener = null; - -#if NET45 - listener = new TcpListener(System.Net.IPAddress.IPv6Any, port); - listener.Server.DualMode = true; -#else - - listener = new TcpListener(System.Net.IPAddress.Any, port); -#endif - - return listener; - } - } -} diff --git a/lib/csharp/src/Transport/TStreamTransport.cs b/lib/csharp/src/Transport/TStreamTransport.cs deleted file mode 100644 index 304599faa3e..00000000000 --- a/lib/csharp/src/Transport/TStreamTransport.cs +++ /dev/null @@ -1,128 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - * - * Contains some contributions under the Thrift Software License. - * Please see doc/old-thrift-license.txt in the Thrift distribution for - * details. - */ - -using System; -using System.IO; - -namespace Thrift.Transport -{ - public class TStreamTransport : TTransport - { - protected Stream inputStream; - protected Stream outputStream; - - protected TStreamTransport() - { - } - - public TStreamTransport(Stream inputStream, Stream outputStream) - { - this.inputStream = inputStream; - this.outputStream = outputStream; - } - - public Stream OutputStream - { - get { return outputStream; } - } - - public Stream InputStream - { - get { return inputStream; } - } - - public override bool IsOpen - { - get { return true; } - } - - public override void Open() - { - } - - public override void Close() - { - if (inputStream != null) - { - inputStream.Close(); - inputStream = null; - } - if (outputStream != null) - { - outputStream.Close(); - outputStream = null; - } - } - - public override int Read(byte[] buf, int off, int len) - { - if (inputStream == null) - { - throw new TTransportException(TTransportException.ExceptionType.NotOpen, "Cannot read from null inputstream"); - } - - return inputStream.Read(buf, off, len); - } - - public override void Write(byte[] buf, int off, int len) - { - if (outputStream == null) - { - throw new TTransportException(TTransportException.ExceptionType.NotOpen, "Cannot write to null outputstream"); - } - - outputStream.Write(buf, off, len); - } - - public override void Flush() - { - if (outputStream == null) - { - throw new TTransportException(TTransportException.ExceptionType.NotOpen, "Cannot flush null outputstream"); - } - - outputStream.Flush(); - } - - - #region " IDisposable Support " - private bool _IsDisposed; - - // IDisposable - protected override void Dispose(bool disposing) - { - if (!_IsDisposed) - { - if (disposing) - { - if (InputStream != null) - InputStream.Dispose(); - if (OutputStream != null) - OutputStream.Dispose(); - } - } - _IsDisposed = true; - } - #endregion - } -} diff --git a/lib/csharp/src/Transport/TTLSServerSocket.cs b/lib/csharp/src/Transport/TTLSServerSocket.cs deleted file mode 100644 index 716a97ca84a..00000000000 --- a/lib/csharp/src/Transport/TTLSServerSocket.cs +++ /dev/null @@ -1,223 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -using System; -using System.Net.Security; -using System.Net.Sockets; -using System.Security.Authentication; -using System.Security.Cryptography.X509Certificates; - -namespace Thrift.Transport -{ - /// - /// SSL Server Socket Wrapper Class - /// - public class TTLSServerSocket : TServerTransport - { - /// - /// Underlying tcp server - /// - private TcpListener server = null; - - /// - /// The port where the socket listen - /// - private int port = 0; - - /// - /// Timeout for the created server socket - /// - private readonly int clientTimeout; - - /// - /// Whether or not to wrap new TSocket connections in buffers - /// - private bool useBufferedSockets = false; - - /// - /// The servercertificate with the private- and public-key - /// - private X509Certificate serverCertificate; - - /// - /// The function to validate the client certificate. - /// - private RemoteCertificateValidationCallback clientCertValidator; - - /// - /// The function to determine which certificate to use. - /// - private LocalCertificateSelectionCallback localCertificateSelectionCallback; - - /// - /// The SslProtocols value that represents the protocol used for authentication. - /// - private readonly SslProtocols sslProtocols; - - /// - /// Initializes a new instance of the class. - /// - /// The port where the server runs. - /// The certificate object. - public TTLSServerSocket(int port, X509Certificate2 certificate) - : this(port, 0, certificate) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// The port where the server runs. - /// Send/receive timeout. - /// The certificate object. - public TTLSServerSocket(int port, int clientTimeout, X509Certificate2 certificate) - : this(port, clientTimeout, false, certificate) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// The port where the server runs. - /// Send/receive timeout. - /// If set to true [use buffered sockets]. - /// The certificate object. - /// The certificate validator. - /// The callback to select which certificate to use. - /// The SslProtocols value that represents the protocol used for authentication. - public TTLSServerSocket( - int port, - int clientTimeout, - bool useBufferedSockets, - X509Certificate2 certificate, - RemoteCertificateValidationCallback clientCertValidator = null, - LocalCertificateSelectionCallback localCertificateSelectionCallback = null, - // TODO: Enable Tls11 and Tls12 (TLS 1.1 and 1.2) by default once we start using .NET 4.5+. - SslProtocols sslProtocols = SslProtocols.Tls) - { - if (!certificate.HasPrivateKey) - { - throw new TTransportException(TTransportException.ExceptionType.Unknown, "Your server-certificate needs to have a private key"); - } - - this.port = port; - this.clientTimeout = clientTimeout; - this.serverCertificate = certificate; - this.useBufferedSockets = useBufferedSockets; - this.clientCertValidator = clientCertValidator; - this.localCertificateSelectionCallback = localCertificateSelectionCallback; - this.sslProtocols = sslProtocols; - try - { - // Create server socket - this.server = TSocketVersionizer.CreateTcpListener(this.port); - this.server.Server.NoDelay = true; - } - catch (Exception ex) - { - server = null; - throw new TTransportException("Could not create ServerSocket on port " + this.port + ".", ex); - } - } - - /// - /// Starts the server. - /// - public override void Listen() - { - // Make sure accept is not blocking - if (this.server != null) - { - try - { - this.server.Start(); - } - catch (SocketException sx) - { - throw new TTransportException("Could not accept on listening socket: " + sx.Message, sx); - } - } - } - - /// - /// Callback for Accept Implementation - /// - /// - /// TTransport-object. - /// - protected override TTransport AcceptImpl() - { - if (this.server == null) - { - throw new TTransportException(TTransportException.ExceptionType.NotOpen, "No underlying server socket."); - } - - try - { - TcpClient client = this.server.AcceptTcpClient(); - client.SendTimeout = client.ReceiveTimeout = this.clientTimeout; - - //wrap the client in an SSL Socket passing in the SSL cert - TTLSSocket socket = new TTLSSocket( - client, - this.serverCertificate, - true, - this.clientCertValidator, - this.localCertificateSelectionCallback, - this.sslProtocols); - - socket.setupTLS(); - - if (useBufferedSockets) - { - TBufferedTransport trans = new TBufferedTransport(socket); - return trans; - } - else - { - return socket; - } - - } - catch (Exception ex) - { - throw new TTransportException(ex.ToString(), ex); - } - } - - /// - /// Stops the Server - /// - public override void Close() - { - if (this.server != null) - { - try - { - this.server.Stop(); - } - catch (Exception ex) - { - throw new TTransportException("WARNING: Could not close server socket: " + ex, ex); - } - this.server = null; - } - } - } -} diff --git a/lib/csharp/src/Transport/TTLSSocket.cs b/lib/csharp/src/Transport/TTLSSocket.cs deleted file mode 100644 index fd019c372fa..00000000000 --- a/lib/csharp/src/Transport/TTLSSocket.cs +++ /dev/null @@ -1,371 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -using System; -using System.Net.Security; -using System.Net.Sockets; -using System.Security.Authentication; -using System.Security.Cryptography.X509Certificates; - -namespace Thrift.Transport -{ - /// - /// SSL Socket Wrapper class - /// - public class TTLSSocket : TStreamTransport - { - /// - /// Internal TCP Client - /// - private TcpClient client; - - /// - /// The host - /// - private string host; - - /// - /// The port - /// - private int port; - - /// - /// The timeout for the connection - /// - private int timeout; - - /// - /// Internal SSL Stream for IO - /// - private SslStream secureStream; - - /// - /// Defines wheter or not this socket is a server socket
- /// This is used for the TLS-authentication - ///
- private bool isServer; - - /// - /// The certificate - /// - private X509Certificate certificate; - - /// - /// User defined certificate validator. - /// - private RemoteCertificateValidationCallback certValidator; - - /// - /// The function to determine which certificate to use. - /// - private LocalCertificateSelectionCallback localCertificateSelectionCallback; - - /// - /// The SslProtocols value that represents the protocol used for authentication.SSL protocols to be used. - /// - private readonly SslProtocols sslProtocols; - - /// - /// Initializes a new instance of the class. - /// - /// An already created TCP-client - /// The certificate. - /// if set to true [is server]. - /// User defined cert validator. - /// The callback to select which certificate to use. - /// The SslProtocols value that represents the protocol used for authentication. - public TTLSSocket( - TcpClient client, - X509Certificate certificate, - bool isServer = false, - RemoteCertificateValidationCallback certValidator = null, - LocalCertificateSelectionCallback localCertificateSelectionCallback = null, - // TODO: Enable Tls11 and Tls12 (TLS 1.1 and 1.2) by default once we start using .NET 4.5+. - SslProtocols sslProtocols = SslProtocols.Tls) - { - this.client = client; - this.certificate = certificate; - this.certValidator = certValidator; - this.localCertificateSelectionCallback = localCertificateSelectionCallback; - this.sslProtocols = sslProtocols; - this.isServer = isServer; - if (isServer && certificate == null) - { - throw new ArgumentException("TTLSSocket needs certificate to be used for server", "certificate"); - } - - if (IsOpen) - { - base.inputStream = client.GetStream(); - base.outputStream = client.GetStream(); - } - } - - /// - /// Initializes a new instance of the class. - /// - /// The host, where the socket should connect to. - /// The port. - /// The certificate path. - /// User defined cert validator. - /// The callback to select which certificate to use. - /// The SslProtocols value that represents the protocol used for authentication. - public TTLSSocket( - string host, - int port, - string certificatePath, - RemoteCertificateValidationCallback certValidator = null, - LocalCertificateSelectionCallback localCertificateSelectionCallback = null, - SslProtocols sslProtocols = SslProtocols.Tls) - : this(host, port, 0, X509Certificate.CreateFromCertFile(certificatePath), certValidator, localCertificateSelectionCallback, sslProtocols) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// The host, where the socket should connect to. - /// The port. - /// The certificate. - /// User defined cert validator. - /// The callback to select which certificate to use. - /// The SslProtocols value that represents the protocol used for authentication. - public TTLSSocket( - string host, - int port, - X509Certificate certificate = null, - RemoteCertificateValidationCallback certValidator = null, - LocalCertificateSelectionCallback localCertificateSelectionCallback = null, - SslProtocols sslProtocols = SslProtocols.Tls) - : this(host, port, 0, certificate, certValidator, localCertificateSelectionCallback, sslProtocols) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// The host, where the socket should connect to. - /// The port. - /// The timeout. - /// The certificate. - /// User defined cert validator. - /// The callback to select which certificate to use. - /// The SslProtocols value that represents the protocol used for authentication. - public TTLSSocket( - string host, - int port, - int timeout, - X509Certificate certificate, - RemoteCertificateValidationCallback certValidator = null, - LocalCertificateSelectionCallback localCertificateSelectionCallback = null, - SslProtocols sslProtocols = SslProtocols.Tls) - { - this.host = host; - this.port = port; - this.timeout = timeout; - this.certificate = certificate; - this.certValidator = certValidator; - this.localCertificateSelectionCallback = localCertificateSelectionCallback; - this.sslProtocols = sslProtocols; - - InitSocket(); - } - - /// - /// Creates the TcpClient and sets the timeouts - /// - private void InitSocket() - { - client = TSocketVersionizer.CreateTcpClient(); - client.ReceiveTimeout = client.SendTimeout = timeout; - client.Client.NoDelay = true; - } - - /// - /// Sets Send / Recv Timeout for IO - /// - public int Timeout - { - set - { - this.client.ReceiveTimeout = this.client.SendTimeout = this.timeout = value; - } - } - - /// - /// Gets the TCP client. - /// - public TcpClient TcpClient - { - get - { - return client; - } - } - - /// - /// Gets the host. - /// - public string Host - { - get - { - return host; - } - } - - /// - /// Gets the port. - /// - public int Port - { - get - { - return port; - } - } - - /// - /// Gets a value indicating whether TCP Client is Cpen - /// - public override bool IsOpen - { - get - { - if (this.client == null) - { - return false; - } - - return this.client.Connected; - } - } - - /// - /// Validates the certificates!
- ///
- /// The sender-object. - /// The used certificate. - /// The certificate chain. - /// An enum, which lists all the errors from the .NET certificate check. - /// - private bool DefaultCertificateValidator(object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslValidationErrors) - { - return (sslValidationErrors == SslPolicyErrors.None); - } - - /// - /// Connects to the host and starts the routine, which sets up the TLS - /// - public override void Open() - { - if (IsOpen) - { - throw new TTransportException(TTransportException.ExceptionType.AlreadyOpen, "Socket already connected"); - } - - if (string.IsNullOrEmpty(host)) - { - throw new TTransportException(TTransportException.ExceptionType.NotOpen, "Cannot open null host"); - } - - if (port <= 0) - { - throw new TTransportException(TTransportException.ExceptionType.NotOpen, "Cannot open without port"); - } - - if (client == null) - { - InitSocket(); - } - - client.Connect(host, port); - - setupTLS(); - } - - /// - /// Creates a TLS-stream and lays it over the existing socket - /// - public void setupTLS() - { - RemoteCertificateValidationCallback validator = this.certValidator ?? DefaultCertificateValidator; - - if (this.localCertificateSelectionCallback != null) - { - this.secureStream = new SslStream( - this.client.GetStream(), - false, - validator, - this.localCertificateSelectionCallback - ); - } - else - { - this.secureStream = new SslStream( - this.client.GetStream(), - false, - validator - ); - } - - try - { - if (isServer) - { - // Server authentication - this.secureStream.AuthenticateAsServer(this.certificate, this.certValidator != null, sslProtocols, true); - } - else - { - // Client authentication - X509CertificateCollection certs = certificate != null ? new X509CertificateCollection { certificate } : new X509CertificateCollection(); - this.secureStream.AuthenticateAsClient(host, certs, sslProtocols, true); - } - } - catch (Exception) - { - this.Close(); - throw; - } - - inputStream = this.secureStream; - outputStream = this.secureStream; - } - - /// - /// Closes the SSL Socket - /// - public override void Close() - { - base.Close(); - if (this.client != null) - { - this.client.Close(); - this.client = null; - } - - if (this.secureStream != null) - { - this.secureStream.Close(); - this.secureStream = null; - } - } - } -} diff --git a/lib/csharp/src/Transport/TTransport.cs b/lib/csharp/src/Transport/TTransport.cs deleted file mode 100644 index 5e4ac22ea90..00000000000 --- a/lib/csharp/src/Transport/TTransport.cs +++ /dev/null @@ -1,146 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - * - * Contains some contributions under the Thrift Software License. - * Please see doc/old-thrift-license.txt in the Thrift distribution for - * details. - */ - -using System; -using System.IO; - -namespace Thrift.Transport -{ - public abstract class TTransport : IDisposable - { - public abstract bool IsOpen - { - get; - } - - private byte[] _peekBuffer = new byte[1]; - private bool _hasPeekByte; - - public bool Peek() - { - //If we already have a byte read but not consumed, do nothing. - if (_hasPeekByte) - return true; - - //If transport closed we can't peek. - if (!IsOpen) - return false; - - //Try to read one byte. If succeeds we will need to store it for the next read. - try - { - int bytes = Read(_peekBuffer, 0, 1); - if (bytes == 0) - return false; - } - catch (IOException) - { - return false; - } - - _hasPeekByte = true; - return true; - } - - public abstract void Open(); - - public abstract void Close(); - - protected static void ValidateBufferArgs(byte[] buf, int off, int len) - { - if (buf == null) - throw new ArgumentNullException("buf"); - if (off < 0) - throw new ArgumentOutOfRangeException("Buffer offset is smaller than zero."); - if (len < 0) - throw new ArgumentOutOfRangeException("Buffer length is smaller than zero."); - if (off + len > buf.Length) - throw new ArgumentOutOfRangeException("Not enough data."); - } - - public abstract int Read(byte[] buf, int off, int len); - - public int ReadAll(byte[] buf, int off, int len) - { - ValidateBufferArgs(buf, off, len); - int got = 0; - - //If we previously peeked a byte, we need to use that first. - if (_hasPeekByte) - { - buf[off + got++] = _peekBuffer[0]; - _hasPeekByte = false; - } - - while (got < len) - { - int ret = Read(buf, off + got, len - got); - if (ret <= 0) - { - throw new TTransportException( - TTransportException.ExceptionType.EndOfFile, - "Cannot read, Remote side has closed"); - } - got += ret; - } - return got; - } - - public virtual void Write(byte[] buf) - { - Write(buf, 0, buf.Length); - } - - public abstract void Write(byte[] buf, int off, int len); - - public virtual void Flush() - { - } - - public virtual IAsyncResult BeginFlush(AsyncCallback callback, object state) - { - throw new TTransportException( - TTransportException.ExceptionType.Unknown, - "Asynchronous operations are not supported by this transport."); - } - - public virtual void EndFlush(IAsyncResult asyncResult) - { - throw new TTransportException( - TTransportException.ExceptionType.Unknown, - "Asynchronous operations are not supported by this transport."); - } - - #region " IDisposable Support " - // IDisposable - protected abstract void Dispose(bool disposing); - - public void Dispose() - { - // Do not change this code. Put cleanup code in Dispose(ByVal disposing As Boolean) above. - Dispose(true); - GC.SuppressFinalize(this); - } - #endregion - } -} diff --git a/lib/csharp/src/Transport/TTransportException.cs b/lib/csharp/src/Transport/TTransportException.cs deleted file mode 100644 index 7f6cc1889ea..00000000000 --- a/lib/csharp/src/Transport/TTransportException.cs +++ /dev/null @@ -1,69 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - * - * Contains some contributions under the Thrift Software License. - * Please see doc/old-thrift-license.txt in the Thrift distribution for - * details. - */ - -using System; - -namespace Thrift.Transport -{ - public class TTransportException : TException - { - protected ExceptionType type; - - public TTransportException() - : base() - { - } - - public TTransportException(ExceptionType type) - : this() - { - this.type = type; - } - - public TTransportException(ExceptionType type, string message, Exception inner = null) - : base(message, inner) - { - this.type = type; - } - - public TTransportException(string message, Exception inner = null) - : base(message, inner) - { - } - - public ExceptionType Type - { - get { return type; } - } - - public enum ExceptionType - { - Unknown, - NotOpen, - AlreadyOpen, - TimedOut, - EndOfFile, - Interrupted - } - } -} diff --git a/lib/csharp/src/Transport/TTransportFactory.cs b/lib/csharp/src/Transport/TTransportFactory.cs deleted file mode 100644 index 47a0c62651a..00000000000 --- a/lib/csharp/src/Transport/TTransportFactory.cs +++ /dev/null @@ -1,42 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - * - * Contains some contributions under the Thrift Software License. - * Please see doc/old-thrift-license.txt in the Thrift distribution for - * details. - */ - -using System; - -namespace Thrift.Transport -{ - /// - /// From Mark Slee & Aditya Agarwal of Facebook: - /// Factory class used to create wrapped instance of Transports. - /// This is used primarily in servers, which get Transports from - /// a ServerTransport and then may want to mutate them (i.e. create - /// a BufferedTransport from the underlying base transport) - /// - public class TTransportFactory - { - public virtual TTransport GetTransport(TTransport trans) - { - return trans; - } - } -} diff --git a/lib/csharp/test/JSON/JSONTest.csproj b/lib/csharp/test/JSON/JSONTest.csproj deleted file mode 100644 index f07d43eece1..00000000000 --- a/lib/csharp/test/JSON/JSONTest.csproj +++ /dev/null @@ -1,85 +0,0 @@ - - - - - Debug - x86 - 8.0.30703 - 2.0 - {E37A0034-DCBF-4886-A0DA-25A03D12D975} - Exe - Properties - JSONTest - JSONTest - v4.0 - - - 512 - - - x86 - true - full - false - bin\Debug\ - DEBUG;TRACE - prompt - 4 - - - x86 - pdbonly - true - bin\Release\ - TRACE - prompt - 4 - - - - - - - - - - - - - - - - - - - - {499EB63C-D74C-47E8-AE48-A2FC94538E9D} - Thrift - - - - - \ No newline at end of file diff --git a/lib/csharp/test/JSON/Program.cs b/lib/csharp/test/JSON/Program.cs deleted file mode 100644 index f61388ae7c2..00000000000 --- a/lib/csharp/test/JSON/Program.cs +++ /dev/null @@ -1,95 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.IO; -using System.Linq; -using System.Text; -using Thrift.Protocol; -using Thrift.Transport; - -namespace JSONTest -{ - class Program - { - static void Main(string[] args) - { - TestThrift2365(); // JSON binary decodes too much data - TestThrift2336(); // hex encoding using \uXXXX where 0xXXXX > 0xFF - TestThrift3403(); // JSON escaped unicode surrogate pair support. - } - - - public static void TestThrift2365() - { - var rnd = new Random(); - for (var len = 0; len < 10; ++len) - { - byte[] dataWritten = new byte[len]; - rnd.NextBytes(dataWritten); - - Stream stm = new MemoryStream(); - TTransport trans = new TStreamTransport(null, stm); - TProtocol prot = new TJSONProtocol(trans); - prot.WriteBinary(dataWritten); - - stm.Position = 0; - trans = new TStreamTransport(stm, null); - prot = new TJSONProtocol(trans); - byte[] dataRead = prot.ReadBinary(); - - Debug.Assert(dataRead.Length == dataWritten.Length); - for (var i = 0; i < dataRead.Length; ++i) - Debug.Assert(dataRead[i] == dataWritten[i]); - } - } - - - public static void TestThrift2336() - { - const string RUSSIAN_TEXT = "\u0420\u0443\u0441\u0441\u043a\u043e\u0435 \u041d\u0430\u0437\u0432\u0430\u043d\u0438\u0435"; - const string RUSSIAN_JSON = "\"\\u0420\\u0443\\u0441\\u0441\\u043a\\u043e\\u0435 \\u041d\\u0430\\u0437\\u0432\\u0430\\u043d\\u0438\\u0435\""; - - // prepare buffer with JSON data - byte[] rawBytes = new byte[RUSSIAN_JSON.Length]; - for (var i = 0; i < RUSSIAN_JSON.Length; ++i) - rawBytes[i] = (byte)(RUSSIAN_JSON[i] & (char)0xFF); // only low bytes - - // parse and check - var stm = new MemoryStream(rawBytes); - var trans = new TStreamTransport(stm, null); - var prot = new TJSONProtocol(trans); - Debug.Assert(prot.ReadString() == RUSSIAN_TEXT, "reading JSON with hex-encoded chars > 8 bit"); - } - - public static void TestThrift3403() - { - string GCLEF_TEXT = "\ud834\udd1e"; - const string GCLEF_JSON = "\"\\ud834\\udd1e\""; - - // parse and check - var stm = new MemoryStream(Encoding.UTF8.GetBytes(GCLEF_JSON)); - var trans = new TStreamTransport(stm, null); - var prot = new TJSONProtocol(trans); - Debug.Assert(prot.ReadString() == GCLEF_TEXT, "reading JSON with surrogate pair hex-encoded chars"); - } - } -} diff --git a/lib/csharp/test/JSON/Properties/AssemblyInfo.cs b/lib/csharp/test/JSON/Properties/AssemblyInfo.cs deleted file mode 100644 index fdff4a1a5c4..00000000000 --- a/lib/csharp/test/JSON/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -using System.Reflection; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; - -// Allgemeine Informationen über eine Assembly werden über die folgenden -// Attribute gesteuert. Ändern Sie diese Attributwerte, um die Informationen zu ändern, -// die mit einer Assembly verknüpft sind. -[assembly: AssemblyTitle("JSONTest")] -[assembly: AssemblyDescription("")] -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("The Apache Software Foundation")] -[assembly: AssemblyProduct("Thrift")] -[assembly: AssemblyCopyright("The Apache Software Foundation")] -[assembly: AssemblyTrademark("")] -[assembly: AssemblyCulture("")] - -// Durch Festlegen von ComVisible auf "false" werden die Typen in dieser Assembly unsichtbar -// für COM-Komponenten. Wenn Sie auf einen Typ in dieser Assembly von -// COM zugreifen müssen, legen Sie das ComVisible-Attribut für diesen Typ auf "true" fest. -[assembly: ComVisible(false)] - -// Die folgende GUID bestimmt die ID der Typbibliothek, wenn dieses Projekt für COM verfügbar gemacht wird -[assembly: Guid("2b2e7d56-3e65-4368-92d7-e34d56b7105e")] - -// Versionsinformationen für eine Assembly bestehen aus den folgenden vier Werten: -// -// Hauptversion -// Nebenversion -// Buildnummer -// Revision -// -// Sie können alle Werte angeben oder die standardmäßigen Build- und Revisionsnummern -// übernehmen, indem Sie "*" eingeben: -// [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("1.0.0.0")] -[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/lib/csharp/test/JSON/app.config b/lib/csharp/test/JSON/app.config deleted file mode 100644 index 9c1919d4fd3..00000000000 --- a/lib/csharp/test/JSON/app.config +++ /dev/null @@ -1,21 +0,0 @@ - - - - diff --git a/lib/csharp/test/Multiplex/Client/Multiplex.Test.Client.cs b/lib/csharp/test/Multiplex/Client/Multiplex.Test.Client.cs deleted file mode 100644 index c810a08917f..00000000000 --- a/lib/csharp/test/Multiplex/Client/Multiplex.Test.Client.cs +++ /dev/null @@ -1,82 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -using System; -using System.Collections.Generic; -using Thrift.Collections; -using Thrift.Transport; -using Thrift.Protocol; -using Thrift.Server; -using Thrift; -using Test.Multiplex; - -namespace Test.Multiplex.Client -{ - public class TestClient - { - static void Execute(int port) - { - try - { - TTransport trans; - trans = new TSocket("localhost", port); - trans = new TFramedTransport(trans); - trans.Open(); - - TProtocol Protocol = new TBinaryProtocol(trans, true, true); - - TMultiplexedProtocol multiplex; - - multiplex = new TMultiplexedProtocol(Protocol, Constants.NAME_BENCHMARKSERVICE); - BenchmarkService.Iface bench = new BenchmarkService.Client(multiplex); - - multiplex = new TMultiplexedProtocol(Protocol, Constants.NAME_AGGR); - Aggr.Iface aggr = new Aggr.Client(multiplex); - - for (sbyte i = 1; 10 >= i; ++i) - { - aggr.addValue(bench.fibonacci(i)); - } - - foreach (int k in aggr.getValues()) - { - Console.Write(k.ToString() + " "); - Console.WriteLine(""); - } - trans.Close(); - } - catch (Exception e) - { - Console.WriteLine(e.Message); - } - } - - static void Main(string[] args) - { - int port = 9090; - if (args.Length > 0) - { - port = ushort.Parse(args[0]); - } - Execute(port); - Console.WriteLine("done."); - } - } -} - diff --git a/lib/csharp/test/Multiplex/Client/MultiplexClient.csproj b/lib/csharp/test/Multiplex/Client/MultiplexClient.csproj deleted file mode 100644 index 4df1cbcf0c0..00000000000 --- a/lib/csharp/test/Multiplex/Client/MultiplexClient.csproj +++ /dev/null @@ -1,148 +0,0 @@ - - - - - Debug - AnyCPU - 9.0.21022 - 2.0 - {5E91DA17-E548-415F-8C9F-9E84EDF8EE06} - Exe - Properties - MultiplexClient - MultiplexClient - v3.5 - 512 - false - - - 3.5 - - publish\ - true - Disk - false - Foreground - 7 - Days - false - false - true - 0 - 0.12.0.%2a - false - true - - - true - full - false - ..\bin\Debug\ - DEBUG;TRACE - prompt - 4 - AllRules.ruleset - - - pdbonly - true - ..\bin\Release\ - TRACE - prompt - 4 - AllRules.ruleset - - - - - - - Multiplex.Test.Common.cs - - - - - - - - - - False - .NET Framework 3.5 SP1 Client Profile - false - - - False - .NET Framework 2.0 %28x86%29 - false - - - False - .NET Framework 3.0 %28x86%29 - false - - - False - .NET Framework 3.5 - true - - - False - .NET Framework 3.5 SP1 - false - - - False - Windows Installer 3.1 - true - - - - - {499EB63C-D74C-47E8-AE48-A2FC94538E9D} - Thrift - - - - - - rmdir /s /q "$(ProjectDir)gen-csharp" -del /f /q "$(ProjectDir)ThriftImpl.dll" -SET OUTPUT_DIR=$(ProjectDir) - -SET THRIFT_FILE=$(ProjectDir)\..\..\..\..\..\contrib\async-test\aggr.thrift -for %25%25I in ("%25OUTPUT_DIR%25") do set SHORT_DIR=%25%25~fsI -for %25%25I in ("%25THRIFT_FILE%25") do set THRIFT_SHORT=%25%25~fsI -"$(ProjectDir)\..\..\..\..\..\compiler\cpp\thrift.exe" --gen csharp -o %25SHORT_DIR%25 %25THRIFT_SHORT%25 - -SET THRIFT_FILE=$(ProjectDir)\..\..\..\..\..\lib\rb\benchmark\Benchmark.thrift -for %25%25I in ("%25OUTPUT_DIR%25") do set SHORT_DIR=%25%25~fsI -for %25%25I in ("%25THRIFT_FILE%25") do set THRIFT_SHORT=%25%25~fsI -"$(ProjectDir)\..\..\..\..\..\compiler\cpp\thrift.exe" --gen csharp -o %25SHORT_DIR%25 %25THRIFT_SHORT%25 - - - - diff --git a/lib/csharp/test/Multiplex/Client/Properties/AssemblyInfo.cs b/lib/csharp/test/Multiplex/Client/Properties/AssemblyInfo.cs deleted file mode 100644 index 5ee34a1af1f..00000000000 --- a/lib/csharp/test/Multiplex/Client/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -using System.Reflection; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; - -// General Information about an assembly is controlled through the following -// set of attributes. Change these attribute values to modify the information -// associated with an assembly. -[assembly: AssemblyTitle("MultiplexClient")] -[assembly: AssemblyDescription("")] -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("The Apache Software Foundation")] -[assembly: AssemblyProduct("Thrift")] -[assembly: AssemblyCopyright("The Apache Software Foundation")] -[assembly: AssemblyTrademark("")] -[assembly: AssemblyCulture("")] - -// Setting ComVisible to false makes the types in this assembly not visible -// to COM components. If you need to access a type in this assembly from -// COM, set the ComVisible attribute to true on that type. -[assembly: ComVisible(false)] - -// The following GUID is for the ID of the typelib if this project is exposed to COM -[assembly: Guid("66FC61E5-420B-4b56-8012-D6D6CE22537F")] - -// Version information for an assembly consists of the following four values: -// -// Major Version -// Minor Version -// Build Number -// Revision -// -// You can specify all the values or you can default the Build and Revision Numbers -// by using the '*' as shown below: -// [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("0.12.0.0")] -[assembly: AssemblyFileVersion("0.12.0.0")] diff --git a/lib/csharp/test/Multiplex/Makefile.am b/lib/csharp/test/Multiplex/Makefile.am deleted file mode 100644 index 57253d6b57b..00000000000 --- a/lib/csharp/test/Multiplex/Makefile.am +++ /dev/null @@ -1,52 +0,0 @@ -# -# Licensed to the Apache Software Foundation (ASF) under one -# or more contributor license agreements. See the NOTICE file -# distributed with this work for additional information -# regarding copyright ownership. The ASF licenses this file -# to you under the Apache License, Version 2.0 (the -# "License"); you may not use this file except in compliance -# with the License. You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, -# software distributed under the License is distributed on an -# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -# KIND, either express or implied. See the License for the -# specific language governing permissions and limitations -# under the License. -# - -GENERATED = \ - gen-csharp/Aggr.cs \ - gen-csharp/BenchmarkService.cs \ - gen-csharp/Error.cs - -BUILT_SOURCES = $(GENERATED) - -THRIFT = $(top_builddir)/compiler/cpp/thrift - -gen-csharp/Aggr.cs: $(top_srcdir)/contrib/async-test/aggr.thrift - $(THRIFT) --gen csharp $< - -gen-csharp/BenchmarkService.cs gen-csharp/Error.cs: $(top_srcdir)/lib/rb/benchmark/Benchmark.thrift - $(THRIFT) --gen csharp $< - -ThriftImpl.dll: Multiplex.Test.Common.cs $(GENERATED) ../../Thrift.dll - $(CSC) $(CSC_DEFINES) -t:library -out:./ThriftImpl.dll -reference:../../Thrift.dll $(GENERATED) $< - -MultiplexClient.exe: Client/Multiplex.Test.Client.cs ThriftImpl.dll - $(CSC) $(CSC_DEFINES) -out:$@ -reference:../../Thrift.dll -reference:ThriftImpl.dll $< - -MultiplexServer.exe: Server/Multiplex.Test.Server.cs ThriftImpl.dll - $(CSC) $(CSC_DEFINES) -out:$@ -reference:../../Thrift.dll -reference:ThriftImpl.dll $< - -clean-local: - $(RM) -rf gen-csharp *.exe *.dll - -TESTPORT = 9501 -check-local: MultiplexServer.exe MultiplexClient.exe - echo $(TESTPORT) - MONO_PATH=../../ timeout 10 mono MultiplexServer.exe $(TESTPORT) & - sleep 1 - MONO_PATH=../../ mono MultiplexClient.exe $(TESTPORT) diff --git a/lib/csharp/test/Multiplex/Server/Multiplex.Test.Server.cs b/lib/csharp/test/Multiplex/Server/Multiplex.Test.Server.cs deleted file mode 100644 index 9786189bb31..00000000000 --- a/lib/csharp/test/Multiplex/Server/Multiplex.Test.Server.cs +++ /dev/null @@ -1,107 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -using System; -using System.Collections.Generic; -using Thrift.Collections; -using Thrift.Transport; -using Thrift.Protocol; -using Thrift.Server; -using Thrift; -using Test.Multiplex; - -namespace Test.Multiplex.Server -{ - public class TestServer - { - class BenchmarkServiceImpl : BenchmarkService.Iface - { - public int fibonacci(sbyte n) - { - int prev, next, result; - prev = 0; - result = 1; - while (n > 0) - { - next = result + prev; - prev = result; - result = next; - --n; - } - return result; - } - } - - class AggrServiceImpl : Aggr.Iface - { - List values = new List(); - - public void addValue(int value) - { - values.Add(value); - } - - public List getValues() - { - return values; - } - } - - static void Execute(int port) - { - try - { - // create protocol factory, default to BinaryProtocol - TProtocolFactory ProtocolFactory = new TBinaryProtocol.Factory(true,true); - TServerTransport servertrans = new TServerSocket(port, 0, false); - TTransportFactory TransportFactory = new TFramedTransport.Factory(); - - BenchmarkService.Iface benchHandler = new BenchmarkServiceImpl(); - TProcessor benchProcessor = new BenchmarkService.Processor(benchHandler); - - Aggr.Iface aggrHandler = new AggrServiceImpl(); - TProcessor aggrProcessor = new Aggr.Processor(aggrHandler); - - TMultiplexedProcessor multiplex = new TMultiplexedProcessor(); - multiplex.RegisterProcessor(Constants.NAME_BENCHMARKSERVICE, benchProcessor); - multiplex.RegisterProcessor(Constants.NAME_AGGR, aggrProcessor); - - TServer ServerEngine = new TSimpleServer(multiplex, servertrans, TransportFactory, ProtocolFactory); - - Console.WriteLine("Starting the server ..."); - ServerEngine.Serve(); - } - catch (Exception e) - { - Console.WriteLine(e.Message); - } - } - - static void Main(string[] args) - { - int port = 9090; - if (args.Length > 0) - { - port = ushort.Parse(args[0]); - } - Execute(port); - } - } -} - diff --git a/lib/csharp/test/Multiplex/Server/MultiplexServer.csproj b/lib/csharp/test/Multiplex/Server/MultiplexServer.csproj deleted file mode 100644 index 57ef76d4ebf..00000000000 --- a/lib/csharp/test/Multiplex/Server/MultiplexServer.csproj +++ /dev/null @@ -1,148 +0,0 @@ - - - - - Debug - AnyCPU - 9.0.21022 - 2.0 - {D592BDF3-0DCE-48FB-890F-E4AE1D9CE7CD} - Exe - Properties - MultiplexServer - MultiplexServer - v3.5 - 512 - false - - - 3.5 - - publish\ - true - Disk - false - Foreground - 7 - Days - false - false - true - 0 - 0.12.0.%2a - false - true - - - true - full - false - ..\bin\Debug\ - DEBUG;TRACE - prompt - 4 - AllRules.ruleset - - - pdbonly - true - ..\bin\Release\ - TRACE - prompt - 4 - AllRules.ruleset - - - - - - - Multiplex.Test.Common.cs - - - - - - - - - - False - .NET Framework 3.5 SP1 Client Profile - false - - - False - .NET Framework 2.0 %28x86%29 - false - - - False - .NET Framework 3.0 %28x86%29 - false - - - False - .NET Framework 3.5 - true - - - False - .NET Framework 3.5 SP1 - false - - - False - Windows Installer 3.1 - true - - - - - {499EB63C-D74C-47E8-AE48-A2FC94538E9D} - Thrift - - - - - - rmdir /s /q "$(ProjectDir)gen-csharp" -del /f /q "$(ProjectDir)ThriftImpl.dll" -SET OUTPUT_DIR=$(ProjectDir) - -SET THRIFT_FILE=$(ProjectDir)\..\..\..\..\..\contrib\async-test\aggr.thrift -for %25%25I in ("%25OUTPUT_DIR%25") do set SHORT_DIR=%25%25~fsI -for %25%25I in ("%25THRIFT_FILE%25") do set THRIFT_SHORT=%25%25~fsI -"$(ProjectDir)\..\..\..\..\..\compiler\cpp\thrift.exe" --gen csharp -o %25SHORT_DIR%25 %25THRIFT_SHORT%25 - -SET THRIFT_FILE=$(ProjectDir)\..\..\..\..\..\lib\rb\benchmark\Benchmark.thrift -for %25%25I in ("%25OUTPUT_DIR%25") do set SHORT_DIR=%25%25~fsI -for %25%25I in ("%25THRIFT_FILE%25") do set THRIFT_SHORT=%25%25~fsI -"$(ProjectDir)\..\..\..\..\..\compiler\cpp\thrift.exe" --gen csharp -o %25SHORT_DIR%25 %25THRIFT_SHORT%25 - - - - diff --git a/lib/csharp/test/Multiplex/Server/Properties/AssemblyInfo.cs b/lib/csharp/test/Multiplex/Server/Properties/AssemblyInfo.cs deleted file mode 100644 index 65fb4ce4b53..00000000000 --- a/lib/csharp/test/Multiplex/Server/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -using System.Reflection; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; - -// General Information about an assembly is controlled through the following -// set of attributes. Change these attribute values to modify the information -// associated with an assembly. -[assembly: AssemblyTitle("MultiplexServer")] -[assembly: AssemblyDescription("")] -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("The Apache Software Foundation")] -[assembly: AssemblyProduct("Thrift")] -[assembly: AssemblyCopyright("The Apache Software Foundation")] -[assembly: AssemblyTrademark("")] -[assembly: AssemblyCulture("")] - -// Setting ComVisible to false makes the types in this assembly not visible -// to COM components. If you need to access a type in this assembly from -// COM, set the ComVisible attribute to true on that type. -[assembly: ComVisible(false)] - -// The following GUID is for the ID of the typelib if this project is exposed to COM -[assembly: Guid("F2F436C1-3D4F-411a-ADC3-B98848476A8E")] - -// Version information for an assembly consists of the following four values: -// -// Major Version -// Minor Version -// Build Number -// Revision -// -// You can specify all the values or you can default the Build and Revision Numbers -// by using the '*' as shown below: -// [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("0.12.0.0")] -[assembly: AssemblyFileVersion("0.12.0.0")] diff --git a/lib/csharp/test/ThriftMVCTest/App_Start/FilterConfig.cs b/lib/csharp/test/ThriftMVCTest/App_Start/FilterConfig.cs deleted file mode 100644 index 855184fd23f..00000000000 --- a/lib/csharp/test/ThriftMVCTest/App_Start/FilterConfig.cs +++ /dev/null @@ -1,31 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -using System.Web.Mvc; - -namespace ThriftMVCTest -{ - public static class FilterConfig - { - public static void RegisterGlobalFilters(GlobalFilterCollection filters) - { - filters.Add(new HandleErrorAttribute()); - } - } -} diff --git a/lib/csharp/test/ThriftMVCTest/App_Start/RouteConfig.cs b/lib/csharp/test/ThriftMVCTest/App_Start/RouteConfig.cs deleted file mode 100644 index b4b6023d69a..00000000000 --- a/lib/csharp/test/ThriftMVCTest/App_Start/RouteConfig.cs +++ /dev/null @@ -1,39 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -using System.Web.Mvc; -using System.Web.Routing; - -namespace ThriftMVCTest -{ - public static class RouteConfig - { - public static void RegisterRoutes(RouteCollection routes) - { - routes.IgnoreRoute("{resource}.thrift"); - routes.IgnoreRoute("{resource}.axd/{*pathInfo}"); - - routes.MapRoute( - name: "Default", - url: "{controller}/{action}/{id}", - defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional } - ); - } - } -} diff --git a/lib/csharp/test/ThriftMVCTest/AsyncHttpHandler.cs b/lib/csharp/test/ThriftMVCTest/AsyncHttpHandler.cs deleted file mode 100644 index 7f26184fac0..00000000000 --- a/lib/csharp/test/ThriftMVCTest/AsyncHttpHandler.cs +++ /dev/null @@ -1,32 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -using Thrift.Transport; - -namespace ThriftMVCTest -{ - public class AsyncHttpHandler : THttpTaskAsyncHandler - { - public AsyncHttpHandler() - : base( - new Thrift.Test.SecondService.AsyncProcessor(new SecondServiceImpl())) - { - } - } -} \ No newline at end of file diff --git a/lib/csharp/test/ThriftMVCTest/Controllers/HomeController.cs b/lib/csharp/test/ThriftMVCTest/Controllers/HomeController.cs deleted file mode 100644 index c9a1ec43fdd..00000000000 --- a/lib/csharp/test/ThriftMVCTest/Controllers/HomeController.cs +++ /dev/null @@ -1,70 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -using System; -using System.Threading.Tasks; -using System.Web.Mvc; -using Thrift.Protocol; -using Thrift.Test; -using Thrift.Transport; - -namespace ThriftMVCTest.Controllers -{ - public class HomeController : Controller - { - public ActionResult Index() - { - return View(); - } - - public async Task TestThriftAsync() - { - var baseUri = new Uri(string.Format("{0}://{1}{2}", Request.Url.Scheme, Request.Url.Authority, - Url.Content("~"))); - - SecondService.IAsync asyncService = - new SecondService.Client(new TBinaryProtocol(new THttpClient(new Uri(baseUri, "Async.thrift")))); - - var result = await asyncService.secondtestStringAsync("TestString"); - if (result != "testString(\"TestString\")") - { - throw new Exception("The wrong result was returned"); - } - - return RedirectToAction("Index"); - } - - public ActionResult TestThriftSync() - { - var baseUri = new Uri(string.Format("{0}://{1}{2}", Request.Url.Scheme, Request.Url.Authority, - Url.Content("~"))); - - SecondService.ISync service = - new SecondService.Client(new TBinaryProtocol(new THttpClient(new Uri(baseUri, "Sync.thrift")))); - - var result = service.secondtestString("TestString"); - if (result != "testString(\"TestString\")") - { - throw new Exception("The wrong result was returned"); - } - - return RedirectToAction("Index"); - } - } -} diff --git a/lib/csharp/test/ThriftMVCTest/Global.asax b/lib/csharp/test/ThriftMVCTest/Global.asax deleted file mode 100644 index 7bb688c7ae7..00000000000 --- a/lib/csharp/test/ThriftMVCTest/Global.asax +++ /dev/null @@ -1,19 +0,0 @@ -<%@ Application Codebehind="Global.asax.cs" Inherits="ThriftMVCTest.MvcApplication" Language="C#" %> - diff --git a/lib/csharp/test/ThriftMVCTest/Global.asax.cs b/lib/csharp/test/ThriftMVCTest/Global.asax.cs deleted file mode 100644 index 59731efb3b5..00000000000 --- a/lib/csharp/test/ThriftMVCTest/Global.asax.cs +++ /dev/null @@ -1,34 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -using System.Web.Mvc; -using System.Web.Routing; - -namespace ThriftMVCTest -{ - public class MvcApplication : System.Web.HttpApplication - { - protected void Application_Start() - { - AreaRegistration.RegisterAllAreas(); - FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters); - RouteConfig.RegisterRoutes(RouteTable.Routes); - } - } -} diff --git a/lib/csharp/test/ThriftMVCTest/Properties/AssemblyInfo.cs b/lib/csharp/test/ThriftMVCTest/Properties/AssemblyInfo.cs deleted file mode 100644 index 186257d6984..00000000000 --- a/lib/csharp/test/ThriftMVCTest/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,53 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -using System.Reflection; -using System.Runtime.InteropServices; - -// General Information about an assembly is controlled through the following -// set of attributes. Change these attribute values to modify the information -// associated with an assembly. -[assembly: AssemblyTitle("ThriftMVCTest")] -[assembly: AssemblyDescription("A web project for testing the thrift ASP.NET features.")] -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("The Apache Software Foundation")] -[assembly: AssemblyProduct("Thrift")] -[assembly: AssemblyCopyright("The Apache Software Foundation")] -[assembly: AssemblyTrademark("")] -[assembly: AssemblyCulture("")] - -// Setting ComVisible to false makes the types in this assembly not visible -// to COM components. If you need to access a type in this assembly from -// COM, set the ComVisible attribute to true on that type. -[assembly: ComVisible(false)] - -// The following GUID is for the ID of the typelib if this project is exposed to COM -[assembly: Guid("366f9bd0-3c0e-48aa-b2ca-61fd4a93e427")] - -// Version information for an assembly consists of the following four values: -// -// Major Version -// Minor Version -// Build Number -// Revision -// -// You can specify all the values or you can default the Build and Revision Numbers -// by using the '*' as shown below: -[assembly: AssemblyVersion("0.12.0.0")] -[assembly: AssemblyFileVersion("0.12.0.0")] diff --git a/lib/csharp/test/ThriftMVCTest/SecondServiceImpl.cs b/lib/csharp/test/ThriftMVCTest/SecondServiceImpl.cs deleted file mode 100644 index fad301a351a..00000000000 --- a/lib/csharp/test/ThriftMVCTest/SecondServiceImpl.cs +++ /dev/null @@ -1,37 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -using System.Threading.Tasks; -using Thrift.Test; - -namespace ThriftMVCTest -{ - public class SecondServiceImpl : SecondService.IAsync, SecondService.ISync - { - public Task secondtestStringAsync(string thing) - { - return Task.FromResult(thing); - } - - public string secondtestString(string thing) - { - return "testString(\"" + thing + "\")"; - } - } -} diff --git a/lib/csharp/test/ThriftMVCTest/SyncHttpHandler.cs b/lib/csharp/test/ThriftMVCTest/SyncHttpHandler.cs deleted file mode 100644 index 4fe26624a2d..00000000000 --- a/lib/csharp/test/ThriftMVCTest/SyncHttpHandler.cs +++ /dev/null @@ -1,32 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -using Thrift.Transport; - -namespace ThriftMVCTest -{ - public class SyncHttpHandler : THttpHandler - { - public SyncHttpHandler() - : base( - new Thrift.Test.SecondService.Processor(new SecondServiceImpl())) - { - } - } -} \ No newline at end of file diff --git a/lib/csharp/test/ThriftMVCTest/ThriftMVCTest.csproj b/lib/csharp/test/ThriftMVCTest/ThriftMVCTest.csproj deleted file mode 100644 index 0eb969a04c2..00000000000 --- a/lib/csharp/test/ThriftMVCTest/ThriftMVCTest.csproj +++ /dev/null @@ -1,200 +0,0 @@ - - - - - - Debug - AnyCPU - - - 2.0 - {891B4487-C7BA-427E-BBC8-4C596C229A10} - {349c5851-65df-11da-9384-00065b846f21};{fae04ec0-301f-11d3-bf4b-00c04f79efbc} - Library - Properties - ThriftMVCTest - ThriftMVCTest - v4.5 - false - true - - - - - - - true - full - false - bin\ - DEBUG;TRACE - prompt - 4 - - - pdbonly - true - bin\ - TRACE - prompt - 4 - - - - - - - - - - - - - - - - - - - - - - - True - ..\packages\Microsoft.Web.Infrastructure.1.0.0.0\lib\net40\Microsoft.Web.Infrastructure.dll - - - - - - - True - ..\packages\Microsoft.AspNet.WebPages.3.2.3\lib\net45\System.Web.Helpers.dll - - - True - ..\packages\Microsoft.AspNet.Mvc.5.2.3\lib\net45\System.Web.Mvc.dll - - - True - ..\packages\Microsoft.AspNet.Razor.3.2.3\lib\net45\System.Web.Razor.dll - - - True - ..\packages\Microsoft.AspNet.WebPages.3.2.3\lib\net45\System.Web.WebPages.dll - - - True - ..\packages\Microsoft.AspNet.WebPages.3.2.3\lib\net45\System.Web.WebPages.Deployment.dll - - - True - ..\packages\Microsoft.AspNet.WebPages.3.2.3\lib\net45\System.Web.WebPages.Razor.dll - - - .\ThriftImpl.dll - - - - - - - - - - Global.asax - - - - - - - - - - Web.config - - - Web.config - - - - - - - - - - - - Designer - - - - - {ebce35da-cf6a-42bc-a357-a9c09b534299} - Thrift.45 - - - - 10.0 - $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) - - - - - - - - - - - - True - True - 57482 - / - http://localhost:57482/ - False - False - - - False - - - - - - rmdir /s /q "$(ProjectDir)gen-csharp" -del /f /q "$(ProjectDir)ThriftImpl.dll" -SET OUTPUT_DIR=$(ProjectDir) -SET THRIFT_FILE=$(ProjectDir)\..\..\..\..\test\ThriftTest.thrift -for %25%25I in ("%25OUTPUT_DIR%25") do set SHORT_DIR=%25%25~fsI -for %25%25I in ("%25THRIFT_FILE%25") do set THRIFT_SHORT=%25%25~fsI -"$(ProjectDir)\..\..\..\..\compiler\cpp\Debug\thrift.exe" --gen csharp:async=true -o %25SHORT_DIR%25 %25THRIFT_SHORT%25 -"$(MSBuildToolsPath)\Csc.exe" /t:library /out:"$(ProjectDir)ThriftImpl.dll" /recurse:"$(ProjectDir)gen-csharp"\* /reference:"$(ProjectDir)..\..\src\bin\Debug\Thrift45.dll" - - - \ No newline at end of file diff --git a/lib/csharp/test/ThriftMVCTest/Views/Home/Index.cshtml b/lib/csharp/test/ThriftMVCTest/Views/Home/Index.cshtml deleted file mode 100644 index f0ca7da209e..00000000000 --- a/lib/csharp/test/ThriftMVCTest/Views/Home/Index.cshtml +++ /dev/null @@ -1,25 +0,0 @@ -@* - Licensed to the Apache Software Foundation (ASF) under one - or more contributor license agreements. See the NOTICE file - distributed with this work for additional information - regarding copyright ownership. The ASF licenses this file - to you under the Apache License, Version 2.0 (the - "License"); you may not use this file except in compliance - with the License. You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, - software distributed under the License is distributed on an - "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - KIND, either express or implied. See the License for the - specific language governing permissions and limitations - under the License. -*@ - -@{ - ViewBag.Title = "Home Page"; -} - -

@Html.ActionLink("Test Thrift Async Service", "TestThriftAsync")

-

@Html.ActionLink("Test Thrift Sync Service", "TestThriftSync")

diff --git a/lib/csharp/test/ThriftMVCTest/Views/Shared/_Layout.cshtml b/lib/csharp/test/ThriftMVCTest/Views/Shared/_Layout.cshtml deleted file mode 100644 index b41c99a100f..00000000000 --- a/lib/csharp/test/ThriftMVCTest/Views/Shared/_Layout.cshtml +++ /dev/null @@ -1,30 +0,0 @@ -@* - Licensed to the Apache Software Foundation (ASF) under one - or more contributor license agreements. See the NOTICE file - distributed with this work for additional information - regarding copyright ownership. The ASF licenses this file - to you under the Apache License, Version 2.0 (the - "License"); you may not use this file except in compliance - with the License. You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, - software distributed under the License is distributed on an - "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - KIND, either express or implied. See the License for the - specific language governing permissions and limitations - under the License. -*@ - - - - - - - Thrift ASP.NET Test - - - @RenderBody() - - diff --git a/lib/csharp/test/ThriftMVCTest/Views/Web.config b/lib/csharp/test/ThriftMVCTest/Views/Web.config deleted file mode 100644 index 3c211387a95..00000000000 --- a/lib/csharp/test/ThriftMVCTest/Views/Web.config +++ /dev/null @@ -1,60 +0,0 @@ - - - - - -
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/lib/csharp/test/ThriftMVCTest/Views/_ViewStart.cshtml b/lib/csharp/test/ThriftMVCTest/Views/_ViewStart.cshtml deleted file mode 100644 index 8cde2eeb95a..00000000000 --- a/lib/csharp/test/ThriftMVCTest/Views/_ViewStart.cshtml +++ /dev/null @@ -1,22 +0,0 @@ -@* - Licensed to the Apache Software Foundation (ASF) under one - or more contributor license agreements. See the NOTICE file - distributed with this work for additional information - regarding copyright ownership. The ASF licenses this file - to you under the Apache License, Version 2.0 (the - "License"); you may not use this file except in compliance - with the License. You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, - software distributed under the License is distributed on an - "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - KIND, either express or implied. See the License for the - specific language governing permissions and limitations - under the License. -*@ - -@{ - Layout = "~/Views/Shared/_Layout.cshtml"; -} diff --git a/lib/csharp/test/ThriftMVCTest/Web.Debug.config b/lib/csharp/test/ThriftMVCTest/Web.Debug.config deleted file mode 100644 index 45d56d80993..00000000000 --- a/lib/csharp/test/ThriftMVCTest/Web.Debug.config +++ /dev/null @@ -1,45 +0,0 @@ - - - - - - - - diff --git a/lib/csharp/test/ThriftMVCTest/Web.Release.config b/lib/csharp/test/ThriftMVCTest/Web.Release.config deleted file mode 100644 index 157c340ca86..00000000000 --- a/lib/csharp/test/ThriftMVCTest/Web.Release.config +++ /dev/null @@ -1,46 +0,0 @@ - - - - - - - - - diff --git a/lib/csharp/test/ThriftMVCTest/Web.config b/lib/csharp/test/ThriftMVCTest/Web.config deleted file mode 100644 index 9c57d117cd7..00000000000 --- a/lib/csharp/test/ThriftMVCTest/Web.config +++ /dev/null @@ -1,92 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/lib/csharp/test/ThriftMVCTest/favicon.ico b/lib/csharp/test/ThriftMVCTest/favicon.ico deleted file mode 100644 index a3a799985c4..00000000000 Binary files a/lib/csharp/test/ThriftMVCTest/favicon.ico and /dev/null differ diff --git a/lib/csharp/test/ThriftMVCTest/packages.config b/lib/csharp/test/ThriftMVCTest/packages.config deleted file mode 100644 index 98c8416f513..00000000000 --- a/lib/csharp/test/ThriftMVCTest/packages.config +++ /dev/null @@ -1,25 +0,0 @@ - - - - - - - - \ No newline at end of file diff --git a/lib/d/Makefile.am b/lib/d/Makefile.am index 2a812185677..4787e0a6077 100644 --- a/lib/d/Makefile.am +++ b/lib/d/Makefile.am @@ -141,9 +141,13 @@ all-local: $(all_targets) install-exec-local: $(INSTALL_PROGRAM) $(all_targets) $(DESTDIR)$(libdir) - clean-local: - $(RM) -rf docs $(D_LIB_NAME) $(D_EVENT_LIB_NAME) $(D_SSL_LIB_NAME) unittest + $(RM) -r docs + $(RM) $(D_LIB_NAME) + $(RM) $(D_EVENT_LIB_NAME) + $(RM) $(D_SSL_LIB_NAME) + $(RM) -r test/gen-d + $(RM) -r unittest # diff --git a/lib/d/README.md b/lib/d/README.md index 5d37e4fe7b8..9b188abf512 100644 --- a/lib/d/README.md +++ b/lib/d/README.md @@ -42,17 +42,8 @@ directory (PowerShell syntax): dmd -ofunittest -unittest -w $(dir -r -filter '*.d' -name) -If you want to run the test clients/servers in OpenSSL -mode, you have to provide »server-private-key.pem« and -»server-certificate.pem« files in the directory the server -executable resides in, and a »trusted-ca-certificate.pem« -file for the client. The easiest way is to generate a new -self signed certificate using the provided config file -(openssl.test.cnf): - -openssl req -new -x509 -nodes -config openssl.test.cnf \ - -out server-certificate.pem -cat server-certificate.pem > trusted-ca-certificate.pem - -This steps are also performed automatically by the -Autotools build system if the files are not present. +Async and SSL +------------- +Using SSL with async is experimental (always has been) and +the unit test "async_test --ssl" hangs. Use at your own +risk. diff --git a/lib/d/src/thrift/async/libevent.d b/lib/d/src/thrift/async/libevent.d index 812e4a7650b..04e0e71ba93 100644 --- a/lib/d/src/thrift/async/libevent.d +++ b/lib/d/src/thrift/async/libevent.d @@ -62,7 +62,7 @@ class TLibeventAsyncManager : TAsyncSocketManager { controlReceiveSocket_.blocking = false; // Register an event for receiving control messages. - controlReceiveEvent_ = event_new(eventBase_, controlReceiveSocket_.handle, + controlReceiveEvent_ = event_new(eventBase_, cast(evutil_socket_t)controlReceiveSocket_.handle, EV_READ | EV_PERSIST | EV_ET, assumeNothrow(&controlMsgReceiveCallback), cast(void*)this); event_add(controlReceiveEvent_, null); @@ -188,7 +188,7 @@ private: void addOneshotListenerImpl(Socket socket, TAsyncEventType eventType, const(timeval)* timeout, TSocketEventListener listener ) { - registerOneshotEvent(socket.handle, libeventEventType(eventType), + registerOneshotEvent(cast(evutil_socket_t)socket.handle, libeventEventType(eventType), assumeNothrow(&socketCallback), timeout, listener); } diff --git a/lib/d/src/thrift/async/socket.d b/lib/d/src/thrift/async/socket.d index a08f51db083..352a8224e6d 100644 --- a/lib/d/src/thrift/async/socket.d +++ b/lib/d/src/thrift/async/socket.d @@ -18,7 +18,6 @@ */ module thrift.async.socket; -import core.stdc.errno: ECONNRESET; import core.thread : Fiber; import core.time : dur, Duration; import std.array : empty; @@ -33,11 +32,15 @@ import thrift.internal.endian; import thrift.internal.socket; version (Windows) { - import std.c.windows.winsock : connect; + import core.sys.windows.winsock2 : connect; } else version (Posix) { import core.sys.posix.sys.socket : connect; } else static assert(0, "Don't know connect on this platform."); +version (Win32) { + import core.stdc.config : __c_long; +} + /** * Non-blocking socket implementation of the TTransport interface. * @@ -148,7 +151,11 @@ class TAsyncSocket : TSocketBase, TAsyncTransport { return; } - int errorCode = void; + version (Win32) { + __c_long errorCode = void; + } else { + int errorCode = void; + } socket_.getOption(SocketOptionLevel.SOCKET, cast(SocketOption)SO_ERROR, errorCode); @@ -344,8 +351,8 @@ private { enum SO_ERROR = 0x1007; } else version (FreeBSD) { enum SO_ERROR = 0x1007; - } else version (Win32) { - import std.c.windows.winsock : SO_ERROR; + } else version (Windows) { + import core.sys.windows.winsock2 : SO_ERROR; } else static assert(false, "Don't know SO_ERROR on this platform."); // This hack forces a delegate literal to be scoped, even if it is passed to diff --git a/lib/d/src/thrift/base.d b/lib/d/src/thrift/base.d index 260e155b690..4a8fbd7599e 100644 --- a/lib/d/src/thrift/base.d +++ b/lib/d/src/thrift/base.d @@ -50,7 +50,7 @@ class TCompoundOperationException : TException { /// The Thrift version string, used for informative purposes. // Note: This is currently hardcoded, but will likely be filled in by the build // system in future versions. -enum VERSION = "0.12.0"; +enum VERSION = "0.14.0"; /** * Functions used for logging inside Thrift. diff --git a/lib/d/src/thrift/codegen/async_client_pool.d b/lib/d/src/thrift/codegen/async_client_pool.d index 26cb975a32c..b51cd16a92f 100644 --- a/lib/d/src/thrift/codegen/async_client_pool.d +++ b/lib/d/src/thrift/codegen/async_client_pool.d @@ -95,7 +95,7 @@ interface TAsyncClientPoolBase(Interface) if (isService!Interface) : } immutable bool delegate(Exception) defaultRpcFaultFilter; -static this() { +shared static this() { defaultRpcFaultFilter = (Exception e) { import thrift.protocol.base; import thrift.transport.base; diff --git a/lib/d/src/thrift/internal/socket.d b/lib/d/src/thrift/internal/socket.d index 6ca0a970e41..757fafab3c0 100644 --- a/lib/d/src/thrift/internal/socket.d +++ b/lib/d/src/thrift/internal/socket.d @@ -34,8 +34,8 @@ version (FreeBSD) { enum connresetOnPeerShutdown = false; } -version (Win32) { - import std.c.windows.winsock : WSAGetLastError, WSAEINTR, WSAEWOULDBLOCK; +version (Windows) { + import core.sys.windows.winsock2 : WSAGetLastError, WSAEINTR, WSAEWOULDBLOCK; import std.windows.syserror : sysErrorString; // These are unfortunately not defined in std.c.windows.winsock, see @@ -60,7 +60,7 @@ version (Win32) { * isSocetCloseErrno(errno): returns true if errno indicates that the socket * is logically in closed state now. */ -version (Win32) { +version (Windows) { alias WSAGetLastError getSocketErrno; enum CONNECT_INPROGRESS_ERRNO = WSAEWOULDBLOCK; enum INTERRUPTED_ERRNO = WSAEINTR; @@ -88,7 +88,7 @@ version (Win32) { } string socketErrnoString(uint errno) { - version (Win32) { + version (Windows) { return sysErrorString(errno); } else { return to!string(strerror(errno)); diff --git a/lib/d/src/thrift/protocol/base.d b/lib/d/src/thrift/protocol/base.d index 70648b3c39f..5b6d84514cb 100644 --- a/lib/d/src/thrift/protocol/base.d +++ b/lib/d/src/thrift/protocol/base.d @@ -260,7 +260,7 @@ protected: * in generated code. */ void skip(Protocol)(Protocol prot, TType type) if (is(Protocol : TProtocol)) { - final switch (type) { + switch (type) { case TType.BOOL: prot.readBool(); break; @@ -324,9 +324,9 @@ void skip(Protocol)(Protocol prot, TType type) if (is(Protocol : TProtocol)) { } prot.readSetEnd(); break; - case TType.STOP: goto case; - case TType.VOID: - assert(false, "Invalid field type passed."); + + default: + throw new TProtocolException(TProtocolException.Type.INVALID_DATA); } } diff --git a/lib/d/src/thrift/server/nonblocking.d b/lib/d/src/thrift/server/nonblocking.d index 5860c0c4274..f0afc1f9207 100644 --- a/lib/d/src/thrift/server/nonblocking.d +++ b/lib/d/src/thrift/server/nonblocking.d @@ -623,7 +623,7 @@ private { to!string(event_get_version()), to!string(event_base_get_method(eventBase_))); // Register the event for the listening socket. - listenEvent_ = event_new(eventBase_, listenSocket_.handle, + listenEvent_ = event_new(eventBase_, cast(evutil_socket_t)listenSocket_.handle, EV_READ | EV_PERSIST | EV_ET, assumeNothrow(&TNonblockingServer.acceptConnectionsCallback), cast(void*)server_); @@ -638,7 +638,7 @@ private { completionReceiveSocket_ = pair[1]; // Register an event for the task completion notification socket. - completionEvent_ = event_new(eventBase_, completionReceiveSocket_.handle, + completionEvent_ = event_new(eventBase_, cast(evutil_socket_t)completionReceiveSocket_.handle, EV_READ | EV_PERSIST | EV_ET, assumeNothrow(&completedCallback), cast(void*)this); @@ -1212,10 +1212,10 @@ private { if (!event_) { // If the event was not already allocated, do it now. - event_ = event_new(loop_.eventBase_, socket_.socketHandle, + event_ = event_new(loop_.eventBase_, cast(evutil_socket_t)socket_.socketHandle, eventFlags_, assumeNothrow(&workSocketCallback), cast(void*)this); } else { - event_assign(event_, loop_.eventBase_, socket_.socketHandle, + event_assign(event_, loop_.eventBase_, cast(evutil_socket_t)socket_.socketHandle, eventFlags_, assumeNothrow(&workSocketCallback), cast(void*)this); } diff --git a/lib/d/src/thrift/transport/socket.d b/lib/d/src/thrift/transport/socket.d index fcb38da36d0..b257fe955d2 100644 --- a/lib/d/src/thrift/transport/socket.d +++ b/lib/d/src/thrift/transport/socket.d @@ -18,7 +18,6 @@ */ module thrift.transport.socket; -import core.stdc.errno: ECONNRESET; import core.thread : Thread; import core.time : dur, Duration; import std.array : empty; @@ -29,6 +28,13 @@ import thrift.base; import thrift.transport.base; import thrift.internal.socket; +version (Windows) { + import core.sys.windows.winsock2 : WSAECONNRESET; + enum ECONNRESET = WSAECONNRESET; +} else version (Posix) { + import core.stdc.errno : ECONNRESET; +} else static assert(0, "Don't know ECONNRESET on this platform."); + /** * Common parts of a socket TTransport implementation, regardless of how the * actual I/O is performed (sync/async). diff --git a/lib/d/src/thrift/transport/ssl.d b/lib/d/src/thrift/transport/ssl.d index f8ce40eb721..5bdacbf1f10 100644 --- a/lib/d/src/thrift/transport/ssl.d +++ b/lib/d/src/thrift/transport/ssl.d @@ -213,7 +213,7 @@ private: if (ssl_ !is null) return; ssl_ = context_.createSSL(); - SSL_set_fd(ssl_, socketHandle); + SSL_set_fd(ssl_, cast(int)socketHandle); int rc; if (serverSide_) { rc = SSL_accept(ssl_); diff --git a/lib/d/src/thrift/transport/websocket.d b/lib/d/src/thrift/transport/websocket.d new file mode 100644 index 00000000000..b800e519e31 --- /dev/null +++ b/lib/d/src/thrift/transport/websocket.d @@ -0,0 +1,388 @@ +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ +module thrift.transport.websocket; + +import std.algorithm; +import std.algorithm.searching; +import std.base64; +import std.bitmanip; +import std.conv; +import std.digest.sha; +import std.stdio; +import std.string; +import std.uni; +import thrift.base : VERSION; +import thrift.transport.base; +import thrift.transport.http; + +/** + * WebSocket server transport. + */ +final class TServerWebSocketTransport(bool binary) : THttpTransport { + /** + * Constructs a new instance. + * + * Param: + * transport = The underlying transport used for the actual I/O. + */ + this(TTransport transport) { + super(transport); + transport_ = transport; + } + + override size_t read(ubyte[] buf) { + // If we do not have a good handshake, the client will attempt one. + if (!handshakeComplete) { + resetHandshake(); + super.read(buf); + // If we did not get everything we expected, the handshake failed + // and we need to send a 400 response back. + if (!handshakeComplete) { + sendBadRequest(); + return 0; + } + // Otherwise, send back the 101 response. + super.flush(); + } + + // If the buffer is empty, read a new frame off the wire. + if (readBuffer_.empty) { + if (!readFrame()) { + return 0; + } + } + + auto size = min(readBuffer_.length, buf.length); + buf[0..size] = readBuffer_[0..size]; + readBuffer_ = readBuffer_[size..$]; + return size; + } + + override void write(in ubyte[] buf) { + writeBuffer_ ~= buf; + } + + override void flush() { + if (writeBuffer_.empty) { + return; + } + + // Properly reset the write buffer even some of the protocol operations go + // wrong. + scope (exit) { + writeBuffer_.length = 0; + writeBuffer_.assumeSafeAppend(); + } + + writeFrameHeader(); + transport_.write(writeBuffer_); + transport_.flush(); + } + +protected: + override string getHeader(size_t dataLength) { + return "HTTP/1.1 101 Switching Protocols\r\n" ~ + "Server: Thrift/" ~ VERSION ~ "\r\n" ~ + "Upgrade: websocket\r\n" ~ + "Connection: Upgrade\r\n" ~ + "Sec-WebSocket-Accept: " ~ acceptKey_ ~ "\r\n" ~ + "\r\n"; + } + + override void parseHeader(const(ubyte)[] header) { + auto split = findSplit(header, [':']); + if (split[1].empty) { + // No colon found. + return; + } + + static bool compToLower(ubyte a, ubyte b) { + return toLower(a) == toLower(b); + } + + if (startsWith!compToLower(split[0], cast(ubyte[])"upgrade")) { + auto upgrade = stripLeft(cast(const(char)[])split[2]); + upgrade_ = sicmp(upgrade, "websocket") == 0; + } else if (startsWith!compToLower(split[0], cast(ubyte[])"connection")) { + auto connection = stripLeft(cast(const(char)[])split[2]); + connection_ = canFind(connection.toLower, "upgrade"); + } else if (startsWith!compToLower(split[0], cast(ubyte[])"sec-websocket-key")) { + auto secWebSocketKey = stripLeft(cast(const(char)[])split[2]); + auto hash = sha1Of(secWebSocketKey ~ WEBSOCKET_GUID); + acceptKey_ = Base64.encode(hash); + secWebSocketKey_ = true; + } else if (startsWith!compToLower(split[0], cast(ubyte[])"sec-websocket-version")) { + auto secWebSocketVersion = stripLeft(cast(const(char)[])split[2]); + secWebSocketVersion_ = sicmp(secWebSocketVersion, "13") == 0; + } + } + + override bool parseStatusLine(const(ubyte)[] status) { + // Method SP Request-URI SP HTTP-Version CRLF. + auto split = findSplit(status, [' ']); + if (split[1].empty) { + throw new TTransportException("Bad status: " ~ to!string(status), + TTransportException.Type.CORRUPTED_DATA); + } + + auto uriVersion = split[2][countUntil!"a != b"(split[2], ' ') .. $]; + if (!canFind(uriVersion, ' ')) { + throw new TTransportException("Bad status: " ~ to!string(status), + TTransportException.Type.CORRUPTED_DATA); + } + + if (split[0] == "GET") { + // GET method ok, looking for content. + return true; + } + + throw new TTransportException("Bad status (unsupported method): " ~ + to!string(status), TTransportException.Type.CORRUPTED_DATA); + } + +private: + @property bool handshakeComplete() { + return upgrade_ && connection_ && secWebSocketKey_ && secWebSocketVersion_; + } + + void failConnection(CloseCode reason) { + writeFrameHeader(Opcode.Close); + transport_.write(nativeToBigEndian!ushort(reason)); + transport_.flush(); + transport_.close(); + } + + void pong() { + writeFrameHeader(Opcode.Pong); + transport_.write(readBuffer_); + transport_.flush(); + } + + bool readFrame() { + ubyte[8] headerBuffer; + + auto read = transport_.read(headerBuffer[0..2]); + if (read < 2) { + return false; + } + // Since Thrift has its own message end marker and we read frame by frame, + // it doesn't really matter if the frame is marked as FIN. + // Capture it only for debugging only. + debug auto fin = (headerBuffer[0] & 0x80) != 0; + + // RSV1, RSV2, RSV3 + if ((headerBuffer[0] & 0x70) != 0) { + failConnection(CloseCode.ProtocolError); + throw new TTransportException("Reserved bits must be zeroes", TTransportException.Type.CORRUPTED_DATA); + } + + Opcode opcode; + try { + opcode = to!Opcode(headerBuffer[0] & 0x0F); + } catch (ConvException) { + failConnection(CloseCode.ProtocolError); + throw new TTransportException("Unknown opcode", TTransportException.Type.CORRUPTED_DATA); + } + + // Mask + if ((headerBuffer[1] & 0x80) == 0) { + failConnection(CloseCode.ProtocolError); + throw new TTransportException("Messages from the client must be masked", TTransportException.Type.CORRUPTED_DATA); + } + + // Read the length + ulong payloadLength = headerBuffer[1] & 0x7F; + if (payloadLength == 126) { + read = transport_.read(headerBuffer[0..2]); + if (read < 2) { + return false; + } + payloadLength = bigEndianToNative!ushort(headerBuffer[0..2]); + } else if (payloadLength == 127) { + read = transport_.read(headerBuffer); + if (read < headerBuffer.length) { + return false; + } + payloadLength = bigEndianToNative!ulong(headerBuffer); + if ((payloadLength & 0x8000000000000000) != 0) { + failConnection(CloseCode.ProtocolError); + throw new TTransportException("The most significant bit of the payload length must be zero", + TTransportException.Type.CORRUPTED_DATA); + } + } + + // size_t is smaller than a ulong on a 32-bit system + static if (size_t.max < ulong.max) { + if(payloadLength > size_t.max) { + failConnection(CloseCode.MessageTooBig); + return false; + } + } + + auto length = cast(size_t)payloadLength; + + if (length > 0) { + // Read the masking key + read = transport_.read(headerBuffer[0..4]); + if (read < 4) { + return false; + } + + readBuffer_ = new ubyte[](length); + read = transport_.read(readBuffer_); + if (read < length) { + return false; + } + + // Unmask the data + for (size_t i = 0; i < length; i++) { + readBuffer_[i] ^= headerBuffer[i % 4]; + } + + debug writef("FIN=%d, Opcode=%X, length=%d, payload=%s\n", + fin, + opcode, + length, + binary ? readBuffer_.toHexString() : cast(string)readBuffer_); + } + + switch (opcode) { + case Opcode.Close: + debug { + if (length >= 2) { + CloseCode closeCode; + try { + closeCode = to!CloseCode(bigEndianToNative!ushort(readBuffer_[0..2])); + } catch (ConvException) { + closeCode = CloseCode.NoStatusCode; + } + + string closeReason; + if (length == 2) { + closeReason = to!string(cast(CloseCode)closeCode); + } else { + closeReason = cast(string)readBuffer_[2..$]; + } + + writef("Connection closed: %d %s\n", closeCode, closeReason); + } + } + transport_.close(); + return false; + case Opcode.Ping: + pong(); + return readFrame(); + default: + return true; + } + } + + void resetHandshake() { + connection_ = false; + secWebSocketKey_ = false; + secWebSocketVersion_ = false; + upgrade_ = false; + } + + void sendBadRequest() { + auto header = "HTTP/1.1 400 Bad Request\r\n" ~ + "Server: Thrift/" ~ VERSION ~ "\r\n" ~ + "\r\n"; + transport_.write(cast(const(ubyte[]))header); + transport_.flush(); + transport_.close(); + } + + void writeFrameHeader(Opcode opcode = Opcode.Continuation) { + size_t headerSize = 1; + if (writeBuffer_.length < 126) { + ++headerSize; + } else if (writeBuffer_.length < 65536) { + headerSize += 3; + } else { + headerSize += 9; + } + // The server does not mask the response + + ubyte[] header = new ubyte[headerSize]; + if (opcode == Opcode.Continuation) { + header[0] = binary ? Opcode.Binary : Opcode.Text; + } + else { + header[0] = opcode; + } + header[0] |= 0x80; + if (writeBuffer_.length < 126) { + header[1] = cast(ubyte)writeBuffer_.length; + } else if (writeBuffer_.length < 65536) { + header[1] = 126; + header[2..4] = nativeToBigEndian(cast(ushort)writeBuffer_.length); + } else { + header[1] = 127; + header[2..10] = nativeToBigEndian(cast(ulong)writeBuffer_.length); + } + + transport_.write(header); + } + + enum WEBSOCKET_GUID = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"; + + TTransport transport_; + + string acceptKey_; + bool connection_; + bool secWebSocketKey_; + bool secWebSocketVersion_; + bool upgrade_; + ubyte[] readBuffer_; + ubyte[] writeBuffer_; +} + +class TServerWebSocketTransportFactory(bool binary) : TTransportFactory { + override TTransport getTransport(TTransport trans) { + return new TServerWebSocketTransport!binary(trans); + } +} + +alias TServerBinaryWebSocketTransportFactory = TServerWebSocketTransportFactory!true; +alias TServerTextWebSocketTransportFactory = TServerWebSocketTransportFactory!false; + +private enum CloseCode : ushort { + NormalClosure = 1000, + GoingAway = 1001, + ProtocolError = 1002, + UnsupportedDataType = 1003, + NoStatusCode = 1005, + AbnormalClosure = 1006, + InvalidData = 1007, + PolicyViolation = 1008, + MessageTooBig = 1009, + ExtensionExpected = 1010, + UnexpectedError = 1011, + NotSecure = 1015 +} + +private enum Opcode : ubyte { + Continuation = 0x0, + Text = 0x1, + Binary = 0x2, + Close = 0x8, + Ping = 0x9, + Pong = 0xA +} diff --git a/lib/d/test/Makefile.am b/lib/d/test/Makefile.am index 3b6a6f14a50..5ec8255bb25 100755 --- a/lib/d/test/Makefile.am +++ b/lib/d/test/Makefile.am @@ -19,9 +19,6 @@ AUTOMAKE_OPTIONS = serial-tests -BUILT_SOURCES = trusted-ca-certificate.pem server-certificate.pem - - # Thrift compiler rules debug_proto_gen = $(addprefix gen-d/, DebugProtoTest_types.d) @@ -99,17 +96,6 @@ thrift_test_server: %: %.d thrift_test_common.d test_utils.d $(thrift_test_gen) $(DMD) $(d_test_flags) -of$@ $^ -# Certificate generation targets (for the SSL tests). -# Currently, we just assume that the "openssl" tool is on the path, could be -# replaced by a more elaborate mechanism. - -server-certificate.pem: openssl.test.cnf - openssl req -new -x509 -nodes -config openssl.test.cnf \ - -out server-certificate.pem - -trusted-ca-certificate.pem: server-certificate.pem - cat server-certificate.pem > $@ - check-local: $(targets) clean-local: @@ -118,9 +104,8 @@ clean-local: # Tests ran as part of make check. -async_test_runner.sh: async_test trusted-ca-certificate.pem server-certificate.pem -thrift_test_runner.sh: thrift_test_client thrift_test_server \ - trusted-ca-certificate.pem server-certificate.pem +async_test_runner.sh: async_test +thrift_test_runner.sh: thrift_test_client thrift_test_server TESTS = $(ran_tests) diff --git a/lib/d/test/async_test.d b/lib/d/test/async_test.d index fb765ad6e8e..51529ba867e 100644 --- a/lib/d/test/async_test.d +++ b/lib/d/test/async_test.d @@ -104,18 +104,18 @@ void main(string[] args) { if (ssl) { auto clientSSLContext = new TSSLContext(); with (clientSSLContext) { - ciphers = "ALL:!ADH:!LOW:!EXP:!MD5:@STRENGTH"; authenticate = true; - loadTrustedCertificates("./trusted-ca-certificate.pem"); + ciphers = "ALL:!ADH:!LOW:!EXP:!MD5:@STRENGTH"; + loadTrustedCertificates("../../../test/keys/CA.pem"); } clientTransportFactory = new TAsyncSSLSocketFactory(clientSSLContext); serverSSLContext = new TSSLContext(); with (serverSSLContext) { serverSide = true; - loadCertificate("./server-certificate.pem"); - loadPrivateKey("./server-private-key.pem"); ciphers = "ALL:!ADH:!LOW:!EXP:!MD5:@STRENGTH"; + loadCertificate("../../../test/keys/server.crt"); + loadPrivateKey("../../../test/keys/server.key"); } } else { clientTransportFactory = new TBufferedTransportFactory; diff --git a/lib/d/test/async_test_runner.sh b/lib/d/test/async_test_runner.sh index 7d507ee929b..d56654f5091 100755 --- a/lib/d/test/async_test_runner.sh +++ b/lib/d/test/async_test_runner.sh @@ -24,5 +24,8 @@ CUR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" # Runs the async test in both SSL and non-SSL mode. ${CUR}/async_test > /dev/null || exit 1 echo "Non-SSL tests done." -${CUR}/async_test --ssl > /dev/null || exit 1 -echo "SSL tests done." + +# THRIFT-4905: disabled the following test as it deadlocks / hangs +# ${CUR}/async_test --ssl > /dev/null || exit 1 +# echo "SSL tests done." +echo "THRIFT-4905: SSL tests are disabled. Fix them." diff --git a/lib/d/test/openssl.test.cnf b/lib/d/test/openssl.test.cnf deleted file mode 100644 index 2ada30b7c79..00000000000 --- a/lib/d/test/openssl.test.cnf +++ /dev/null @@ -1,14 +0,0 @@ -[ req ] -default_bits = 2048 -default_keyfile = server-private-key.pem -distinguished_name = req_distinguished_name -x509_extensions = v3_ca -prompt = no - -[ req_distinguished_name ] -CN = localhost - -[ v3_ca ] -# Add ::1 to the list of allowed IPs so we can use ::1 to explicitly connect -# to localhost via IPv6. -subjectAltName = IP:::1 diff --git a/lib/d/test/test_utils.d b/lib/d/test/test_utils.d index 174100b796f..d0548b71439 100644 --- a/lib/d/test/test_utils.d +++ b/lib/d/test/test_utils.d @@ -35,6 +35,7 @@ import thrift.transport.base; import thrift.transport.buffered; import thrift.transport.framed; import thrift.transport.http; +import thrift.transport.zlib; // This is a likely victim of @@BUG4744@@ when used with command argument // parsing. @@ -79,6 +80,7 @@ enum TransportType { buffered, framed, http, + zlib, raw } @@ -90,6 +92,8 @@ TTransportFactory createTransportFactory(TransportType type) { return new TFramedTransportFactory; case TransportType.http: return new TServerHttpTransportFactory; + case TransportType.zlib: + return new TZlibTransportFactory; case TransportType.raw: return new TTransportFactory; } diff --git a/lib/d/test/thrift_test_client.d b/lib/d/test/thrift_test_client.d index 49419f71a4a..c9911e28b4e 100644 --- a/lib/d/test/thrift_test_client.d +++ b/lib/d/test/thrift_test_client.d @@ -35,6 +35,7 @@ import thrift.transport.base; import thrift.transport.buffered; import thrift.transport.framed; import thrift.transport.http; +import thrift.transport.zlib; import thrift.transport.socket; import thrift.transport.ssl; import thrift.util.hashset; @@ -47,6 +48,7 @@ enum TransportType { buffered, framed, http, + zlib, raw } @@ -68,6 +70,7 @@ void main(string[] args) { bool ssl; ProtocolType protocolType; TransportType transportType; + bool zlib; bool trace; getopt(args, @@ -75,6 +78,7 @@ void main(string[] args) { "protocol", &protocolType, "ssl", &ssl, "transport", &transportType, + "zlib", &zlib, "trace", &trace, "port", &port, "host", (string _, string value) { @@ -102,22 +106,28 @@ void main(string[] args) { socket = new TSocket(host, port); } - TProtocol protocol; + TTransport transport; final switch (transportType) { case TransportType.buffered: - protocol = createProtocol(new TBufferedTransport(socket), protocolType); + transport = new TBufferedTransport(socket); break; case TransportType.framed: - protocol = createProtocol(new TFramedTransport(socket), protocolType); + transport = new TFramedTransport(socket); break; case TransportType.http: - protocol = createProtocol( - new TClientHttpTransport(socket, host, "/service"), protocolType); + transport = new TClientHttpTransport(socket, host, "/service"); + break; + case TransportType.zlib: + transport = new TZlibTransport(socket); break; case TransportType.raw: - protocol = createProtocol(socket, protocolType); + transport = socket; break; } + if (zlib && transportType != TransportType.zlib) { + transport = new TZlibTransport(socket); + } + TProtocol protocol = createProtocol(transport, protocolType); auto client = tClient!ThriftTest(protocol); diff --git a/lib/d/test/thrift_test_server.d b/lib/d/test/thrift_test_server.d index b582253c77a..192ff31593e 100644 --- a/lib/d/test/thrift_test_server.d +++ b/lib/d/test/thrift_test_server.d @@ -20,7 +20,7 @@ module thrift_test_server; import core.stdc.errno : errno; -import core.stdc.signal : signal, sigfn_t, SIGINT, SIG_DFL, SIG_ERR; +import core.stdc.signal : signal, SIGINT, SIG_DFL, SIG_ERR; import core.thread : dur, Thread; import std.algorithm; import std.exception : enforce; @@ -42,6 +42,7 @@ import thrift.transport.base; import thrift.transport.buffered; import thrift.transport.framed; import thrift.transport.http; +import thrift.transport.zlib; import thrift.transport.ssl; import thrift.util.cancellation; import thrift.util.hashset; @@ -246,11 +247,12 @@ void main(string[] args) { size_t numIOThreads = 1; TransportType transportType; bool ssl = false; + bool zlib = false; bool trace = true; size_t taskPoolSize = totalCPUs; getopt(args, "port", &port, "protocol", &protocolType, "server-type", - &serverType, "ssl", &ssl, "num-io-threads", &numIOThreads, + &serverType, "ssl", &ssl, "zlib", &zlib, "num-io-threads", &numIOThreads, "task-pool-size", &taskPoolSize, "trace", &trace, "transport", &transportType); @@ -314,8 +316,7 @@ void main(string[] args) { processor, serverSocket, transportFactory, protocolFactory); // Set up SIGINT signal handling - sigfn_t oldHandler = signal(SIGINT, &handleSignal); - enforce(oldHandler != SIG_ERR, + enforce(signal(SIGINT, &handleSignal) != SIG_ERR, "Could not replace the SIGINT signal handler: errno {0}".format(errno())); // Set up a server cancellation trigger diff --git a/lib/dart/LICENSE_HEADER b/lib/dart/LICENSE similarity index 100% rename from lib/dart/LICENSE_HEADER rename to lib/dart/LICENSE diff --git a/lib/dart/Makefile.am b/lib/dart/Makefile.am index ab6ddc02256..373a883d6c8 100644 --- a/lib/dart/Makefile.am +++ b/lib/dart/Makefile.am @@ -22,12 +22,18 @@ all-local: clean-local: $(RM) -r .pub - find . -type d -name "packages" | xargs $(RM) -r + find . -type d -name ".dart_tool" | xargs $(RM) -r find . -type f -name ".packages" | xargs $(RM) - find . -type f -name "pubspec.lock" | xargs $(RM) + find . -type d -name "packages" | xargs $(RM) -r check-local: all +dist-hook: + $(RM) -r $(distdir)/.pub + find $(distdir) -type d -name ".dart_tool" | xargs $(RM) -r + find $(distdir) -type f -name ".packages" | xargs $(RM) + find $(distdir) -type d -name "packages" | xargs $(RM) -r + EXTRA_DIST = \ .analysis_options diff --git a/lib/dart/README.md b/lib/dart/README.md index 2be168ba00a..4c30291242a 100644 --- a/lib/dart/README.md +++ b/lib/dart/README.md @@ -23,4 +23,4 @@ under the License. Using Thrift with Dart ==================== -Dart 1.12.0 or newer is required +Dart 1.24.3 or newer is required diff --git a/lib/dart/lib/src/browser/t_web_socket.dart b/lib/dart/lib/src/browser/t_web_socket.dart index 17693b300af..dac9ffddeca 100644 --- a/lib/dart/lib/src/browser/t_web_socket.dart +++ b/lib/dart/lib/src/browser/t_web_socket.dart @@ -18,7 +18,7 @@ library thrift.src.browser; import 'dart:async'; -import 'dart:convert' show BASE64; +import 'package:dart2_constant/convert.dart' show base64; import 'dart:html' show CloseEvent; import 'dart:html' show Event; import 'dart:html' show MessageEvent; @@ -90,7 +90,7 @@ class TWebSocket implements TSocket { void _sendRequests() { while (isOpen && _requests.isNotEmpty) { Uint8List data = _requests.removeAt(0); - _socket.sendString(BASE64.encode(data)); + _socket.sendString(base64.encode(data)); } } @@ -113,8 +113,7 @@ class TWebSocket implements TSocket { void _onMessage(MessageEvent message) { try { - Uint8List data = - new Uint8List.fromList(BASE64.decode(message.data)); + Uint8List data = new Uint8List.fromList(base64.decode(message.data)); _onMessageController.add(data); } on FormatException catch (_) { var error = new TProtocolError(TProtocolErrorType.INVALID_DATA, diff --git a/lib/dart/lib/src/console/t_web_socket.dart b/lib/dart/lib/src/console/t_web_socket.dart index 4ed728444cd..c938a966f4e 100644 --- a/lib/dart/lib/src/console/t_web_socket.dart +++ b/lib/dart/lib/src/console/t_web_socket.dart @@ -18,7 +18,7 @@ library thrift.src.console.t_web_socket; import 'dart:async'; -import 'dart:convert' show BASE64; +import 'package:dart2_constant/convert.dart' show base64; import 'dart:io'; import 'dart:typed_data' show Uint8List; @@ -67,13 +67,12 @@ class TWebSocket implements TSocket { } void send(Uint8List data) { - _socket.add(BASE64.encode(data)); + _socket.add(base64.encode(data)); } void _onMessage(String message) { try { - Uint8List data = - new Uint8List.fromList(BASE64.decode(message)); + Uint8List data = new Uint8List.fromList(base64.decode(message)); _onMessageController.add(data); } on FormatException catch (_) { var error = new TProtocolError(TProtocolErrorType.INVALID_DATA, diff --git a/lib/dart/lib/src/protocol/t_compact_protocol.dart b/lib/dart/lib/src/protocol/t_compact_protocol.dart index 72d7641feeb..ee8094f8e90 100644 --- a/lib/dart/lib/src/protocol/t_compact_protocol.dart +++ b/lib/dart/lib/src/protocol/t_compact_protocol.dart @@ -187,7 +187,7 @@ class TCompactProtocol extends TProtocol { void writeDouble(double d) { if (d == null) d = 0.0; - tempBD.setFloat64(0, d, Endianness.LITTLE_ENDIAN); + tempBD.setFloat64(0, d, Endianness.little); transport.write(tempBD.buffer.asUint8List(), 0, 8); } @@ -364,7 +364,7 @@ class TCompactProtocol extends TProtocol { double readDouble() { transport.readAll(tempList, 0, 8); - return tempList.buffer.asByteData().getFloat64(0, Endianness.LITTLE_ENDIAN); + return tempList.buffer.asByteData().getFloat64(0, Endianness.little); } String readString() { diff --git a/lib/dart/lib/src/protocol/t_json_protocol.dart b/lib/dart/lib/src/protocol/t_json_protocol.dart index 7fbb7c443cc..180568ddf40 100644 --- a/lib/dart/lib/src/protocol/t_json_protocol.dart +++ b/lib/dart/lib/src/protocol/t_json_protocol.dart @@ -155,8 +155,8 @@ class TJsonProtocol extends TProtocol { _context.write(); transport.writeAll(_Constants.QUOTE_BYTES); - String base64 = BASE64.encode(bytes); - transport.writeAll(utf8Codec.encode(base64)); + String base64text = base64.encode(bytes); + transport.writeAll(utf8Codec.encode(base64text)); transport.writeAll(_Constants.QUOTE_BYTES); } @@ -421,9 +421,9 @@ class TJsonProtocol extends TProtocol { Uint8List _readJsonBase64() { // convert UTF-8 bytes of a Base 64 encoded string to binary bytes Uint8List base64Bytes = _readJsonString(); - String base64 = utf8Codec.decode(base64Bytes); + String base64text = utf8Codec.decode(base64Bytes); - return new Uint8List.fromList(BASE64.decode(base64)); + return new Uint8List.fromList(base64.decode(base64text)); } void _readJsonObjectStart() { diff --git a/lib/dart/lib/src/protocol/t_protocol_util.dart b/lib/dart/lib/src/protocol/t_protocol_util.dart index ad20068c201..841ea8217d7 100644 --- a/lib/dart/lib/src/protocol/t_protocol_util.dart +++ b/lib/dart/lib/src/protocol/t_protocol_util.dart @@ -101,7 +101,7 @@ class TProtocolUtil { break; default: - break; + throw new TProtocolError(TProtocolErrorType.INVALID_DATA, "Invalid data"); } } } diff --git a/lib/dart/lib/src/serializer/t_deserializer.dart b/lib/dart/lib/src/serializer/t_deserializer.dart index ed1d139aa2a..aefbee25b52 100644 --- a/lib/dart/lib/src/serializer/t_deserializer.dart +++ b/lib/dart/lib/src/serializer/t_deserializer.dart @@ -26,7 +26,7 @@ class TDeserializer { this.transport = new TBufferedTransport(); if (protocolFactory == null) { - protocolFactory = new TBinaryProtocolFactory(); + protocolFactory = new TBinaryProtocolFactory(); } this.protocol = protocolFactory.getProtocol(this.transport); @@ -41,8 +41,8 @@ class TDeserializer { } void readString(TBase base, String data) { - transport.writeAll(BASE64.decode(data)); + transport.writeAll(base64.decode(data)); transport.flush(); base.read(protocol); diff --git a/lib/dart/lib/src/serializer/t_serializer.dart b/lib/dart/lib/src/serializer/t_serializer.dart index 20ddb6d9725..feec8226d49 100644 --- a/lib/dart/lib/src/serializer/t_serializer.dart +++ b/lib/dart/lib/src/serializer/t_serializer.dart @@ -43,6 +43,6 @@ class TSerializer { Uint8List bytes = transport.consumeWriteBuffer(); - return BASE64.encode(bytes); + return base64.encode(bytes); } } diff --git a/lib/dart/lib/src/transport/t_http_transport.dart b/lib/dart/lib/src/transport/t_http_transport.dart index aa78e9c1587..630213fbfc1 100644 --- a/lib/dart/lib/src/transport/t_http_transport.dart +++ b/lib/dart/lib/src/transport/t_http_transport.dart @@ -44,7 +44,7 @@ class THttpClientTransport extends TBufferedTransport { } Future flush() { - var requestBody = BASE64.encode(consumeWriteBuffer()); + var requestBody = base64.encode(consumeWriteBuffer()); // Use a sync completer to ensure that the buffer can be read immediately // after the read buffer is set, and avoid a race condition where another @@ -56,8 +56,7 @@ class THttpClientTransport extends TBufferedTransport { .then((response) { Uint8List data; try { - data = new Uint8List.fromList( - BASE64.decode(response.body)); + data = new Uint8List.fromList(base64.decode(response.body)); } on FormatException catch (_) { throw new TProtocolError(TProtocolErrorType.INVALID_DATA, "Expected a Base 64 encoded string."); diff --git a/lib/dart/lib/thrift.dart b/lib/dart/lib/thrift.dart index 3d9bb016c27..c429d773cc5 100644 --- a/lib/dart/lib/thrift.dart +++ b/lib/dart/lib/thrift.dart @@ -19,11 +19,12 @@ library thrift; import 'dart:async'; import 'dart:collection'; -import 'dart:convert' show Utf8Codec, BASE64; +import 'dart:convert' show Utf8Codec; import 'dart:typed_data' show ByteData; -import 'dart:typed_data' show Endianness; import 'dart:typed_data' show Uint8List; +import 'package:dart2_constant/convert.dart' show base64; +import 'package:dart2_constant/typed_data.dart' show Endianness; import 'package:fixnum/fixnum.dart'; import 'package:http/http.dart' show Client; import 'package:logging/logging.dart'; diff --git a/lib/dart/pubspec.yaml b/lib/dart/pubspec.yaml index 365b2cdb810..1999b58247b 100644 --- a/lib/dart/pubspec.yaml +++ b/lib/dart/pubspec.yaml @@ -16,7 +16,7 @@ # under the License. name: thrift -version: 0.12.0 +version: 0.14.0 description: > A Dart library for Apache Thrift author: Apache Thrift Developers @@ -24,20 +24,15 @@ homepage: http://thrift.apache.org documentation: http://thrift.apache.org environment: - sdk: ">=1.13.0 <2.0.0" + sdk: ">=1.24.3 <3.0.0" dependencies: + dart2_constant: ^1.0.0 fixnum: ^0.10.2 http: ^0.11.3 logging: ^0.11.0 dev_dependencies: - # test - mockito: ^1.0.0 - test: ^0.12.0 - - # dart_dev - https://github.com/Workiva/dart_dev - dart_dev: ^1.5.0 - coverage: ^0.7.3 - dart_style: ">=0.2.4 <0.3.0" - dartdoc: ^0.9.0 + dart_dev: ^2.0.0 + mockito: ">=2.2.2 <4.0.0" + test: ">=0.12.30 <2.0.0" diff --git a/lib/dart/test/protocol/t_protocol_test.dart b/lib/dart/test/protocol/t_protocol_test.dart index 0e6cde50d4a..dc63dbb71c0 100644 --- a/lib/dart/test/protocol/t_protocol_test.dart +++ b/lib/dart/test/protocol/t_protocol_test.dart @@ -18,9 +18,9 @@ library thrift.test.transport.t_json_protocol_test; import 'dart:async'; -import 'dart:convert' show UTF8; import 'dart:typed_data' show Uint8List; +import 'package:dart2_constant/convert.dart' show utf8; import 'package:test/test.dart'; import 'package:thrift/thrift.dart'; @@ -362,7 +362,7 @@ void main() { UTF-8: 0xF0 0x9D 0x84 0x9E UTF-16: 0xD834 0xDD1E */ - var buffer = UTF8.encode(r'"\u0001\u0e01 \ud834\udd1e"'); + var buffer = utf8.encode(r'"\u0001\u0e01 \ud834\udd1e"'); var transport = new TBufferedTransport(); transport.writeAll(buffer); @@ -372,7 +372,7 @@ void main() { var subject = protocol.readString(); expect(subject, - UTF8.decode([0x01, 0xE0, 0xB8, 0x81, 0x20, 0xF0, 0x9D, 0x84, 0x9E])); + utf8.decode([0x01, 0xE0, 0xB8, 0x81, 0x20, 0xF0, 0x9D, 0x84, 0x9E])); }); group('shared tests', sharedTests); diff --git a/lib/dart/test/transport/t_framed_transport_test.dart b/lib/dart/test/transport/t_framed_transport_test.dart index e072e6877ab..7ab4905398e 100644 --- a/lib/dart/test/transport/t_framed_transport_test.dart +++ b/lib/dart/test/transport/t_framed_transport_test.dart @@ -18,9 +18,9 @@ library thrift.test.transport.t_framed_transport_test; import 'dart:async'; -import 'dart:convert'; import 'dart:typed_data' show Uint8List; +import 'package:dart2_constant/convert.dart' show utf8; import 'package:test/test.dart'; import 'package:thrift/thrift.dart'; @@ -66,14 +66,14 @@ void main() { expectNoReadableBytes(); // write first batch of body - socket.messageController.add(new Uint8List.fromList(UTF8.encode("He"))); + socket.messageController.add(new Uint8List.fromList(utf8.encode("He"))); // you shouldn't be able to get any bytes from the read, // because the frame has been consumed internally expectNoReadableBytes(); // write second batch of body - socket.messageController.add(new Uint8List.fromList(UTF8.encode("llo!"))); + socket.messageController.add(new Uint8List.fromList(utf8.encode("llo!"))); // have to wait for the flush to complete, // because it's only then that the frame is available for reading @@ -83,7 +83,7 @@ void main() { // at this point the frame is complete, so we expect the read to complete readBytes = transport.read(readBuffer, 0, readBuffer.lengthInBytes); expect(readBytes, 6); - expect(readBuffer.sublist(0, 6), UTF8.encode("Hello!")); + expect(readBuffer.sublist(0, 6), utf8.encode("Hello!")); }); test('Test transport reads messages where header is sent in pieces ' @@ -112,14 +112,14 @@ void main() { readBytes = expectNoReadableBytes(); // write first batch of body - socket.messageController.add(new Uint8List.fromList(UTF8.encode("H"))); + socket.messageController.add(new Uint8List.fromList(utf8.encode("H"))); // you shouldn't be able to get any bytes from the read, // because the frame has been consumed internally expectNoReadableBytes(); // write second batch of body - socket.messageController.add(new Uint8List.fromList(UTF8.encode("i!"))); + socket.messageController.add(new Uint8List.fromList(utf8.encode("i!"))); // have to wait for the flush to complete, // because it's only then that the frame is available for reading @@ -129,7 +129,7 @@ void main() { // at this point the frame is complete, so we expect the read to complete readBytes = transport.read(readBuffer, 0, readBuffer.lengthInBytes); expect(readBytes, 3); - expect(readBuffer.sublist(0, 3), UTF8.encode("Hi!")); + expect(readBuffer.sublist(0, 3), utf8.encode("Hi!")); }); }); } diff --git a/lib/dart/test/transport/t_http_transport_test.dart b/lib/dart/test/transport/t_http_transport_test.dart index 66f3d054bed..03ccede9ab5 100644 --- a/lib/dart/test/transport/t_http_transport_test.dart +++ b/lib/dart/test/transport/t_http_transport_test.dart @@ -19,9 +19,10 @@ library thrift.test.transport.t_socket_transport_test; import 'dart:async'; import 'dart:convert' show Encoding; -import 'dart:convert' show Utf8Codec, BASE64; +import 'dart:convert' show Utf8Codec; import 'dart:typed_data' show Uint8List; +import 'package:dart2_constant/convert.dart' show base64; import 'package:http/http.dart' show BaseRequest; import 'package:http/http.dart' show Client; import 'package:http/http.dart' show Response; @@ -52,15 +53,14 @@ void main() { expect(client.postRequest, isNotEmpty); - var requestText = - utf8Codec.decode(BASE64.decode(client.postRequest)); + var requestText = utf8Codec.decode(base64.decode(client.postRequest)); expect(requestText, expectedText); }); test('Test transport receives response', () async { var expectedText = 'my response'; var expectedBytes = utf8Codec.encode(expectedText); - client.postResponse = BASE64.encode(expectedBytes); + client.postResponse = base64.encode(expectedBytes); transport.writeAll(utf8Codec.encode('my request')); expect(transport.hasReadData, isFalse); @@ -94,7 +94,7 @@ void main() { // prepare a response transport.writeAll(utf8Codec.encode('request 1')); - client.postResponse = BASE64.encode(expectedBytes); + client.postResponse = base64.encode(expectedBytes); Future responseReady = transport.flush().then((_) { var buffer = new Uint8List(expectedBytes.length); @@ -105,7 +105,7 @@ void main() { // prepare a second response transport.writeAll(utf8Codec.encode('request 2')); var response2Bytes = utf8Codec.encode('response 2'); - client.postResponse = BASE64.encode(response2Bytes); + client.postResponse = base64.encode(response2Bytes); await transport.flush(); await responseReady; diff --git a/lib/dart/test/transport/t_socket_transport_test.dart b/lib/dart/test/transport/t_socket_transport_test.dart index 929ab17b1a7..90bffbe5434 100644 --- a/lib/dart/test/transport/t_socket_transport_test.dart +++ b/lib/dart/test/transport/t_socket_transport_test.dart @@ -18,9 +18,11 @@ library thrift.test.transport.t_socket_transport_test; import 'dart:async'; -import 'dart:convert' show Utf8Codec, BASE64; +import 'dart:convert' show Utf8Codec; import 'dart:typed_data' show Uint8List; +import 'package:dart2_constant/convert.dart' show base64; +import 'package:dart2_constant/core.dart' as core; import 'package:mockito/mockito.dart'; import 'package:test/test.dart'; import 'package:thrift/thrift.dart'; @@ -30,14 +32,13 @@ void main() { final requestText = 'my test request'; final requestBytes = new Uint8List.fromList(utf8Codec.encode(requestText)); - final requestBase64 = BASE64.encode(requestBytes); + final requestBase64 = base64.encode(requestBytes); final responseText = 'response 1'; final responseBytes = new Uint8List.fromList(utf8Codec.encode(responseText)); - final responseBase64 = BASE64.encode(responseBytes); + final responseBase64 = base64.encode(responseBytes); - final framedResponseBase64 = - BASE64.encode(_getFramedResponse(responseBytes)); + final framedResponseBase64 = base64.encode(_getFramedResponse(responseBytes)); group('TClientSocketTransport', () { FakeSocket socket; @@ -117,7 +118,7 @@ void main() { protocolFactory.message = new TMessage('foo', TMessageType.CALL, 123); transport = new TAsyncClientSocketTransport( socket, new TMessageReader(protocolFactory), - responseTimeout: Duration.ZERO); + responseTimeout: core.Duration.zero); await transport.open(); transport.writeAll(requestBytes); }); @@ -139,7 +140,7 @@ void main() { var response2Text = 'response 2'; var response2Bytes = new Uint8List.fromList(utf8Codec.encode(response2Text)); - var response2Base64 = BASE64.encode(response2Bytes); + var response2Base64 = base64.encode(response2Bytes); protocolFactory.message = new TMessage('foo2', TMessageType.REPLY, 124); socket.receiveFakeMessage(response2Base64); @@ -169,7 +170,7 @@ void main() { transport = new TFramedTransport(new TAsyncClientSocketTransport( socket, messageReader, - responseTimeout: Duration.ZERO)); + responseTimeout: core.Duration.zero)); await transport.open(); transport.writeAll(requestBytes); }); @@ -275,11 +276,10 @@ class FakeSocket extends TSocket { _sendPayload = data; } - void receiveFakeMessage(String base64) { + void receiveFakeMessage(String base64text) { if (!isOpen) throw new StateError('The socket is not open'); - var message = - new Uint8List.fromList(BASE64.decode(base64)); + var message = new Uint8List.fromList(base64.decode(base64text)); _onMessageController.add(message); } } diff --git a/lib/delphi/src/Thrift.Collections.pas b/lib/delphi/src/Thrift.Collections.pas index b2206cbf0a1..ad852accaa8 100644 --- a/lib/delphi/src/Thrift.Collections.pas +++ b/lib/delphi/src/Thrift.Collections.pas @@ -22,7 +22,7 @@ interface uses - Generics.Collections, Generics.Defaults, Thrift.Utils; + SysUtils, Generics.Collections, Generics.Defaults, Thrift.Utils; type @@ -30,11 +30,11 @@ interface TArray = array of T; {$IFEND} - IThriftContainer = interface - ['{93DEF5A0-D162-461A-AB22-5B4EE0734050}'] - function ToString: string; + IThriftContainer = interface( ISupportsToString) + ['{E05C0F9D-A4F5-491D-AADA-C926B4BDB6E4}'] end; + IThriftDictionary = interface(IThriftContainer) ['{25EDD506-F9D1-4008-A40F-5940364B7E46}'] function GetEnumerator: TEnumerator>; @@ -64,10 +64,10 @@ interface property Values: TDictionary.TValueCollection read GetValues; end; - TThriftDictionaryImpl = class( TInterfacedObject, IThriftDictionary) - private + TThriftDictionaryImpl = class( TInterfacedObject, IThriftDictionary, IThriftContainer, ISupportsToString) + strict private FDictionaly : TDictionary; - protected + strict protected function GetEnumerator: TEnumerator>; function GetKeys: TDictionary.TKeyCollection; @@ -95,6 +95,7 @@ TThriftDictionaryImpl = class( TInterfacedObject, IThriftDictiona public constructor Create(ACapacity: Integer = 0); destructor Destroy; override; + function ToString : string; override; end; IThriftList = interface(IThriftContainer) @@ -140,10 +141,10 @@ TThriftDictionaryImpl = class( TInterfacedObject, IThriftDictiona property Items[Index: Integer]: T read GetItem write SetItem; default; end; - TThriftListImpl = class( TInterfacedObject, IThriftList) - private + TThriftListImpl = class( TInterfacedObject, IThriftList, IThriftContainer, ISupportsToString) + strict private FList : TList; - protected + strict protected function GetEnumerator: TEnumerator; function GetCapacity: Integer; procedure SetCapacity(Value: Integer); @@ -186,6 +187,7 @@ TThriftListImpl = class( TInterfacedObject, IThriftList) public constructor Create; destructor Destroy; override; + function ToString : string; override; end; IHashSet = interface(IThriftContainer) @@ -202,11 +204,11 @@ TThriftListImpl = class( TInterfacedObject, IThriftList) function Remove( const item: TValue ): Boolean; end; - THashSetImpl = class( TInterfacedObject, IHashSet) - private + THashSetImpl = class( TInterfacedObject, IHashSet, IThriftContainer, ISupportsToString) + strict private FDictionary : IThriftDictionary; FIsReadOnly: Boolean; - protected + strict protected function GetEnumerator: TEnumerator; function GetIsReadOnly: Boolean; function GetCount: Integer; @@ -219,6 +221,7 @@ THashSetImpl = class( TInterfacedObject, IHashSet) function Remove( const item: TValue ): Boolean; public constructor Create; + function ToString : string; override; end; implementation @@ -287,6 +290,28 @@ function THashSetImpl.Remove( const item: TValue): Boolean; end; end; +function THashSetImpl.ToString : string; +var elm : TValue; + sb : TThriftStringBuilder; + first : Boolean; +begin + sb := TThriftStringBuilder.Create('{'); + try + first := TRUE; + for elm in FDictionary.Keys do begin + if first + then first := FALSE + else sb.Append(', '); + + sb.Append( StringUtils.ToString(elm)); + end; + sb.Append('}'); + Result := sb.ToString; + finally + sb.Free; + end; +end; + { TThriftDictionaryImpl } procedure TThriftDictionaryImpl.Add(const Key: TKey; @@ -393,6 +418,32 @@ function TThriftDictionaryImpl.ToArray: TArray {$IFEND} end; +function TThriftDictionaryImpl.ToString : string; +var pair : TPair; + sb : TThriftStringBuilder; + first : Boolean; +begin + sb := TThriftStringBuilder.Create('{'); + try + first := TRUE; + for pair in FDictionaly do begin + if first + then first := FALSE + else sb.Append(', '); + + sb.Append( '('); + sb.Append( StringUtils.ToString(pair.Key)); + sb.Append(' => '); + sb.Append( StringUtils.ToString(pair.Value)); + sb.Append(')'); + end; + sb.Append('}'); + Result := sb.ToString; + finally + sb.Free; + end; +end; + procedure TThriftDictionaryImpl.TrimExcess; begin FDictionaly.TrimExcess; @@ -588,7 +639,7 @@ procedure TThriftListImpl.Sort; procedure TThriftListImpl.Sort(const AComparer: IComparer); begin - FList.Sort; + FList.Sort(AComparer); end; function TThriftListImpl.ToArray: TArray; @@ -611,6 +662,28 @@ function TThriftListImpl.ToArray: TArray; {$IFEND} end; +function TThriftListImpl.ToString : string; +var elm : T; + sb : TThriftStringBuilder; + first : Boolean; +begin + sb := TThriftStringBuilder.Create('{'); + try + first := TRUE; + for elm in FList do begin + if first + then first := FALSE + else sb.Append(', '); + + sb.Append( StringUtils.ToString(elm)); + end; + sb.Append('}'); + Result := sb.ToString; + finally + sb.Free; + end; +end; + procedure TThriftListImpl.TrimExcess; begin FList.TrimExcess; diff --git a/lib/delphi/src/Thrift.Configuration.pas b/lib/delphi/src/Thrift.Configuration.pas new file mode 100644 index 00000000000..0cb11af350e --- /dev/null +++ b/lib/delphi/src/Thrift.Configuration.pas @@ -0,0 +1,121 @@ +(* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + *) + +unit Thrift.Configuration; + +interface + +uses + SysUtils, Generics.Collections, Generics.Defaults; + +const + DEFAULT_RECURSION_LIMIT = 64; + DEFAULT_MAX_MESSAGE_SIZE = 100 * 1024 * 1024; // 100 MB + DEFAULT_MAX_FRAME_SIZE = 16384000; // this value is used consistently across all Thrift libraries + + DEFAULT_THRIFT_TIMEOUT = 5 * 1000; // ms + +type + IThriftConfiguration = interface + ['{ADD75449-1A67-4B78-9B75-502A1E338CFC}'] + function GetRecursionLimit : Cardinal; + procedure SetRecursionLimit( const value : Cardinal); + function GetMaxFrameSize : Cardinal; + procedure SetMaxFrameSize( const value : Cardinal); + function GetMaxMessageSize : Cardinal; + procedure SetMaxMessageSize( const value : Cardinal); + + property RecursionLimit : Cardinal read GetRecursionLimit write SetRecursionLimit; + property MaxFrameSize : Cardinal read GetMaxFrameSize write SetMaxFrameSize; + property MaxMessageSize : Cardinal read GetMaxMessageSize write SetMaxMessageSize; + end; + + + TThriftConfigurationImpl = class( TInterfacedObject, IThriftConfiguration) + strict protected + FRecursionLimit : Cardinal; + FMaxFrameSize : Cardinal; + FMaxMessageSize : Cardinal; + + // IThriftConfiguration + function GetRecursionLimit : Cardinal; + procedure SetRecursionLimit( const value : Cardinal); + function GetMaxFrameSize : Cardinal; + procedure SetMaxFrameSize( const value : Cardinal); + function GetMaxMessageSize : Cardinal; + procedure SetMaxMessageSize( const value : Cardinal); + + public + constructor Create; + end; + + +implementation + + +{ TThriftConfigurationImpl } + + +constructor TThriftConfigurationImpl.Create; +begin + inherited Create; + + FRecursionLimit := DEFAULT_RECURSION_LIMIT; + FMaxFrameSize := DEFAULT_MAX_FRAME_SIZE; + FMaxMessageSize := DEFAULT_MAX_MESSAGE_SIZE; +end; + + +function TThriftConfigurationImpl.GetRecursionLimit: Cardinal; +begin + result := FRecursionLimit; +end; + + +procedure TThriftConfigurationImpl.SetRecursionLimit(const value: Cardinal); +begin + FRecursionLimit := value; +end; + + +function TThriftConfigurationImpl.GetMaxFrameSize: Cardinal; +begin + result := FMaxFrameSize; +end; + + +procedure TThriftConfigurationImpl.SetMaxFrameSize(const value: Cardinal); +begin + FMaxFrameSize := value; +end; + + +function TThriftConfigurationImpl.GetMaxMessageSize: Cardinal; +begin + result := FMaxMessageSize; +end; + + +procedure TThriftConfigurationImpl.SetMaxMessageSize(const value: Cardinal); +begin + FMaxMessageSize := value; +end; + + +end. diff --git a/lib/delphi/src/Thrift.Exception.pas b/lib/delphi/src/Thrift.Exception.pas index 5d15c365690..88b1cfe03e7 100644 --- a/lib/delphi/src/Thrift.Exception.pas +++ b/lib/delphi/src/Thrift.Exception.pas @@ -29,6 +29,8 @@ interface type // base class for all Thrift exceptions TException = class( SysUtils.Exception) + strict private + function GetMessageText : string; public function Message : string; // hide inherited property: allow read, but prevent accidental writes procedure UpdateMessageProperty; // update inherited message property with toString() @@ -45,17 +47,25 @@ function TException.Message; // allow read (exception summary), but prevent accidental writes // read will return the exception summary begin - result := Self.ToString; + result := Self.GetMessageText; end; + procedure TException.UpdateMessageProperty; // Update the inherited Message property to better conform to standard behaviour. // Nice benefit: The IDE is now able to show the exception message again. begin - inherited Message := Self.ToString; // produces a summary text + inherited Message := Self.GetMessageText; end; +function TException.GetMessageText : string; +// produces a summary text +begin + result := Self.ToString; + if (result <> '') and (result[1] = '(') + then result := Copy(result,2,Length(result)-2); +end; end. diff --git a/lib/delphi/src/Thrift.Processor.Multiplex.pas b/lib/delphi/src/Thrift.Processor.Multiplex.pas index 8cf23db0760..ba77d946446 100644 --- a/lib/delphi/src/Thrift.Processor.Multiplex.pas +++ b/lib/delphi/src/Thrift.Processor.Multiplex.pas @@ -62,19 +62,19 @@ interface TMultiplexedProcessorImpl = class( TInterfacedObject, IMultiplexedProcessor, IProcessor) - private type + strict private type // Our goal was to work with any protocol. In order to do that, we needed // to allow them to call readMessageBegin() and get a TMessage in exactly // the standard format, without the service name prepended to TMessage.name. TStoredMessageProtocol = class( TProtocolDecorator) - private + strict private FMessageBegin : TThriftMessage; public constructor Create( const protocol : IProtocol; const aMsgBegin : TThriftMessage); function ReadMessageBegin: TThriftMessage; override; end; - private + strict private FServiceProcessorMap : TDictionary; FDefaultProcessor : IProcessor; @@ -113,12 +113,6 @@ constructor TMultiplexedProcessorImpl.TStoredMessageProtocol.Create( const proto end; -function TMultiplexedProcessorImpl.TStoredMessageProtocol.ReadMessageBegin: TThriftMessage; -begin - result := FMessageBegin; -end; - - constructor TMultiplexedProcessorImpl.Create; begin inherited Create; @@ -136,6 +130,13 @@ destructor TMultiplexedProcessorImpl.Destroy; end; +function TMultiplexedProcessorImpl.TStoredMessageProtocol.ReadMessageBegin: TThriftMessage; +begin + Reset; + result := FMessageBegin; +end; + + procedure TMultiplexedProcessorImpl.RegisterProcessor( const serviceName : String; const processor : IProcessor; const asDefault : Boolean); begin FServiceProcessorMap.Add( serviceName, processor); diff --git a/lib/delphi/src/Thrift.Protocol.Compact.pas b/lib/delphi/src/Thrift.Protocol.Compact.pas index 1c1b3da7174..665cfc4ba34 100644 --- a/lib/delphi/src/Thrift.Protocol.Compact.pas +++ b/lib/delphi/src/Thrift.Protocol.Compact.pas @@ -28,6 +28,7 @@ interface SysUtils, Math, Generics.Collections, + Thrift.Configuration, Thrift.Transport, Thrift.Protocol, Thrift.Utils; @@ -47,7 +48,7 @@ TFactory = class( TInterfacedObject, IProtocolFactory) function GetProtocol( const trans: ITransport): IProtocol; end; - private const + strict private const { TODO static TStruct ANONYMOUS_STRUCT = new TStruct(""); @@ -61,7 +62,7 @@ TFactory = class( TInterfacedObject, IProtocolFactory) TYPE_BITS = Byte( $07); // 0000 0111 TYPE_SHIFT_AMOUNT = Byte( 5); - private type + strict private type // All of the on-wire type codes. Types = ( STOP = $00, @@ -79,7 +80,7 @@ TFactory = class( TInterfacedObject, IProtocolFactory) STRUCT = $0C ); - private const + strict private const ttypeToCompactType : array[TType] of Types = ( Types.STOP, // Stop = 0, Types(-1), // Void = 1, @@ -115,7 +116,7 @@ TFactory = class( TInterfacedObject, IProtocolFactory) TType.Struct // STRUCT ); - private + strict private // Used to keep track of the last field for the current and previous structs, // so we can do the delta stuff. lastField_ : TStack; @@ -123,19 +124,17 @@ TFactory = class( TInterfacedObject, IProtocolFactory) // If we encounter a boolean field begin, save the TField here so it can // have the value incorporated. - private booleanField_ : TThriftField; + strict private booleanField_ : TThriftField; // If we Read a field header, and it's a boolean field, save the boolean // value here so that ReadBool can use it. - private boolValue_ : ( unused, bool_true, bool_false); + strict private boolValue_ : ( unused, bool_true, bool_false); public constructor Create(const trans : ITransport); destructor Destroy; override; - procedure Reset; - - private + strict private procedure WriteByteDirect( const b : Byte); overload; // Writes a byte without any possibility of all that field header nonsense. @@ -145,7 +144,7 @@ TFactory = class( TInterfacedObject, IProtocolFactory) // TODO: make a permanent buffer like WriteVarint64? procedure WriteVarint32( n : Cardinal); - private + strict private // The workhorse of WriteFieldBegin. It has the option of doing a 'type override' // of the type header. This is used specifically in the boolean field case. procedure WriteFieldBeginInternal( const field : TThriftField; typeOverride : Byte); @@ -172,7 +171,7 @@ TFactory = class( TInterfacedObject, IProtocolFactory) procedure WriteDouble( const dub: Double); override; procedure WriteBinary( const b: TBytes); overload; override; - private + private // unit visible stuff class function DoubleToInt64Bits( const db : Double) : Int64; class function Int64BitsToDouble( const i64 : Int64) : Double; @@ -193,6 +192,10 @@ TFactory = class( TInterfacedObject, IProtocolFactory) //Convert a Int64 into little-endian bytes in buf starting at off and going until off+7. class procedure fixedLongToBytes( const n : Int64; var buf : TBytes); + strict protected + function GetMinSerializedSize( const aType : TType) : Integer; override; + procedure Reset; override; + public function ReadMessageBegin: TThriftMessage; override; procedure ReadMessageEnd(); override; @@ -266,7 +269,7 @@ function TCompactProtocolImpl.TFactory.GetProtocol( const trans: ITransport): IP //--- TCompactProtocolImpl ------------------------------------------------- -constructor TCompactProtocolImpl.Create(const trans: ITransport); +constructor TCompactProtocolImpl.Create( const trans : ITransport); begin inherited Create( trans); @@ -291,6 +294,7 @@ destructor TCompactProtocolImpl.Destroy; procedure TCompactProtocolImpl.Reset; begin + inherited Reset; lastField_.Clear(); lastFieldId_ := 0; Init( booleanField_, '', TType.Stop, 0); @@ -686,7 +690,8 @@ procedure TCompactProtocolImpl.ReadStructEnd; // Read a field header off the wire. function TCompactProtocolImpl.ReadFieldBegin: TThriftField; var type_ : Byte; - fieldId, modifier : ShortInt; + modifier : ShortInt; + fieldId : SmallInt; begin type_ := Byte( ReadByte); @@ -700,7 +705,7 @@ function TCompactProtocolImpl.ReadFieldBegin: TThriftField; modifier := ShortInt( (type_ and $F0) shr 4); if (modifier = 0) then fieldId := ReadI16 // not a delta. look ahead for the zigzag varint field id. - else fieldId := ShortInt( lastFieldId_ + modifier); // add the delta to the last Read field id. + else fieldId := SmallInt( lastFieldId_ + modifier); // add the delta to the last Read field id. Init( result, '', getTType(Byte(type_ and $0F)), fieldId); @@ -734,6 +739,7 @@ function TCompactProtocolImpl.ReadMapBegin: TThriftMap; val := getTType( Byte( keyAndValueType and $F)); Init( result, key, val, size); ASSERT( (result.KeyType = key) and (result.ValueType = val)); + CheckReadBytesAvailable(result); end; @@ -754,6 +760,7 @@ function TCompactProtocolImpl.ReadListBegin: TThriftList; type_ := getTType( size_and_type); Init( result, type_, size); + CheckReadBytesAvailable(result); end; @@ -774,6 +781,7 @@ function TCompactProtocolImpl.ReadSetBegin: TThriftSet; type_ := getTType( size_and_type); Init( result, type_, size); + CheckReadBytesAvailable(result); end; @@ -835,6 +843,7 @@ function TCompactProtocolImpl.ReadBinary: TBytes; var length : Integer; begin length := Integer( ReadVarint32); + FTrans.CheckReadBytesAvailable(length); SetLength( result, length); if (length > 0) then Transport.ReadAll( result, 0, length); @@ -967,6 +976,32 @@ class function TCompactProtocolImpl.getCompactType( const ttype : TType) : Byte; end; +function TCompactProtocolImpl.GetMinSerializedSize( const aType : TType) : Integer; +// Return the minimum number of bytes a type will consume on the wire +begin + case aType of + TType.Stop: result := 0; + TType.Void: result := 0; + TType.Bool_: result := SizeOf(Byte); + TType.Byte_: result := SizeOf(Byte); + TType.Double_: result := 8; // uses fixedLongToBytes() which always writes 8 bytes + TType.I16: result := SizeOf(Byte); + TType.I32: result := SizeOf(Byte); + TType.I64: result := SizeOf(Byte); + TType.String_: result := SizeOf(Byte); // string length + TType.Struct: result := 0; // empty struct + TType.Map: result := SizeOf(Byte); // element count + TType.Set_: result := SizeOf(Byte); // element count + TType.List: result := SizeOf(Byte); // element count + else + raise TTransportExceptionBadArgs.Create('Unhandled type code'); + end; +end; + + + + + //--- unit tests ------------------------------------------- {$IFDEF Debug} diff --git a/lib/delphi/src/Thrift.Protocol.JSON.pas b/lib/delphi/src/Thrift.Protocol.JSON.pas index 30600aa808b..61cad8b627c 100644 --- a/lib/delphi/src/Thrift.Protocol.JSON.pas +++ b/lib/delphi/src/Thrift.Protocol.JSON.pas @@ -29,6 +29,7 @@ interface SysUtils, Math, Generics.Collections, + Thrift.Configuration, Thrift.Transport, Thrift.Protocol, Thrift.Utils; @@ -52,17 +53,17 @@ TFactory = class( TInterfacedObject, IProtocolFactory) function GetProtocol( const trans: ITransport): IProtocol; end; - private + strict private class function GetTypeNameForTypeID(typeID : TType) : string; class function GetTypeIDForTypeName( const name : string) : TType; - protected + strict protected type // Base class for tracking JSON contexts that may require // inserting/Reading additional JSON syntax characters. // This base context does nothing. TJSONBaseContext = class - protected + strict protected FProto : Pointer; // weak IJSONProtocol; public constructor Create( const aProto : IJSONProtocol); @@ -74,7 +75,7 @@ TJSONBaseContext = class // Context for JSON lists. // Will insert/Read commas before each item except for the first one. TJSONListContext = class( TJSONBaseContext) - private + strict private FFirst : Boolean; public constructor Create( const aProto : IJSONProtocol); @@ -86,7 +87,7 @@ TJSONListContext = class( TJSONBaseContext) // pair, and commas before each key except the first. In addition, will indicate that numbers // in the key position need to be escaped in quotes (since JSON keys must be strings). TJSONPairContext = class( TJSONBaseContext) - private + strict private FFirst, FColon : Boolean; public constructor Create( const aProto : IJSONProtocol); @@ -97,11 +98,13 @@ TJSONPairContext = class( TJSONBaseContext) // Holds up to one byte from the transport TLookaheadReader = class - protected + strict protected FProto : Pointer; // weak IJSONProtocol; + + protected constructor Create( const aProto : IJSONProtocol); - private + strict private FHasData : Boolean; FData : Byte; @@ -115,7 +118,7 @@ TLookaheadReader = class function Peek : Byte; end; - protected + strict protected // Stack of nested contexts that we may be in FContextStack : TStack; @@ -130,17 +133,21 @@ TLookaheadReader = class procedure PushContext( const aCtx : TJSONBaseContext); procedure PopContext; + strict protected + function GetMinSerializedSize( const aType : TType) : Integer; override; + procedure Reset; override; + public // TJSONProtocolImpl Constructor constructor Create( const aTrans : ITransport); destructor Destroy; override; - protected + strict protected // IJSONProtocol // Read a byte that must match b; otherwise an exception is thrown. procedure ReadJSONSyntaxChar( b : Byte); - private + strict private // Convert a byte containing a hex char ('0'-'9' or 'a'-'f') into its corresponding hex value class function HexVal( ch : Byte) : Byte; @@ -213,7 +220,7 @@ TLookaheadReader = class function ReadBinary: TBytes; override; - private + strict private // Reading methods. // Read in a JSON string, unescaping as appropriate. @@ -292,7 +299,7 @@ implementation function TJSONProtocolImpl.TFactory.GetProtocol( const trans: ITransport): IProtocol; begin - result := TJSONProtocolImpl.Create(trans); + result := TJSONProtocolImpl.Create( trans); end; class function TJSONProtocolImpl.GetTypeNameForTypeID(typeID : TType) : string; @@ -478,6 +485,13 @@ destructor TJSONProtocolImpl.Destroy; end; +procedure TJSONProtocolImpl.Reset; +begin + inherited Reset; + ResetContextStack; +end; + + procedure TJSONProtocolImpl.ResetContextStack; begin while FContextStack.Count > 0 @@ -681,6 +695,7 @@ procedure TJSONProtocolImpl.WriteJSONArrayEnd; procedure TJSONProtocolImpl.WriteMessageBegin( const aMsg : TThriftMessage); begin + Reset; ResetContextStack; // THRIFT-1473 WriteJSONArrayStart; @@ -1051,6 +1066,7 @@ procedure TJSONProtocolImpl.ReadJSONArrayEnd; function TJSONProtocolImpl.ReadMessageBegin: TThriftMessage; begin + Reset; ResetContextStack; // THRIFT-1473 Init( result); @@ -1121,6 +1137,8 @@ function TJSONProtocolImpl.ReadMapBegin : TThriftMap; result.ValueType := GetTypeIDForTypeName( str); result.Count := ReadJSONInteger; + CheckReadBytesAvailable(result); + ReadJSONObjectStart; end; @@ -1141,6 +1159,7 @@ function TJSONProtocolImpl.ReadListBegin : TThriftList; str := SysUtils.TEncoding.UTF8.GetString( ReadJSONString(FALSE)); result.ElementType := GetTypeIDForTypeName( str); result.Count := ReadJSONInteger; + CheckReadBytesAvailable(result); end; @@ -1159,6 +1178,7 @@ function TJSONProtocolImpl.ReadSetBegin : TThriftSet; str := SysUtils.TEncoding.UTF8.GetString( ReadJSONString(FALSE)); result.ElementType := GetTypeIDForTypeName( str); result.Count := ReadJSONInteger; + CheckReadBytesAvailable(result); end; @@ -1216,6 +1236,30 @@ function TJSONProtocolImpl.ReadBinary : TBytes; end; +function TJSONProtocolImpl.GetMinSerializedSize( const aType : TType) : Integer; +// Return the minimum number of bytes a type will consume on the wire +begin + case aType of + TType.Stop: result := 0; + TType.Void: result := 0; + TType.Bool_: result := 1; + TType.Byte_: result := 1; + TType.Double_: result := 1; + TType.I16: result := 1; + TType.I32: result := 1; + TType.I64: result := 1; + TType.String_: result := 2; // empty string + TType.Struct: result := 2; // empty struct + TType.Map: result := 2; // empty map + TType.Set_: result := 2; // empty set + TType.List: result := 2; // empty list + else + raise TTransportExceptionBadArgs.Create('Unhandled type code'); + end; +end; + + + //--- init code --- procedure InitBytes( var b : TBytes; aData : array of Byte); diff --git a/lib/delphi/src/Thrift.Protocol.Multiplex.pas b/lib/delphi/src/Thrift.Protocol.Multiplex.pas index 93a38380d11..e5e0cd9fe35 100644 --- a/lib/delphi/src/Thrift.Protocol.Multiplex.pas +++ b/lib/delphi/src/Thrift.Protocol.Multiplex.pas @@ -54,7 +54,7 @@ TMultiplexedProtocol = class( TProtocolDecorator) { Used to delimit the service name from the function name } SEPARATOR = ':'; - private + strict private FServiceName : String; public diff --git a/lib/delphi/src/Thrift.Protocol.pas b/lib/delphi/src/Thrift.Protocol.pas index 36509ca1882..d5a758797eb 100644 --- a/lib/delphi/src/Thrift.Protocol.pas +++ b/lib/delphi/src/Thrift.Protocol.pas @@ -29,7 +29,9 @@ interface Contnrs, Thrift.Exception, Thrift.Stream, + Thrift.Utils, Thrift.Collections, + Thrift.Configuration, Thrift.Transport; type @@ -66,9 +68,6 @@ interface VALID_MESSAGETYPES = [Low(TMessageType)..High(TMessageType)]; -const - DEFAULT_RECURSION_LIMIT = 64; - type IProtocol = interface; @@ -105,36 +104,32 @@ TThriftSet = record end; - IProtocolFactory = interface ['{7CD64A10-4E9F-4E99-93BF-708A31F4A67B}'] function GetProtocol( const trans: ITransport): IProtocol; end; - TThriftStringBuilder = class( TStringBuilder) - public - function Append(const Value: TBytes): TStringBuilder; overload; - function Append(const Value: IThriftContainer): TStringBuilder; overload; - end; - - TProtocolException = class( TException) + TProtocolException = class abstract( TException) public - const // TODO(jensg): change into enum - UNKNOWN = 0; - INVALID_DATA = 1; - NEGATIVE_SIZE = 2; - SIZE_LIMIT = 3; - BAD_VERSION = 4; - NOT_IMPLEMENTED = 5; - DEPTH_LIMIT = 6; - protected + type TExceptionType = ( + UNKNOWN = 0, + INVALID_DATA = 1, + NEGATIVE_SIZE = 2, + SIZE_LIMIT = 3, + BAD_VERSION = 4, + NOT_IMPLEMENTED = 5, + DEPTH_LIMIT = 6 + ); + strict protected constructor HiddenCreate(const Msg: string); + class function GetType: TExceptionType; virtual; abstract; public // purposefully hide inherited constructor class function Create(const Msg: string): TProtocolException; overload; deprecated 'Use specialized TProtocolException types (or regenerate from IDL)'; class function Create: TProtocolException; overload; deprecated 'Use specialized TProtocolException types (or regenerate from IDL)'; - class function Create( type_: Integer): TProtocolException; overload; deprecated 'Use specialized TProtocolException types (or regenerate from IDL)'; - class function Create( type_: Integer; const msg: string): TProtocolException; overload; deprecated 'Use specialized TProtocolException types (or regenerate from IDL)'; + class function Create( aType: TExceptionType): TProtocolException; overload; deprecated 'Use specialized TProtocolException types (or regenerate from IDL)'; + class function Create( aType: TExceptionType; const msg: string): TProtocolException; overload; deprecated 'Use specialized TProtocolException types (or regenerate from IDL)'; + property Type_: TExceptionType read GetType; end; // Needed to remove deprecation warning @@ -143,13 +138,41 @@ TProtocolExceptionSpecialized = class abstract (TProtocolException) constructor Create(const Msg: string); end; - TProtocolExceptionUnknown = class (TProtocolExceptionSpecialized); - TProtocolExceptionInvalidData = class (TProtocolExceptionSpecialized); - TProtocolExceptionNegativeSize = class (TProtocolExceptionSpecialized); - TProtocolExceptionSizeLimit = class (TProtocolExceptionSpecialized); - TProtocolExceptionBadVersion = class (TProtocolExceptionSpecialized); - TProtocolExceptionNotImplemented = class (TProtocolExceptionSpecialized); - TProtocolExceptionDepthLimit = class (TProtocolExceptionSpecialized); + TProtocolExceptionUnknown = class (TProtocolExceptionSpecialized) + strict protected + class function GetType: TProtocolException.TExceptionType; override; + end; + + TProtocolExceptionInvalidData = class (TProtocolExceptionSpecialized) + strict protected + class function GetType: TProtocolException.TExceptionType; override; + end; + + TProtocolExceptionNegativeSize = class (TProtocolExceptionSpecialized) + strict protected + class function GetType: TProtocolException.TExceptionType; override; + end; + + TProtocolExceptionSizeLimit = class (TProtocolExceptionSpecialized) + strict protected + class function GetType: TProtocolException.TExceptionType; override; + end; + + TProtocolExceptionBadVersion = class (TProtocolExceptionSpecialized) + strict protected + class function GetType: TProtocolException.TExceptionType; override; + end; + + TProtocolExceptionNotImplemented = class (TProtocolExceptionSpecialized) + strict protected + class function GetType: TProtocolException.TExceptionType; override; + end; + + TProtocolExceptionDepthLimit = class (TProtocolExceptionSpecialized) + strict protected + class function GetType: TProtocolException.TExceptionType; override; + end; + TProtocolUtil = class @@ -163,7 +186,7 @@ TProtocolUtil = class end; TProtocolRecursionTrackerImpl = class abstract( TInterfacedObject, IProtocolRecursionTracker) - protected + strict protected FProtocol : IProtocol; public constructor Create( prot : IProtocol); @@ -171,7 +194,7 @@ TProtocolRecursionTrackerImpl = class abstract( TInterfacedObject, IProtocolRe end; IProtocol = interface - ['{602A7FFB-0D9E-4CD8-8D7F-E5076660588A}'] + ['{F0040D99-937F-400D-9932-AF04F665899F}'] function GetTransport: ITransport; procedure WriteMessageBegin( const msg: TThriftMessage); procedure WriteMessageEnd; @@ -218,30 +241,34 @@ TProtocolRecursionTrackerImpl = class abstract( TInterfacedObject, IProtocolRe function ReadString: string; function ReadAnsiString: AnsiString; - procedure SetRecursionLimit( value : Integer); - function GetRecursionLimit : Integer; function NextRecursionLevel : IProtocolRecursionTracker; procedure IncrementRecursionDepth; procedure DecrementRecursionDepth; + function GetMinSerializedSize( const aType : TType) : Integer; property Transport: ITransport read GetTransport; - property RecursionLimit : Integer read GetRecursionLimit write SetRecursionLimit; + function Configuration : IThriftConfiguration; end; TProtocolImpl = class abstract( TInterfacedObject, IProtocol) - protected + strict protected FTrans : ITransport; FRecursionLimit : Integer; FRecursionDepth : Integer; - procedure SetRecursionLimit( value : Integer); - function GetRecursionLimit : Integer; function NextRecursionLevel : IProtocolRecursionTracker; procedure IncrementRecursionDepth; procedure DecrementRecursionDepth; - function GetTransport: ITransport; - public + function GetMinSerializedSize( const aType : TType) : Integer; virtual; abstract; + procedure CheckReadBytesAvailable( const value : TThriftList); overload; inline; + procedure CheckReadBytesAvailable( const value : TThriftSet); overload; inline; + procedure CheckReadBytesAvailable( const value : TThriftMap); overload; inline; + + procedure Reset; virtual; + function GetTransport: ITransport; + function Configuration : IThriftConfiguration; + procedure WriteMessageBegin( const msg: TThriftMessage); virtual; abstract; procedure WriteMessageEnd; virtual; abstract; procedure WriteStructBegin( const struc: TThriftStruct); virtual; abstract; @@ -287,47 +314,45 @@ TProtocolImpl = class abstract( TInterfacedObject, IProtocol) function ReadString: string; virtual; function ReadAnsiString: AnsiString; virtual; - property Transport: ITransport read GetTransport; + property Transport: ITransport read GetTransport; - constructor Create( trans: ITransport ); + public + constructor Create( const aTransport : ITransport); end; - IBase = interface - ['{08D9BAA8-5EAA-410F-B50B-AC2E6E5E4155}'] - function ToString: string; + IBase = interface( ISupportsToString) + ['{AFF6CECA-5200-4540-950E-9B89E0C1C00C}'] procedure Read( const iprot: IProtocol); procedure Write( const iprot: IProtocol); end; TBinaryProtocolImpl = class( TProtocolImpl ) - protected + strict protected const VERSION_MASK : Cardinal = $ffff0000; VERSION_1 : Cardinal = $80010000; - protected + strict protected FStrictRead : Boolean; FStrictWrite : Boolean; + function GetMinSerializedSize( const aType : TType) : Integer; override; - private + strict private function ReadAll( const pBuf : Pointer; const buflen : Integer; off: Integer; len: Integer ): Integer; inline; function ReadStringBody( size: Integer): string; public - type TFactory = class( TInterfacedObject, IProtocolFactory) - protected + strict protected FStrictRead : Boolean; FStrictWrite : Boolean; - public function GetProtocol( const trans: ITransport): IProtocol; - constructor Create( AStrictRead, AStrictWrite: Boolean ); overload; - constructor Create; overload; + public + constructor Create( const aStrictRead : Boolean = FALSE; const aStrictWrite: Boolean = TRUE); reintroduce; end; - constructor Create( const trans: ITransport); overload; - constructor Create( const trans: ITransport; strictRead: Boolean; strictWrite: Boolean); overload; + constructor Create( const trans: ITransport; strictRead: Boolean = FALSE; strictWrite: Boolean = TRUE); reintroduce; procedure WriteMessageBegin( const msg: TThriftMessage); override; procedure WriteMessageEnd; override; @@ -380,9 +405,12 @@ TFactory = class( TInterfacedObject, IProtocolFactory) See p.175 of Design Patterns (by Gamma et al.) } TProtocolDecorator = class( TProtocolImpl) - private + strict private FWrappedProtocol : IProtocol; + strict protected + function GetMinSerializedSize( const aType : TType) : Integer; override; + public // Encloses the specified protocol. // All operations will be forward to the given protocol. Must be non-null. @@ -482,13 +510,13 @@ procedure Init( var rec : TThriftList; const AElementType: TType = Low(TType) implementation -function ConvertInt64ToDouble( const n: Int64): Double; +function ConvertInt64ToDouble( const n: Int64): Double; inline; begin ASSERT( SizeOf(n) = SizeOf(Result)); System.Move( n, Result, SizeOf(Result)); end; -function ConvertDoubleToInt64( const d: Double): Int64; +function ConvertDoubleToInt64( const d: Double): Int64; inline; begin ASSERT( SizeOf(d) = SizeOf(Result)); System.Move( d, Result, SizeOf(Result)); @@ -522,24 +550,14 @@ destructor TProtocolRecursionTrackerImpl.Destroy; { TProtocolImpl } -constructor TProtocolImpl.Create(trans: ITransport); +constructor TProtocolImpl.Create( const aTransport : ITransport); begin inherited Create; - FTrans := trans; - FRecursionLimit := DEFAULT_RECURSION_LIMIT; + FTrans := aTransport; + FRecursionLimit := aTransport.Configuration.RecursionLimit; FRecursionDepth := 0; end; -procedure TProtocolImpl.SetRecursionLimit( value : Integer); -begin - FRecursionLimit := value; -end; - -function TProtocolImpl.GetRecursionLimit : Integer; -begin - result := FRecursionLimit; -end; - function TProtocolImpl.NextRecursionLevel : IProtocolRecursionTracker; begin result := TProtocolRecursionTrackerImpl.Create(Self); @@ -562,6 +580,16 @@ function TProtocolImpl.GetTransport: ITransport; Result := FTrans; end; +function TProtocolImpl.Configuration : IThriftConfiguration; +begin + Result := FTrans.Configuration; +end; + +procedure TProtocolImpl.Reset; +begin + FTrans.ResetConsumedMessageSize; +end; + function TProtocolImpl.ReadAnsiString: AnsiString; var b : TBytes; @@ -570,8 +598,7 @@ function TProtocolImpl.ReadAnsiString: AnsiString; Result := ''; b := ReadBinary; len := Length( b ); - if len > 0 then - begin + if len > 0 then begin SetLength( Result, len); System.Move( b[0], Pointer(Result)^, len ); end; @@ -589,8 +616,7 @@ procedure TProtocolImpl.WriteAnsiString(const s: AnsiString); begin len := Length(s); SetLength( b, len); - if len > 0 then - begin + if len > 0 then begin System.Move( Pointer(s)^, b[0], len ); end; WriteBinary( b ); @@ -604,6 +630,26 @@ procedure TProtocolImpl.WriteString(const s: string); WriteBinary( b ); end; + +procedure TProtocolImpl.CheckReadBytesAvailable( const value : TThriftList); +begin + FTrans.CheckReadBytesAvailable( value.Count * GetMinSerializedSize(value.ElementType)); +end; + + +procedure TProtocolImpl.CheckReadBytesAvailable( const value : TThriftSet); +begin + FTrans.CheckReadBytesAvailable( value.Count * GetMinSerializedSize(value.ElementType)); +end; + + +procedure TProtocolImpl.CheckReadBytesAvailable( const value : TThriftMap); +var nPairSize : Integer; +begin + nPairSize := GetMinSerializedSize(value.KeyType) + GetMinSerializedSize(value.ValueType); + FTrans.CheckReadBytesAvailable( value.Count * nPairSize); +end; + { TProtocolUtil } class procedure TProtocolUtil.Skip( prot: IProtocol; type_: TType); @@ -668,16 +714,9 @@ class procedure TProtocolUtil.Skip( prot: IProtocol; type_: TType); { TBinaryProtocolImpl } -constructor TBinaryProtocolImpl.Create( const trans: ITransport); -begin - //no inherited - Create( trans, False, True); -end; - -constructor TBinaryProtocolImpl.Create( const trans: ITransport; strictRead, - strictWrite: Boolean); +constructor TBinaryProtocolImpl.Create( const trans: ITransport; strictRead, strictWrite: Boolean); begin - inherited Create( trans ); + inherited Create( trans); FStrictRead := strictRead; FStrictWrite := strictWrite; end; @@ -693,7 +732,8 @@ function TBinaryProtocolImpl.ReadBinary: TBytes; buf : TBytes; begin size := ReadI32; - SetLength( buf, size ); + FTrans.CheckReadBytesAvailable( size); + SetLength( buf, size); FTrans.ReadAll( buf, 0, size); Result := buf; end; @@ -765,6 +805,7 @@ function TBinaryProtocolImpl.ReadListBegin: TThriftList; begin result.ElementType := TType(ReadByte); result.Count := ReadI32; + CheckReadBytesAvailable(result); end; procedure TBinaryProtocolImpl.ReadListEnd; @@ -777,6 +818,7 @@ function TBinaryProtocolImpl.ReadMapBegin: TThriftMap; result.KeyType := TType(ReadByte); result.ValueType := TType(ReadByte); result.Count := ReadI32; + CheckReadBytesAvailable(result); end; procedure TBinaryProtocolImpl.ReadMapEnd; @@ -789,6 +831,7 @@ function TBinaryProtocolImpl.ReadMessageBegin: TThriftMessage; size : Integer; version : Integer; begin + Reset; Init( result); size := ReadI32; if (size < 0) then begin @@ -820,6 +863,7 @@ function TBinaryProtocolImpl.ReadSetBegin: TThriftSet; begin result.ElementType := TType(ReadByte); result.Count := ReadI32; + CheckReadBytesAvailable(result); end; procedure TBinaryProtocolImpl.ReadSetEnd; @@ -828,10 +872,10 @@ procedure TBinaryProtocolImpl.ReadSetEnd; end; function TBinaryProtocolImpl.ReadStringBody( size: Integer): string; -var - buf : TBytes; +var buf : TBytes; begin - SetLength( buf, size ); + FTrans.CheckReadBytesAvailable( size); + SetLength( buf, size); FTrans.ReadAll( buf, 0, size ); Result := TEncoding.UTF8.GetString( buf); end; @@ -946,17 +990,15 @@ procedure TBinaryProtocolImpl.WriteMapEnd; end; procedure TBinaryProtocolImpl.WriteMessageBegin( const msg: TThriftMessage); -var - version : Cardinal; +var version : Cardinal; begin - if FStrictWrite then - begin + Reset; + if FStrictWrite then begin version := VERSION_1 or Cardinal( msg.Type_); WriteI32( Integer( version) ); WriteString( msg.Name); WriteI32( msg.SeqID); - end else - begin + end else begin WriteString( msg.Name); WriteByte(ShortInt( msg.Type_)); WriteI32( msg.SeqID); @@ -989,6 +1031,29 @@ procedure TBinaryProtocolImpl.WriteStructEnd; end; +function TBinaryProtocolImpl.GetMinSerializedSize( const aType : TType) : Integer; +// Return the minimum number of bytes a type will consume on the wire +begin + case aType of + TType.Stop: result := 0; + TType.Void: result := 0; + TType.Bool_: result := SizeOf(Byte); + TType.Byte_: result := SizeOf(Byte); + TType.Double_: result := SizeOf(Double); + TType.I16: result := SizeOf(Int16); + TType.I32: result := SizeOf(Int32); + TType.I64: result := SizeOf(Int64); + TType.String_: result := SizeOf(Int32); // string length + TType.Struct: result := 0; // empty struct + TType.Map: result := SizeOf(Int32); // element count + TType.Set_: result := SizeOf(Int32); // element count + TType.List: result := SizeOf(Int32); // element count + else + raise TTransportExceptionBadArgs.Create('Unhandled type code'); + end; +end; + + { TProtocolException } constructor TProtocolException.HiddenCreate(const Msg: string); @@ -1006,23 +1071,24 @@ class function TProtocolException.Create: TProtocolException; Result := TProtocolExceptionUnknown.Create(''); end; -class function TProtocolException.Create(type_: Integer): TProtocolException; +class function TProtocolException.Create(aType: TExceptionType): TProtocolException; begin {$WARN SYMBOL_DEPRECATED OFF} - Result := Create(type_, ''); + Result := Create(aType, ''); {$WARN SYMBOL_DEPRECATED DEFAULT} end; -class function TProtocolException.Create(type_: Integer; const msg: string): TProtocolException; +class function TProtocolException.Create(aType: TExceptionType; const msg: string): TProtocolException; begin - case type_ of - INVALID_DATA: Result := TProtocolExceptionInvalidData.Create(msg); - NEGATIVE_SIZE: Result := TProtocolExceptionNegativeSize.Create(msg); - SIZE_LIMIT: Result := TProtocolExceptionSizeLimit.Create(msg); - BAD_VERSION: Result := TProtocolExceptionBadVersion.Create(msg); - NOT_IMPLEMENTED: Result := TProtocolExceptionNotImplemented.Create(msg); - DEPTH_LIMIT: Result := TProtocolExceptionDepthLimit.Create(msg); + case aType of + TExceptionType.INVALID_DATA: Result := TProtocolExceptionInvalidData.Create(msg); + TExceptionType.NEGATIVE_SIZE: Result := TProtocolExceptionNegativeSize.Create(msg); + TExceptionType.SIZE_LIMIT: Result := TProtocolExceptionSizeLimit.Create(msg); + TExceptionType.BAD_VERSION: Result := TProtocolExceptionBadVersion.Create(msg); + TExceptionType.NOT_IMPLEMENTED: Result := TProtocolExceptionNotImplemented.Create(msg); + TExceptionType.DEPTH_LIMIT: Result := TProtocolExceptionDepthLimit.Create(msg); else + ASSERT( TExceptionType.UNKNOWN = aType); Result := TProtocolExceptionUnknown.Create(msg); end; end; @@ -1034,34 +1100,52 @@ constructor TProtocolExceptionSpecialized.Create(const Msg: string); inherited HiddenCreate(Msg); end; -{ TThriftStringBuilder } +{ specialized TProtocolExceptions } + +class function TProtocolExceptionUnknown.GetType: TProtocolException.TExceptionType; +begin + result := TExceptionType.UNKNOWN; +end; + +class function TProtocolExceptionInvalidData.GetType: TProtocolException.TExceptionType; +begin + result := TExceptionType.INVALID_DATA; +end; + +class function TProtocolExceptionNegativeSize.GetType: TProtocolException.TExceptionType; +begin + result := TExceptionType.NEGATIVE_SIZE; +end; + +class function TProtocolExceptionSizeLimit.GetType: TProtocolException.TExceptionType; +begin + result := TExceptionType.SIZE_LIMIT; +end; -function TThriftStringBuilder.Append(const Value: TBytes): TStringBuilder; +class function TProtocolExceptionBadVersion.GetType: TProtocolException.TExceptionType; begin - Result := Append( string( RawByteString(Value)) ); + result := TExceptionType.BAD_VERSION; end; -function TThriftStringBuilder.Append( - const Value: IThriftContainer): TStringBuilder; +class function TProtocolExceptionNotImplemented.GetType: TProtocolException.TExceptionType; begin - Result := Append( Value.ToString ); + result := TExceptionType.NOT_IMPLEMENTED; +end; + +class function TProtocolExceptionDepthLimit.GetType: TProtocolException.TExceptionType; +begin + result := TExceptionType.DEPTH_LIMIT; end; { TBinaryProtocolImpl.TFactory } -constructor TBinaryProtocolImpl.TFactory.Create(AStrictRead, AStrictWrite: Boolean); +constructor TBinaryProtocolImpl.TFactory.Create( const aStrictRead, aStrictWrite: Boolean); begin inherited Create; FStrictRead := AStrictRead; FStrictWrite := AStrictWrite; end; -constructor TBinaryProtocolImpl.TFactory.Create; -begin - //no inherited; - Create( False, True ) -end; - function TBinaryProtocolImpl.TFactory.GetProtocol( const trans: ITransport): IProtocol; begin Result := TBinaryProtocolImpl.Create( trans, FStrictRead, FStrictWrite); @@ -1336,6 +1420,12 @@ function TProtocolDecorator.ReadAnsiString: AnsiString; end; +function TProtocolDecorator.GetMinSerializedSize( const aType : TType) : Integer; +begin + result := FWrappedProtocol.GetMinSerializedSize(aType); +end; + + { Init helper functions } procedure Init( var rec : TThriftMessage; const AName: string; const AMessageType: TMessageType; const ASeqID: Integer); @@ -1383,7 +1473,5 @@ procedure Init( var rec : TThriftList; const AElementType: TType; const ACount: - - end. diff --git a/lib/delphi/src/Thrift.Serializer.pas b/lib/delphi/src/Thrift.Serializer.pas index 5f2905a97ea..cb62603dbf0 100644 --- a/lib/delphi/src/Thrift.Serializer.pas +++ b/lib/delphi/src/Thrift.Serializer.pas @@ -28,6 +28,7 @@ interface {$ELSE} System.Classes, Winapi.Windows, System.SysUtils, {$ENDIF} + Thrift.Configuration, Thrift.Protocol, Thrift.Transport, Thrift.Stream; @@ -36,18 +37,15 @@ interface type // Generic utility for easily serializing objects into a byte array or Stream. TSerializer = class - private + strict private FStream : TMemoryStream; FTransport : ITransport; FProtocol : IProtocol; public - // Create a new TSerializer that uses the TBinaryProtocol by default. - constructor Create; overload; - - // Create a new TSerializer. - // It will use the TProtocol specified by the factory that is passed in. - constructor Create( const factory : IProtocolFactory); overload; + constructor Create( const aProtFact : IProtocolFactory = nil; // defaults to TBinaryProtocol + const aTransFact : ITransportFactory = nil; + const aConfig : IThriftConfiguration = nil); // DTOR destructor Destroy; override; @@ -60,18 +58,15 @@ TSerializer = class // Generic utility for easily deserializing objects from byte array or Stream. TDeserializer = class - private + strict private FStream : TMemoryStream; FTransport : ITransport; FProtocol : IProtocol; public - // Create a new TDeserializer that uses the TBinaryProtocol by default. - constructor Create; overload; - - // Create a new TDeserializer. - // It will use the TProtocol specified by the factory that is passed in. - constructor Create( const factory : IProtocolFactory); overload; + constructor Create( const aProtFact : IProtocolFactory = nil; // defaults to TBinaryProtocol + const aTransFact : ITransportFactory = nil; + const aConfig : IThriftConfiguration = nil); // DTOR destructor Destroy; override; @@ -89,24 +84,27 @@ implementation { TSerializer } -constructor TSerializer.Create(); -// Create a new TSerializer that uses the TBinaryProtocol by default. -begin - //no inherited; - Create( TBinaryProtocolImpl.TFactory.Create); -end; - - -constructor TSerializer.Create( const factory : IProtocolFactory); -// Create a new TSerializer. -// It will use the TProtocol specified by the factory that is passed in. +constructor TSerializer.Create( const aProtFact : IProtocolFactory; + const aTransFact : ITransportFactory; + const aConfig : IThriftConfiguration); var adapter : IThriftStream; + protfact : IProtocolFactory; begin inherited Create; + FStream := TMemoryStream.Create; adapter := TThriftStreamAdapterDelphi.Create( FStream, FALSE); - FTransport := TStreamTransportImpl.Create( nil, adapter); - FProtocol := factory.GetProtocol( FTransport); + + FTransport := TStreamTransportImpl.Create( nil, adapter, aConfig); + if aTransfact <> nil then FTransport := aTransfact.GetTransport( FTransport); + + if aProtFact <> nil + then protfact := aProtFact + else protfact := TBinaryProtocolImpl.TFactory.Create; + FProtocol := protfact.GetProtocol( FTransport); + + if not FTransport.IsOpen + then FTransport.Open; end; @@ -131,6 +129,8 @@ function TSerializer.Serialize( const input : IBase) : TBytes; try FStream.Size := 0; input.Write( FProtocol); + FTransport.Flush; + SetLength( result, FStream.Size); iBytes := Length(result); if iBytes > 0 @@ -150,6 +150,8 @@ procedure TSerializer.Serialize( const input : IBase; const aStm : TStream); try FStream.Size := 0; input.Write( FProtocol); + FTransport.Flush; + aStm.CopyFrom( FStream, COPY_ENTIRE_STREAM); finally FStream.Size := 0; // free any allocated memory @@ -160,24 +162,27 @@ procedure TSerializer.Serialize( const input : IBase; const aStm : TStream); { TDeserializer } -constructor TDeserializer.Create(); -// Create a new TDeserializer that uses the TBinaryProtocol by default. -begin - //no inherited; - Create( TBinaryProtocolImpl.TFactory.Create); -end; - - -constructor TDeserializer.Create( const factory : IProtocolFactory); -// Create a new TDeserializer. -// It will use the TProtocol specified by the factory that is passed in. +constructor TDeserializer.Create( const aProtFact : IProtocolFactory; + const aTransFact : ITransportFactory; + const aConfig : IThriftConfiguration); var adapter : IThriftStream; + protfact : IProtocolFactory; begin inherited Create; + FStream := TMemoryStream.Create; adapter := TThriftStreamAdapterDelphi.Create( FStream, FALSE); - FTransport := TStreamTransportImpl.Create( adapter, nil); - FProtocol := factory.GetProtocol( FTransport); + + FTransport := TStreamTransportImpl.Create( adapter, nil, aConfig); + if aTransfact <> nil then FTransport := aTransfact.GetTransport( FTransport); + + if aProtFact <> nil + then protfact := aProtFact + else protfact := TBinaryProtocolImpl.TFactory.Create; + FProtocol := protfact.GetProtocol( FTransport); + + if not FTransport.IsOpen + then FTransport.Open; end; diff --git a/lib/delphi/src/Thrift.Server.pas b/lib/delphi/src/Thrift.Server.pas index 13c5762cf65..a73e6cb4475 100644 --- a/lib/delphi/src/Thrift.Server.pas +++ b/lib/delphi/src/Thrift.Server.pas @@ -32,7 +32,8 @@ interface {$ENDIF} Thrift, Thrift.Protocol, - Thrift.Transport; + Thrift.Transport, + Thrift.Configuration; type IServerEvents = interface @@ -61,7 +62,7 @@ TServerImpl = class abstract( TInterfacedObject, IServer ) public type TLogDelegate = reference to procedure( const str: string); - protected + strict protected FProcessor : IProcessor; FServerTransport : IServerTransport; FInputTransportFactory : ITransportFactory; @@ -70,6 +71,7 @@ TServerImpl = class abstract( TInterfacedObject, IServer ) FOutputProtocolFactory : IProtocolFactory; FLogDelegate : TLogDelegate; FServerEvents : IServerEvents; + FConfiguration : IThriftConfiguration; class procedure DefaultLogDelegate( const str: string); @@ -80,52 +82,31 @@ TServerImpl = class abstract( TInterfacedObject, IServer ) procedure Stop; virtual; abstract; public constructor Create( - const AProcessor :IProcessor; - const AServerTransport: IServerTransport; - const AInputTransportFactory : ITransportFactory; - const AOutputTransportFactory : ITransportFactory; - const AInputProtocolFactory : IProtocolFactory; - const AOutputProtocolFactory : IProtocolFactory; - const ALogDelegate : TLogDelegate + const aProcessor :IProcessor; + const aServerTransport: IServerTransport; + const aInputTransportFactory : ITransportFactory; + const aOutputTransportFactory : ITransportFactory; + const aInputProtocolFactory : IProtocolFactory; + const aOutputProtocolFactory : IProtocolFactory; + const aConfig : IThriftConfiguration; + const aLogDelegate : TLogDelegate ); overload; constructor Create( - const AProcessor :IProcessor; - const AServerTransport: IServerTransport - ); overload; - - constructor Create( - const AProcessor :IProcessor; - const AServerTransport: IServerTransport; - const ALogDelegate: TLogDelegate - ); overload; - - constructor Create( - const AProcessor :IProcessor; - const AServerTransport: IServerTransport; - const ATransportFactory : ITransportFactory - ); overload; - - constructor Create( - const AProcessor :IProcessor; - const AServerTransport: IServerTransport; - const ATransportFactory : ITransportFactory; - const AProtocolFactory : IProtocolFactory + const aProcessor: IProcessor; + const aServerTransport: IServerTransport; + const aTransportFactory: ITransportFactory = nil; + const aProtocolFactory: IProtocolFactory = nil; + const aConfig : IThriftConfiguration = nil; + const aLogDel: TServerImpl.TLogDelegate = nil ); overload; end; + TSimpleServer = class( TServerImpl) private FStop : Boolean; public - constructor Create( const AProcessor: IProcessor; const AServerTransport: IServerTransport); overload; - constructor Create( const AProcessor: IProcessor; const AServerTransport: IServerTransport; - ALogDel: TServerImpl.TLogDelegate); overload; - constructor Create( const AProcessor: IProcessor; const AServerTransport: IServerTransport; - const ATransportFactory: ITransportFactory); overload; - constructor Create( const AProcessor: IProcessor; const AServerTransport: IServerTransport; - const ATransportFactory: ITransportFactory; const AProtocolFactory: IProtocolFactory); overload; - procedure Serve; override; procedure Stop; override; end; @@ -135,84 +116,57 @@ implementation { TServerImpl } -constructor TServerImpl.Create( const AProcessor: IProcessor; - const AServerTransport: IServerTransport; const ALogDelegate: TLogDelegate); -var - InputFactory, OutputFactory : IProtocolFactory; - InputTransFactory, OutputTransFactory : ITransportFactory; - +constructor TServerImpl.Create( const aProcessor: IProcessor; + const aServerTransport: IServerTransport; + const aInputTransportFactory, aOutputTransportFactory: ITransportFactory; + const aInputProtocolFactory, aOutputProtocolFactory: IProtocolFactory; + const aConfig : IThriftConfiguration; + const aLogDelegate : TLogDelegate); begin - InputFactory := TBinaryProtocolImpl.TFactory.Create; - OutputFactory := TBinaryProtocolImpl.TFactory.Create; - InputTransFactory := TTransportFactoryImpl.Create; - OutputTransFactory := TTransportFactoryImpl.Create; - - //no inherited; - Create( - AProcessor, - AServerTransport, - InputTransFactory, - OutputTransFactory, - InputFactory, - OutputFactory, - ALogDelegate - ); -end; + inherited Create; + FProcessor := aProcessor; + FServerTransport := aServerTransport; -constructor TServerImpl.Create(const AProcessor: IProcessor; - const AServerTransport: IServerTransport); -var - InputFactory, OutputFactory : IProtocolFactory; - InputTransFactory, OutputTransFactory : ITransportFactory; + if aConfig <> nil + then FConfiguration := aConfig + else FConfiguration := TThriftConfigurationImpl.Create; -begin - InputFactory := TBinaryProtocolImpl.TFactory.Create; - OutputFactory := TBinaryProtocolImpl.TFactory.Create; - InputTransFactory := TTransportFactoryImpl.Create; - OutputTransFactory := TTransportFactoryImpl.Create; - - //no inherited; - Create( - AProcessor, - AServerTransport, - InputTransFactory, - OutputTransFactory, - InputFactory, - OutputFactory, - DefaultLogDelegate - ); -end; + if aInputTransportFactory <> nil + then FInputTransportFactory := aInputTransportFactory + else FInputTransportFactory := TTransportFactoryImpl.Create; -constructor TServerImpl.Create(const AProcessor: IProcessor; - const AServerTransport: IServerTransport; const ATransportFactory: ITransportFactory); -var - InputProtocolFactory : IProtocolFactory; - OutputProtocolFactory : IProtocolFactory; -begin - InputProtocolFactory := TBinaryProtocolImpl.TFactory.Create; - OutputProtocolFactory := TBinaryProtocolImpl.TFactory.Create; + if aOutputTransportFactory <> nil + then FOutputTransportFactory := aOutputTransportFactory + else FOutputTransportFactory := TTransportFactoryImpl.Create; + + if aInputProtocolFactory <> nil + then FInputProtocolFactory := aInputProtocolFactory + else FInputProtocolFactory := TBinaryProtocolImpl.TFactory.Create; - //no inherited; - Create( AProcessor, AServerTransport, ATransportFactory, ATransportFactory, - InputProtocolFactory, OutputProtocolFactory, DefaultLogDelegate); + if aOutputProtocolFactory <> nil + then FOutputProtocolFactory := aOutputProtocolFactory + else FOutputProtocolFactory := TBinaryProtocolImpl.TFactory.Create; + + if Assigned(aLogDelegate) + then FLogDelegate := aLogDelegate + else FLogDelegate := DefaultLogDelegate; end; -constructor TServerImpl.Create(const AProcessor: IProcessor; - const AServerTransport: IServerTransport; - const AInputTransportFactory, AOutputTransportFactory: ITransportFactory; - const AInputProtocolFactory, AOutputProtocolFactory: IProtocolFactory; - const ALogDelegate : TLogDelegate); + +constructor TServerImpl.Create( const aProcessor: IProcessor; + const aServerTransport: IServerTransport; + const aTransportFactory: ITransportFactory; + const aProtocolFactory: IProtocolFactory; + const aConfig : IThriftConfiguration; + const aLogDel: TServerImpl.TLogDelegate); begin - inherited Create; - FProcessor := AProcessor; - FServerTransport := AServerTransport; - FInputTransportFactory := AInputTransportFactory; - FOutputTransportFactory := AOutputTransportFactory; - FInputProtocolFactory := AInputProtocolFactory; - FOutputProtocolFactory := AOutputProtocolFactory; - FLogDelegate := ALogDelegate; + Create( aProcessor, aServerTransport, + aTransportFactory, aTransportFactory, + aProtocolFactory, aProtocolFactory, + aConfig, aLogDel); end; + class procedure TServerImpl.DefaultLogDelegate( const str: string); begin try @@ -223,16 +177,6 @@ class procedure TServerImpl.DefaultLogDelegate( const str: string); end; end; -constructor TServerImpl.Create( const AProcessor: IProcessor; - const AServerTransport: IServerTransport; const ATransportFactory: ITransportFactory; - const AProtocolFactory: IProtocolFactory); -begin - //no inherited; - Create( AProcessor, AServerTransport, - ATransportFactory, ATransportFactory, - AProtocolFactory, AProtocolFactory, - DefaultLogDelegate); -end; function TServerImpl.GetServerEvents : IServerEvents; @@ -250,55 +194,6 @@ procedure TServerImpl.SetServerEvents( const value : IServerEvents); { TSimpleServer } -constructor TSimpleServer.Create( const AProcessor: IProcessor; - const AServerTransport: IServerTransport); -var - InputProtocolFactory : IProtocolFactory; - OutputProtocolFactory : IProtocolFactory; - InputTransportFactory : ITransportFactory; - OutputTransportFactory : ITransportFactory; -begin - InputProtocolFactory := TBinaryProtocolImpl.TFactory.Create; - OutputProtocolFactory := TBinaryProtocolImpl.TFactory.Create; - InputTransportFactory := TTransportFactoryImpl.Create; - OutputTransportFactory := TTransportFactoryImpl.Create; - - inherited Create( AProcessor, AServerTransport, InputTransportFactory, - OutputTransportFactory, InputProtocolFactory, OutputProtocolFactory, DefaultLogDelegate); -end; - -constructor TSimpleServer.Create( const AProcessor: IProcessor; - const AServerTransport: IServerTransport; ALogDel: TServerImpl.TLogDelegate); -var - InputProtocolFactory : IProtocolFactory; - OutputProtocolFactory : IProtocolFactory; - InputTransportFactory : ITransportFactory; - OutputTransportFactory : ITransportFactory; -begin - InputProtocolFactory := TBinaryProtocolImpl.TFactory.Create; - OutputProtocolFactory := TBinaryProtocolImpl.TFactory.Create; - InputTransportFactory := TTransportFactoryImpl.Create; - OutputTransportFactory := TTransportFactoryImpl.Create; - - inherited Create( AProcessor, AServerTransport, InputTransportFactory, - OutputTransportFactory, InputProtocolFactory, OutputProtocolFactory, ALogDel); -end; - -constructor TSimpleServer.Create( const AProcessor: IProcessor; - const AServerTransport: IServerTransport; const ATransportFactory: ITransportFactory); -begin - inherited Create( AProcessor, AServerTransport, ATransportFactory, - ATransportFactory, TBinaryProtocolImpl.TFactory.Create, TBinaryProtocolImpl.TFactory.Create, DefaultLogDelegate); -end; - -constructor TSimpleServer.Create( const AProcessor: IProcessor; - const AServerTransport: IServerTransport; const ATransportFactory: ITransportFactory; - const AProtocolFactory: IProtocolFactory); -begin - inherited Create( AProcessor, AServerTransport, ATransportFactory, - ATransportFactory, AProtocolFactory, AProtocolFactory, DefaultLogDelegate); -end; - procedure TSimpleServer.Serve; var client : ITransport; @@ -372,20 +267,17 @@ procedure TSimpleServer.Serve; end; except - on E: TTransportException do - begin + on E: TTransportException do begin if FStop then FLogDelegate('TSimpleServer was shutting down, caught ' + E.ToString) else FLogDelegate( E.ToString); end; - on E: Exception do - begin + on E: Exception do begin FLogDelegate( E.ToString); end; end; - if context <> nil - then begin + if context <> nil then begin context.CleanupContext; context := nil; end; diff --git a/lib/delphi/src/Thrift.Socket.pas b/lib/delphi/src/Thrift.Socket.pas index f0cab79db28..01fdf366fa3 100644 --- a/lib/delphi/src/Thrift.Socket.pas +++ b/lib/delphi/src/Thrift.Socket.pas @@ -81,7 +81,7 @@ TIn6Addr = record TScopeId = record public Value: ULONG; - private + strict private function GetBitField(Loc: Integer): Integer; inline; procedure SetBitField(Loc: Integer; const aValue: Integer); inline; public @@ -125,7 +125,7 @@ function GetNameInfoW(const pSockaddr: TSockAddr; SockaddrLength: Integer; pNode ISmartPointer = reference to function: T; TSmartPointer = class(TInterfacedObject, ISmartPointer) - private + strict private FValue: T; FDestroyer: TSmartPointerDestroyer; public @@ -147,7 +147,7 @@ TBaseSocket = class abstract class constructor Create; class destructor Destroy; class procedure DefaultLogDelegate(const Str: string); - protected type + strict protected type IGetAddrInfoWrapper = interface function Init: Integer; function GetRes: PAddrInfoW; @@ -575,7 +575,7 @@ function TBaseSocket.CreateSocket(AAddress: string; APort: Integer): IGetAddrInf FillChar(Hints, SizeOf(Hints), 0); Hints.ai_family := PF_UNSPEC; Hints.ai_socktype := SOCK_STREAM; - Hints.ai_flags := AI_PASSIVE or AI_ADDRCONFIG; + Hints.ai_flags := AI_PASSIVE; StrFmt(ThePort, '%d', [FPort]); Result := TGetAddrInfoWrapper.Create(AAddress, ThePort, @Hints); diff --git a/lib/delphi/src/Thrift.Stream.pas b/lib/delphi/src/Thrift.Stream.pas index 3308c53a55f..16680591c9f 100644 --- a/lib/delphi/src/Thrift.Stream.pas +++ b/lib/delphi/src/Thrift.Stream.pas @@ -36,9 +36,8 @@ interface Thrift.Utils; type - IThriftStream = interface - ['{2A77D916-7446-46C1-8545-0AEC0008DBCA}'] + ['{3A61A8A6-3639-4B91-A260-EFCA23944F3A}'] procedure Write( const buffer: TBytes; offset: Integer; count: Integer); overload; procedure Write( const pBuf : Pointer; offset: Integer; count: Integer); overload; function Read( var buffer: TBytes; offset: Integer; count: Integer): Integer; overload; @@ -48,12 +47,16 @@ interface procedure Flush; function IsOpen: Boolean; function ToArray: TBytes; + function Size : Int64; + function Position : Int64; end; - TThriftStreamImpl = class( TInterfacedObject, IThriftStream) - private + + TThriftStreamImpl = class abstract( TInterfacedObject, IThriftStream) + strict private procedure CheckSizeAndOffset( const pBuf : Pointer; const buflen : Integer; offset: Integer; count: Integer); overload; - protected + strict protected + // IThriftStream procedure Write( const buffer: TBytes; offset: Integer; count: Integer); overload; inline; procedure Write( const pBuf : Pointer; offset: Integer; count: Integer); overload; virtual; function Read( var buffer: TBytes; offset: Integer; count: Integer): Integer; overload; inline; @@ -63,13 +66,16 @@ TThriftStreamImpl = class( TInterfacedObject, IThriftStream) procedure Flush; virtual; abstract; function IsOpen: Boolean; virtual; abstract; function ToArray: TBytes; virtual; abstract; + function Size : Int64; virtual; + function Position : Int64; virtual; end; - TThriftStreamAdapterDelphi = class( TThriftStreamImpl ) - private + TThriftStreamAdapterDelphi = class( TThriftStreamImpl) + strict private FStream : TStream; FOwnsStream : Boolean; - protected + strict protected + // IThriftStream procedure Write( const pBuf : Pointer; offset: Integer; count: Integer); override; function Read( const pBuf : Pointer; const buflen : Integer; offset: Integer; count: Integer): Integer; override; procedure Open; override; @@ -77,15 +83,18 @@ TThriftStreamAdapterDelphi = class( TThriftStreamImpl ) procedure Flush; override; function IsOpen: Boolean; override; function ToArray: TBytes; override; + function Size : Int64; override; + function Position : Int64; override; public - constructor Create( const AStream: TStream; AOwnsStream : Boolean); + constructor Create( const aStream: TStream; aOwnsStream : Boolean); destructor Destroy; override; end; TThriftStreamAdapterCOM = class( TThriftStreamImpl) - private + strict private FStream : IStream; - protected + strict protected + // IThriftStream procedure Write( const pBuf : Pointer; offset: Integer; count: Integer); override; function Read( const pBuf : Pointer; const buflen : Integer; offset: Integer; count: Integer): Integer; override; procedure Open; override; @@ -93,12 +102,16 @@ TThriftStreamAdapterCOM = class( TThriftStreamImpl) procedure Flush; override; function IsOpen: Boolean; override; function ToArray: TBytes; override; + function Size : Int64; override; + function Position : Int64; override; public - constructor Create( const AStream: IStream); + constructor Create( const aStream: IStream); end; implementation +uses Thrift.Transport; + { TThriftStreamAdapterCOM } procedure TThriftStreamAdapterCOM.Close; @@ -106,10 +119,10 @@ procedure TThriftStreamAdapterCOM.Close; FStream := nil; end; -constructor TThriftStreamAdapterCOM.Create( const AStream: IStream); +constructor TThriftStreamAdapterCOM.Create( const aStream: IStream); begin inherited Create; - FStream := AStream; + FStream := aStream; end; procedure TThriftStreamAdapterCOM.Flush; @@ -121,6 +134,24 @@ procedure TThriftStreamAdapterCOM.Flush; end; end; +function TThriftStreamAdapterCOM.Size : Int64; +var statstg: TStatStg; +begin + FillChar( statstg, SizeOf( statstg), 0); + if IsOpen + and Succeeded( FStream.Stat( statstg, STATFLAG_NONAME )) + then result := statstg.cbSize + else result := 0; +end; + +function TThriftStreamAdapterCOM.Position : Int64; +var newpos : {$IF CompilerVersion >= 29.0} UInt64 {$ELSE} Int64 {$IFEND}; +begin + if SUCCEEDED( FStream.Seek( 0, STREAM_SEEK_CUR, newpos)) + then result := Int64(newpos) + else raise TTransportExceptionEndOfFile.Create('Seek() error'); +end; + function TThriftStreamAdapterCOM.IsOpen: Boolean; begin Result := FStream <> nil; @@ -151,19 +182,11 @@ function TThriftStreamAdapterCOM.Read( const pBuf : Pointer; const buflen : Inte function TThriftStreamAdapterCOM.ToArray: TBytes; var - statstg: TStatStg; - len : Integer; + len : Int64; NewPos : {$IF CompilerVersion >= 29.0} UInt64 {$ELSE} Int64 {$IFEND}; cbRead : Integer; begin - FillChar( statstg, SizeOf( statstg), 0); - len := 0; - if IsOpen then begin - if Succeeded( FStream.Stat( statstg, STATFLAG_NONAME )) then begin - len := statstg.cbSize; - end; - end; - + len := Self.Size; SetLength( Result, len ); if len > 0 then begin @@ -225,35 +248,58 @@ procedure TThriftStreamImpl.Write( const pBuf : Pointer; offset: Integer; count: CheckSizeAndOffset( pBuf, offset+count, offset, count); end; -{ TThriftStreamAdapterDelphi } +function TThriftStreamImpl.Size : Int64; +begin + ASSERT(FALSE); + raise ENotImplemented.Create(ClassName+'.Size'); +end; -procedure TThriftStreamAdapterDelphi.Close; +function TThriftStreamImpl.Position : Int64; begin - FStream.Free; - FStream := nil; - FOwnsStream := False; + ASSERT(FALSE); + raise ENotImplemented.Create(ClassName+'.Position'); end; -constructor TThriftStreamAdapterDelphi.Create( const AStream: TStream; AOwnsStream: Boolean); + +{ TThriftStreamAdapterDelphi } + +constructor TThriftStreamAdapterDelphi.Create( const aStream: TStream; aOwnsStream: Boolean); begin inherited Create; - FStream := AStream; - FOwnsStream := AOwnsStream; + FStream := aStream; + FOwnsStream := aOwnsStream; end; destructor TThriftStreamAdapterDelphi.Destroy; begin - if FOwnsStream + if FOwnsStream then Close; - + inherited; end; +procedure TThriftStreamAdapterDelphi.Close; +begin + FStream.Free; + FStream := nil; + FOwnsStream := False; +end; + procedure TThriftStreamAdapterDelphi.Flush; begin // nothing to do end; +function TThriftStreamAdapterDelphi.Size : Int64; +begin + result := FStream.Size; +end; + +function TThriftStreamAdapterDelphi.Position : Int64; +begin + result := FStream.Position; +end; + function TThriftStreamAdapterDelphi.IsOpen: Boolean; begin Result := FStream <> nil; @@ -285,11 +331,9 @@ function TThriftStreamAdapterDelphi.ToArray: TBytes; OrgPos : Integer; len : Integer; begin - len := 0; - if FStream <> nil then - begin - len := FStream.Size; - end; + if FStream <> nil + then len := FStream.Size + else len := 0; SetLength( Result, len ); diff --git a/lib/delphi/src/Thrift.Transport.MsxmlHTTP.pas b/lib/delphi/src/Thrift.Transport.MsxmlHTTP.pas new file mode 100644 index 00000000000..bdc65d1fdaf --- /dev/null +++ b/lib/delphi/src/Thrift.Transport.MsxmlHTTP.pas @@ -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. + *) +unit Thrift.Transport.MsxmlHTTP; + +{$I Thrift.Defines.inc} +{$SCOPEDENUMS ON} + +interface + +uses + Classes, + SysUtils, + Math, + Generics.Collections, + {$IFDEF OLD_UNIT_NAMES} + ActiveX, msxml, + {$ELSE} + Winapi.ActiveX, Winapi.msxml, + {$ENDIF} + Thrift.Collections, + Thrift.Configuration, + Thrift.Transport, + Thrift.Exception, + Thrift.Utils, + Thrift.Stream; + +type + TMsxmlHTTPClientImpl = class( TEndpointTransportBase, IHTTPClient) + strict private + FUri : string; + FInputStream : IThriftStream; + FOutputStream : IThriftStream; + FDnsResolveTimeout : Integer; + FConnectionTimeout : Integer; + FSendTimeout : Integer; + FReadTimeout : Integer; + FCustomHeaders : IThriftDictionary; + + function CreateRequest: IXMLHTTPRequest; + strict protected + function GetIsOpen: Boolean; override; + procedure Open(); override; + procedure Close(); override; + function Read( const pBuf : Pointer; const buflen : Integer; off: Integer; len: Integer): Integer; override; + procedure Write( const pBuf : Pointer; off, len : Integer); override; + procedure Flush; override; + + procedure SetDnsResolveTimeout(const Value: Integer); + function GetDnsResolveTimeout: Integer; + procedure SetConnectionTimeout(const Value: Integer); + function GetConnectionTimeout: Integer; + procedure SetSendTimeout(const Value: Integer); + function GetSendTimeout: Integer; + procedure SetReadTimeout(const Value: Integer); + function GetReadTimeout: Integer; + function GetSecureProtocols : TSecureProtocols; + procedure SetSecureProtocols( const value : TSecureProtocols); + + function GetCustomHeaders: IThriftDictionary; + procedure SendRequest; + + property DnsResolveTimeout: Integer read GetDnsResolveTimeout write SetDnsResolveTimeout; + property ConnectionTimeout: Integer read GetConnectionTimeout write SetConnectionTimeout; + property SendTimeout: Integer read GetSendTimeout write SetSendTimeout; + property ReadTimeout: Integer read GetReadTimeout write SetReadTimeout; + property CustomHeaders: IThriftDictionary read GetCustomHeaders; + public + constructor Create( const aUri: string; const aConfig : IThriftConfiguration); reintroduce; + destructor Destroy; override; + end; + + +implementation + +const + XMLHTTP_CONNECTION_TIMEOUT = 60 * 1000; + XMLHTTP_SENDRECV_TIMEOUT = 30 * 1000; + +{ TMsxmlHTTPClientImpl } + +constructor TMsxmlHTTPClientImpl.Create( const aUri: string; const aConfig : IThriftConfiguration); +begin + inherited Create( aConfig); + FUri := aUri; + + // defaults according to MSDN + FDnsResolveTimeout := 0; // no timeout + FConnectionTimeout := XMLHTTP_CONNECTION_TIMEOUT; + FSendTimeout := XMLHTTP_SENDRECV_TIMEOUT; + FReadTimeout := XMLHTTP_SENDRECV_TIMEOUT; + + FCustomHeaders := TThriftDictionaryImpl.Create; + FOutputStream := TThriftStreamAdapterDelphi.Create( TMemoryStream.Create, True); +end; + +function TMsxmlHTTPClientImpl.CreateRequest: IXMLHTTPRequest; +var + pair : TPair; + srvHttp : IServerXMLHTTPRequest; +begin + {$IF CompilerVersion >= 21.0} + Result := CoServerXMLHTTP.Create; + {$ELSE} + Result := CoXMLHTTPRequest.Create; + {$IFEND} + + // setting a timeout value to 0 (zero) means "no timeout" for that setting + if Supports( result, IServerXMLHTTPRequest, srvHttp) + then srvHttp.setTimeouts( DnsResolveTimeout, ConnectionTimeout, SendTimeout, ReadTimeout); + + Result.open('POST', FUri, False, '', ''); + Result.setRequestHeader( 'Content-Type', THRIFT_MIMETYPE); + Result.setRequestHeader( 'Accept', THRIFT_MIMETYPE); + Result.setRequestHeader( 'User-Agent', 'Delphi/IHTTPClient'); + + for pair in FCustomHeaders do begin + Result.setRequestHeader( pair.Key, pair.Value ); + end; +end; + +destructor TMsxmlHTTPClientImpl.Destroy; +begin + Close; + inherited; +end; + +function TMsxmlHTTPClientImpl.GetDnsResolveTimeout: Integer; +begin + Result := FDnsResolveTimeout; +end; + +procedure TMsxmlHTTPClientImpl.SetDnsResolveTimeout(const Value: Integer); +begin + FDnsResolveTimeout := Value; +end; + +function TMsxmlHTTPClientImpl.GetConnectionTimeout: Integer; +begin + Result := FConnectionTimeout; +end; + +procedure TMsxmlHTTPClientImpl.SetConnectionTimeout(const Value: Integer); +begin + FConnectionTimeout := Value; +end; + +function TMsxmlHTTPClientImpl.GetSendTimeout: Integer; +begin + Result := FSendTimeout; +end; + +procedure TMsxmlHTTPClientImpl.SetSendTimeout(const Value: Integer); +begin + FSendTimeout := Value; +end; + +function TMsxmlHTTPClientImpl.GetReadTimeout: Integer; +begin + Result := FReadTimeout; +end; + +procedure TMsxmlHTTPClientImpl.SetReadTimeout(const Value: Integer); +begin + FReadTimeout := Value; +end; + +function TMsxmlHTTPClientImpl.GetSecureProtocols : TSecureProtocols; +begin + Result := []; +end; + +procedure TMsxmlHTTPClientImpl.SetSecureProtocols( const value : TSecureProtocols); +begin + raise TTransportExceptionBadArgs.Create('SetSecureProtocols: Not supported with '+ClassName); +end; + +function TMsxmlHTTPClientImpl.GetCustomHeaders: IThriftDictionary; +begin + Result := FCustomHeaders; +end; + +function TMsxmlHTTPClientImpl.GetIsOpen: Boolean; +begin + Result := True; +end; + +procedure TMsxmlHTTPClientImpl.Open; +begin + FOutputStream := TThriftStreamAdapterDelphi.Create( TMemoryStream.Create, True); +end; + +procedure TMsxmlHTTPClientImpl.Close; +begin + FInputStream := nil; + FOutputStream := nil; +end; + +procedure TMsxmlHTTPClientImpl.Flush; +begin + try + SendRequest; + finally + FOutputStream := nil; + FOutputStream := TThriftStreamAdapterDelphi.Create( TMemoryStream.Create, True); + ASSERT( FOutputStream <> nil); + end; +end; + +function TMsxmlHTTPClientImpl.Read( const pBuf : Pointer; const buflen : Integer; off: Integer; len: Integer): Integer; +begin + if FInputStream = nil then begin + raise TTransportExceptionNotOpen.Create('No request has been sent'); + end; + + try + Result := FInputStream.Read( pBuf, buflen, off, len); + except + on E: Exception + do raise TTransportExceptionUnknown.Create(E.Message); + end; +end; + +procedure TMsxmlHTTPClientImpl.SendRequest; +var + xmlhttp : IXMLHTTPRequest; + ms : TMemoryStream; + a : TBytes; + len : Integer; +begin + xmlhttp := CreateRequest; + + ms := TMemoryStream.Create; + try + a := FOutputStream.ToArray; + len := Length(a); + if len > 0 then begin + ms.WriteBuffer( Pointer(@a[0])^, len); + end; + ms.Position := 0; + xmlhttp.send( IUnknown( TStreamAdapter.Create( ms, soReference ))); + FInputStream := nil; + FInputStream := TThriftStreamAdapterCOM.Create( IUnknown( xmlhttp.responseStream) as IStream); + UpdateKnownMessageSize( FInputStream.Size); + finally + ms.Free; + end; +end; + +procedure TMsxmlHTTPClientImpl.Write( const pBuf : Pointer; off, len : Integer); +begin + FOutputStream.Write( pBuf, off, len); +end; + + + +end. + diff --git a/lib/delphi/src/Thrift.Transport.Pipes.pas b/lib/delphi/src/Thrift.Transport.Pipes.pas index 77a343b0cac..635a8417859 100644 --- a/lib/delphi/src/Thrift.Transport.Pipes.pas +++ b/lib/delphi/src/Thrift.Transport.Pipes.pas @@ -29,6 +29,7 @@ interface {$ELSE} Winapi.Windows, System.SysUtils, System.Math, Winapi.AccCtrl, Winapi.AclAPI, System.SyncObjs, {$ENDIF} + Thrift.Configuration, Thrift.Transport, Thrift.Utils, Thrift.Stream; @@ -64,7 +65,9 @@ TPipeStreamBase = class( TThriftStreamImpl) public constructor Create( aEnableOverlapped : Boolean; const aTimeOut : DWORD = DEFAULT_THRIFT_TIMEOUT; - const aOpenTimeOut : DWORD = DEFAULT_THRIFT_PIPE_OPEN_TIMEOUT); + const aOpenTimeOut : DWORD = DEFAULT_THRIFT_PIPE_OPEN_TIMEOUT + ); reintroduce; overload; + destructor Destroy; override; end; @@ -84,7 +87,8 @@ TNamedPipeStreamImpl = class sealed( TPipeStreamBase) const aShareMode: DWORD = 0; const aSecurityAttributes: PSecurityAttributes = nil; const aTimeOut : DWORD = DEFAULT_THRIFT_TIMEOUT; - const aOpenTimeOut : DWORD = DEFAULT_THRIFT_PIPE_OPEN_TIMEOUT); overload; + const aOpenTimeOut : DWORD = DEFAULT_THRIFT_PIPE_OPEN_TIMEOUT + ); reintroduce; overload; end; @@ -98,7 +102,9 @@ THandlePipeStreamImpl = class sealed( TPipeStreamBase) public constructor Create( const aPipeHandle : THandle; const aOwnsHandle, aEnableOverlapped : Boolean; - const aTimeOut : DWORD = DEFAULT_THRIFT_TIMEOUT); overload; + const aTimeOut : DWORD = DEFAULT_THRIFT_TIMEOUT + ); reintroduce; overload; + destructor Destroy; override; end; @@ -112,7 +118,7 @@ THandlePipeStreamImpl = class sealed( TPipeStreamBase) TPipeTransportBase = class( TStreamTransportImpl, IPipeTransport) - public + strict protected // ITransport function GetIsOpen: Boolean; override; procedure Open; override; @@ -123,33 +129,46 @@ TPipeTransportBase = class( TStreamTransportImpl, IPipeTransport) TNamedPipeTransportClientEndImpl = class( TPipeTransportBase) public // Named pipe constructors - constructor Create( aPipe : THandle; aOwnsHandle : Boolean; - const aTimeOut : DWORD); overload; + constructor Create( const aPipe : THandle; + const aOwnsHandle : Boolean; + const aTimeOut : DWORD; + const aConfig : IThriftConfiguration = nil + ); reintroduce; overload; + constructor Create( const aPipeName : string; const aShareMode: DWORD = 0; const aSecurityAttributes: PSecurityAttributes = nil; const aTimeOut : DWORD = DEFAULT_THRIFT_TIMEOUT; - const aOpenTimeOut : DWORD = DEFAULT_THRIFT_PIPE_OPEN_TIMEOUT); overload; + const aOpenTimeOut : DWORD = DEFAULT_THRIFT_PIPE_OPEN_TIMEOUT; + const aConfig : IThriftConfiguration = nil + ); reintroduce; overload; end; TNamedPipeTransportServerEndImpl = class( TNamedPipeTransportClientEndImpl) strict private FHandle : THandle; - public + strict protected // ITransport procedure Close; override; - constructor Create( aPipe : THandle; aOwnsHandle : Boolean; - const aTimeOut : DWORD = DEFAULT_THRIFT_TIMEOUT); reintroduce; + public + constructor Create( const aPipe : THandle; + const aOwnsHandle : Boolean; + const aTimeOut : DWORD = DEFAULT_THRIFT_TIMEOUT; + const aConfig : IThriftConfiguration = nil + ); reintroduce; overload; + end; TAnonymousPipeTransportImpl = class( TPipeTransportBase) public // Anonymous pipe constructor - constructor Create(const aPipeRead, aPipeWrite : THandle; - aOwnsHandles : Boolean; - const aTimeOut : DWORD = DEFAULT_THRIFT_TIMEOUT); overload; + constructor Create( const aPipeRead, aPipeWrite : THandle; + const aOwnsHandles : Boolean; + const aTimeOut : DWORD = DEFAULT_THRIFT_TIMEOUT; + const aConfig : IThriftConfiguration = nil + ); reintroduce; overload; end; @@ -179,7 +198,7 @@ TPipeServerTransportBase = class( TServerTransportImpl) procedure InternalClose; virtual; abstract; function QueryStopServer : Boolean; public - constructor Create; + constructor Create( const aConfig : IThriftConfiguration); destructor Destroy; override; procedure Listen; override; procedure Close; override; @@ -199,7 +218,7 @@ TAnonymousPipeServerTransportImpl = class( TPipeServerTransportBase, IAnonymou FClientAnonWrite : THandle; FTimeOut: DWORD; - protected + strict protected function Accept(const fnAccepting: TProc): ITransport; override; function CreateAnonPipe : Boolean; @@ -213,7 +232,10 @@ TAnonymousPipeServerTransportImpl = class( TPipeServerTransportBase, IAnonymou procedure InternalClose; override; public - constructor Create(aBufsize : Cardinal = 4096; aTimeOut : DWORD = DEFAULT_THRIFT_TIMEOUT); + constructor Create( const aBufsize : Cardinal = 4096; + const aTimeOut : DWORD = DEFAULT_THRIFT_TIMEOUT; + const aConfig : IThriftConfiguration = nil + ); reintroduce; overload; end; @@ -237,9 +259,12 @@ TNamedPipeServerTransportImpl = class( TPipeServerTransportBase, INamedPipeSer procedure InternalClose; override; public - constructor Create( aPipename : string; aBufsize : Cardinal = 4096; - aMaxConns : Cardinal = PIPE_UNLIMITED_INSTANCES; - aTimeOut : Cardinal = INFINITE); + constructor Create( const aPipename : string; + const aBufsize : Cardinal = 4096; + const aMaxConns : Cardinal = PIPE_UNLIMITED_INSTANCES; + const aTimeOut : Cardinal = INFINITE; + const aConfig : IThriftConfiguration = nil + ); reintroduce; overload; end; @@ -270,15 +295,14 @@ function DuplicatePipeHandle( const hSource : THandle) : THandle; { TPipeStreamBase } -constructor TPipeStreamBase.Create( aEnableOverlapped : Boolean; - const aTimeOut, aOpenTimeOut : DWORD); +constructor TPipeStreamBase.Create( aEnableOverlapped : Boolean; const aTimeOut, aOpenTimeOut : DWORD); begin inherited Create; - ASSERT( aTimeout > 0); // aOpenTimeout may be 0 FPipe := INVALID_HANDLE_VALUE; FTimeout := aTimeOut; FOpenTimeOut := aOpenTimeOut; FOverlapped := aEnableOverlapped; + ASSERT( FTimeout > 0); // FOpenTimeout may be 0 end; @@ -524,7 +548,7 @@ constructor TNamedPipeStreamImpl.Create( const aPipeName : string; const aSecurityAttributes: PSecurityAttributes; const aTimeOut, aOpenTimeOut : DWORD); begin - inherited Create( aEnableOverlapped, aTimeout, aOpenTimeOut); + inherited Create( aEnableOverlapped, aTimeOut, aOpenTimeOut); FPipeName := aPipeName; FShareMode := aShareMode; @@ -587,7 +611,7 @@ constructor THandlePipeStreamImpl.Create( const aPipeHandle : THandle; const aOwnsHandle, aEnableOverlapped : Boolean; const aTimeOut : DWORD); begin - inherited Create( aEnableOverlapped, aTimeOut); + inherited Create( aEnableOverlapped, aTimeout, aTimeout); if aOwnsHandle then FSrcHandle := aPipeHandle @@ -641,23 +665,27 @@ procedure TPipeTransportBase.Close; { TNamedPipeTransportClientEndImpl } -constructor TNamedPipeTransportClientEndImpl.Create( const aPipeName : string; const aShareMode: DWORD; - const aSecurityAttributes: PSecurityAttributes; - const aTimeOut, aOpenTimeOut : DWORD); +constructor TNamedPipeTransportClientEndImpl.Create( const aPipeName : string; + const aShareMode: DWORD; + const aSecurityAttributes: PSecurityAttributes; + const aTimeOut, aOpenTimeOut : DWORD; + const aConfig : IThriftConfiguration); // Named pipe constructor begin - inherited Create( nil, nil); + inherited Create( nil, nil, aConfig); FInputStream := TNamedPipeStreamImpl.Create( aPipeName, TRUE, aShareMode, aSecurityAttributes, aTimeOut, aOpenTimeOut); FOutputStream := FInputStream; // true for named pipes end; -constructor TNamedPipeTransportClientEndImpl.Create( aPipe : THandle; aOwnsHandle : Boolean; - const aTimeOut : DWORD); +constructor TNamedPipeTransportClientEndImpl.Create( const aPipe : THandle; + const aOwnsHandle : Boolean; + const aTimeOut : DWORD; + const aConfig : IThriftConfiguration); // Named pipe constructor begin - inherited Create( nil, nil); - FInputStream := THandlePipeStreamImpl.Create( aPipe, TRUE, aOwnsHandle, aTimeOut); + inherited Create( nil, nil, aConfig); + FInputStream := THandlePipeStreamImpl.Create( aPipe, aOwnsHandle, TRUE, aTimeOut); FOutputStream := FInputStream; // true for named pipes end; @@ -665,12 +693,14 @@ constructor TNamedPipeTransportClientEndImpl.Create( aPipe : THandle; aOwnsHandl { TNamedPipeTransportServerEndImpl } -constructor TNamedPipeTransportServerEndImpl.Create( aPipe : THandle; aOwnsHandle : Boolean; - const aTimeOut : DWORD); +constructor TNamedPipeTransportServerEndImpl.Create( const aPipe : THandle; + const aOwnsHandle : Boolean; + const aTimeOut : DWORD; + const aConfig : IThriftConfiguration); // Named pipe constructor begin FHandle := DuplicatePipeHandle( aPipe); - inherited Create( aPipe, aOwnsHandle, aTimeOut); + inherited Create( aPipe, aOwnsHandle, aTimeout, aConfig); end; @@ -688,23 +718,24 @@ procedure TNamedPipeTransportServerEndImpl.Close; constructor TAnonymousPipeTransportImpl.Create( const aPipeRead, aPipeWrite : THandle; - aOwnsHandles : Boolean; - const aTimeOut : DWORD = DEFAULT_THRIFT_TIMEOUT); + const aOwnsHandles : Boolean; + const aTimeOut : DWORD; + const aConfig : IThriftConfiguration); // Anonymous pipe constructor begin - inherited Create( nil, nil); + inherited Create( nil, nil, aConfig); // overlapped is not supported with AnonPipes, see MSDN - FInputStream := THandlePipeStreamImpl.Create( aPipeRead, aOwnsHandles, FALSE, aTimeOut); - FOutputStream := THandlePipeStreamImpl.Create( aPipeWrite, aOwnsHandles, FALSE, aTimeOut); + FInputStream := THandlePipeStreamImpl.Create( aPipeRead, aOwnsHandles, FALSE, aTimeout); + FOutputStream := THandlePipeStreamImpl.Create( aPipeWrite, aOwnsHandles, FALSE, aTimeout); end; { TPipeServerTransportBase } -constructor TPipeServerTransportBase.Create; +constructor TPipeServerTransportBase.Create( const aConfig : IThriftConfiguration); begin - inherited Create; + inherited Create( aConfig); FStopServer := TEvent.Create(nil,TRUE,FALSE,''); // manual reset end; @@ -741,11 +772,12 @@ procedure TPipeServerTransportBase.Close; { TAnonymousPipeServerTransportImpl } - -constructor TAnonymousPipeServerTransportImpl.Create(aBufsize : Cardinal; aTimeOut : DWORD); +constructor TAnonymousPipeServerTransportImpl.Create( const aBufsize : Cardinal; + const aTimeOut : DWORD; + const aConfig : IThriftConfiguration); // Anonymous pipe CTOR begin - inherited Create; + inherited Create(aConfig); FBufsize := aBufSize; FReadHandle := INVALID_HANDLE_VALUE; FWriteHandle := INVALID_HANDLE_VALUE; @@ -774,7 +806,7 @@ function TAnonymousPipeServerTransportImpl.Accept(const fnAccepting: TProc): ITr then raise TTransportExceptionNotOpen.Create('TServerPipe unable to initiate pipe communication'); // create the transport impl - result := TAnonymousPipeTransportImpl.Create( FReadHandle, FWriteHandle, FALSE, FTimeOut); + result := TAnonymousPipeTransportImpl.Create( FReadHandle, FWriteHandle, FALSE, FTimeOut, Configuration); end; @@ -852,17 +884,19 @@ function TAnonymousPipeServerTransportImpl.CreateAnonPipe : Boolean; { TNamedPipeServerTransportImpl } -constructor TNamedPipeServerTransportImpl.Create( aPipename : string; aBufsize, aMaxConns, aTimeOut : Cardinal); +constructor TNamedPipeServerTransportImpl.Create( const aPipename : string; + const aBufsize, aMaxConns, aTimeOut : Cardinal; + const aConfig : IThriftConfiguration); // Named Pipe CTOR begin - inherited Create; - ASSERT( aTimeout > 0); + inherited Create( aConfig); FPipeName := aPipename; FBufsize := aBufSize; FMaxConns := Max( 1, Min( PIPE_UNLIMITED_INSTANCES, aMaxConns)); FHandle := INVALID_HANDLE_VALUE; FTimeout := aTimeOut; FConnected := FALSE; + ASSERT( FTimeout > 0); if Copy(FPipeName,1,2) <> '\\' then FPipeName := '\\.\pipe\' + FPipeName; // assume localhost @@ -931,7 +965,7 @@ function TNamedPipeServerTransportImpl.CreateTransportInstance : ITransport; hPipe := THandle( InterlockedExchangePointer( Pointer(FHandle), Pointer(INVALID_HANDLE_VALUE))); try FConnected := FALSE; - result := TNamedPipeTransportServerEndImpl.Create( hPipe, TRUE, FTimeout); + result := TNamedPipeTransportServerEndImpl.Create( hPipe, TRUE, FTimeout, Configuration); except ClosePipeHandle(hPipe); raise; diff --git a/lib/delphi/src/Thrift.Transport.WinHTTP.pas b/lib/delphi/src/Thrift.Transport.WinHTTP.pas new file mode 100644 index 00000000000..b0f32ef3b81 --- /dev/null +++ b/lib/delphi/src/Thrift.Transport.WinHTTP.pas @@ -0,0 +1,418 @@ +(* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + *) +unit Thrift.Transport.WinHTTP; + +{$I Thrift.Defines.inc} +{$SCOPEDENUMS ON} + +interface + +uses + Classes, + SysUtils, + Math, + Generics.Collections, + Thrift.Collections, + Thrift.Configuration, + Thrift.Transport, + Thrift.Exception, + Thrift.Utils, + Thrift.WinHTTP, + Thrift.Stream; + +type + TWinHTTPClientImpl = class( TEndpointTransportBase, IHTTPClient) + strict private + FUri : string; + FInputStream : IThriftStream; + FOutputMemoryStream : TMemoryStream; + FDnsResolveTimeout : Integer; + FConnectionTimeout : Integer; + FSendTimeout : Integer; + FReadTimeout : Integer; + FCustomHeaders : IThriftDictionary; + FSecureProtocols : TSecureProtocols; + + function CreateRequest: IWinHTTPRequest; + function SecureProtocolsAsWinHTTPFlags : Cardinal; + + strict private + type + TErrorInfo = ( SplitUrl, WinHTTPSession, WinHTTPConnection, WinHTTPRequest, RequestSetup, AutoProxy ); + + THTTPResponseStream = class( TThriftStreamImpl) + strict private + FRequest : IWinHTTPRequest; + strict protected + procedure Write( const pBuf : Pointer; offset: Integer; count: Integer); override; + function Read( const pBuf : Pointer; const buflen : Integer; offset: Integer; count: Integer): Integer; override; + procedure Open; override; + procedure Close; override; + procedure Flush; override; + function IsOpen: Boolean; override; + function ToArray: TBytes; override; + public + constructor Create( const aRequest : IWinHTTPRequest); + destructor Destroy; override; + end; + + strict protected + function GetIsOpen: Boolean; override; + procedure Open(); override; + procedure Close(); override; + function Read( const pBuf : Pointer; const buflen : Integer; off: Integer; len: Integer): Integer; override; + procedure Write( const pBuf : Pointer; off, len : Integer); override; + procedure Flush; override; + + procedure SetDnsResolveTimeout(const Value: Integer); + function GetDnsResolveTimeout: Integer; + procedure SetConnectionTimeout(const Value: Integer); + function GetConnectionTimeout: Integer; + procedure SetSendTimeout(const Value: Integer); + function GetSendTimeout: Integer; + procedure SetReadTimeout(const Value: Integer); + function GetReadTimeout: Integer; + function GetSecureProtocols : TSecureProtocols; + procedure SetSecureProtocols( const value : TSecureProtocols); + + function GetCustomHeaders: IThriftDictionary; + procedure SendRequest; + + property DnsResolveTimeout: Integer read GetDnsResolveTimeout write SetDnsResolveTimeout; + property ConnectionTimeout: Integer read GetConnectionTimeout write SetConnectionTimeout; + property SendTimeout: Integer read GetSendTimeout write SetSendTimeout; + property ReadTimeout: Integer read GetReadTimeout write SetReadTimeout; + property CustomHeaders: IThriftDictionary read GetCustomHeaders; + public + constructor Create( const aUri: string; const aConfig : IThriftConfiguration = nil); + destructor Destroy; override; + end; + +implementation + +const + WINHTTP_CONNECTION_TIMEOUT = 60 * 1000; + WINHTTP_SENDRECV_TIMEOUT = 30 * 1000; + + +{ TWinHTTPClientImpl } + +constructor TWinHTTPClientImpl.Create( const aUri: string; const aConfig : IThriftConfiguration); +begin + inherited Create( aConfig); + FUri := AUri; + + // defaults according to MSDN + FDnsResolveTimeout := 0; // no timeout + FConnectionTimeout := WINHTTP_CONNECTION_TIMEOUT; + FSendTimeout := WINHTTP_SENDRECV_TIMEOUT; + FReadTimeout := WINHTTP_SENDRECV_TIMEOUT; + + FSecureProtocols := DEFAULT_THRIFT_SECUREPROTOCOLS; + + FCustomHeaders := TThriftDictionaryImpl.Create; + FOutputMemoryStream := TMemoryStream.Create; +end; + +destructor TWinHTTPClientImpl.Destroy; +begin + Close; + FreeAndNil( FOutputMemoryStream); + inherited; +end; + +function TWinHTTPClientImpl.CreateRequest: IWinHTTPRequest; +var + pair : TPair; + session : IWinHTTPSession; + connect : IWinHTTPConnection; + url : IWinHTTPUrl; + sPath : string; + info : TErrorInfo; +begin + info := TErrorInfo.SplitUrl; + try + url := TWinHTTPUrlImpl.Create( FUri); + + info := TErrorInfo.WinHTTPSession; + session := TWinHTTPSessionImpl.Create('Apache Thrift Delphi WinHTTP'); + session.EnableSecureProtocols( SecureProtocolsAsWinHTTPFlags); + + info := TErrorInfo.WinHTTPConnection; + connect := session.Connect( url.HostName, url.Port); + + info := TErrorInfo.WinHTTPRequest; + sPath := url.UrlPath + url.ExtraInfo; + result := connect.OpenRequest( (url.Scheme = 'https'), 'POST', sPath, THRIFT_MIMETYPE); + + // setting a timeout value to 0 (zero) means "no timeout" for that setting + info := TErrorInfo.RequestSetup; + result.SetTimeouts( DnsResolveTimeout, ConnectionTimeout, SendTimeout, ReadTimeout); + + // headers + result.AddRequestHeader( 'Content-Type: '+THRIFT_MIMETYPE, WINHTTP_ADDREQ_FLAG_ADD); + for pair in FCustomHeaders do begin + Result.AddRequestHeader( pair.Key +': '+ pair.Value, WINHTTP_ADDREQ_FLAG_ADD); + end; + + // enable automatic gzip,deflate decompression + result.EnableAutomaticContentDecompression(TRUE); + + // AutoProxy support + info := TErrorInfo.AutoProxy; + result.TryAutoProxy( FUri); + except + on e:TException do raise; + on e:Exception do raise TTransportExceptionUnknown.Create( e.Message+' (at '+EnumUtils.ToString(Ord(info))+')'); + end; +end; + + +function TWinHTTPClientImpl.SecureProtocolsAsWinHTTPFlags : Cardinal; +const + PROTOCOL_MAPPING : array[TSecureProtocol] of Cardinal = ( + WINHTTP_FLAG_SECURE_PROTOCOL_SSL2, + WINHTTP_FLAG_SECURE_PROTOCOL_SSL3, + WINHTTP_FLAG_SECURE_PROTOCOL_TLS1, + WINHTTP_FLAG_SECURE_PROTOCOL_TLS1_1, + WINHTTP_FLAG_SECURE_PROTOCOL_TLS1_2 + ); +var + prot : TSecureProtocol; + protos : TSecureProtocols; +begin + result := 0; + protos := GetSecureProtocols; + for prot := Low(TSecureProtocol) to High(TSecureProtocol) do begin + if prot in protos + then result := result or PROTOCOL_MAPPING[prot]; + end; +end; + + +function TWinHTTPClientImpl.GetDnsResolveTimeout: Integer; +begin + Result := FDnsResolveTimeout; +end; + +procedure TWinHTTPClientImpl.SetDnsResolveTimeout(const Value: Integer); +begin + FDnsResolveTimeout := Value; +end; + +function TWinHTTPClientImpl.GetConnectionTimeout: Integer; +begin + Result := FConnectionTimeout; +end; + +procedure TWinHTTPClientImpl.SetConnectionTimeout(const Value: Integer); +begin + FConnectionTimeout := Value; +end; + +function TWinHTTPClientImpl.GetSendTimeout: Integer; +begin + Result := FSendTimeout; +end; + +procedure TWinHTTPClientImpl.SetSendTimeout(const Value: Integer); +begin + FSendTimeout := Value; +end; + +function TWinHTTPClientImpl.GetReadTimeout: Integer; +begin + Result := FReadTimeout; +end; + +procedure TWinHTTPClientImpl.SetReadTimeout(const Value: Integer); +begin + FReadTimeout := Value; +end; + +function TWinHTTPClientImpl.GetSecureProtocols : TSecureProtocols; +begin + Result := FSecureProtocols; +end; + +procedure TWinHTTPClientImpl.SetSecureProtocols( const value : TSecureProtocols); +begin + FSecureProtocols := Value; +end; + +function TWinHTTPClientImpl.GetCustomHeaders: IThriftDictionary; +begin + Result := FCustomHeaders; +end; + +function TWinHTTPClientImpl.GetIsOpen: Boolean; +begin + Result := Assigned( FOutputMemoryStream); +end; + +procedure TWinHTTPClientImpl.Open; +begin + FreeAndNil( FOutputMemoryStream); + FOutputMemoryStream := TMemoryStream.Create; +end; + +procedure TWinHTTPClientImpl.Close; +begin + FInputStream := nil; + FreeAndNil( FOutputMemoryStream); +end; + +procedure TWinHTTPClientImpl.Flush; +begin + try + SendRequest; + finally + FreeAndNil( FOutputMemoryStream); + FOutputMemoryStream := TMemoryStream.Create; + ASSERT( FOutputMemoryStream <> nil); + end; +end; + +function TWinHTTPClientImpl.Read( const pBuf : Pointer; const buflen : Integer; off: Integer; len: Integer): Integer; +begin + if FInputStream = nil then begin + raise TTransportExceptionNotOpen.Create('No request has been sent'); + end; + + try + Result := FInputStream.Read( pBuf, buflen, off, len); + CountConsumedMessageBytes( result); + except + on E: Exception + do raise TTransportExceptionUnknown.Create(E.Message); + end; +end; + +procedure TWinHTTPClientImpl.SendRequest; +var + http : IWinHTTPRequest; + pData : PByte; + len : Integer; + error, dwSize : Cardinal; + sMsg : string; +begin + http := CreateRequest; + + pData := FOutputMemoryStream.Memory; + len := FOutputMemoryStream.Size; + + // send all data immediately, since we have it in memory + if not http.SendRequest( pData, len, 0) then begin + error := Cardinal( GetLastError); + sMsg := 'WinHTTP send error '+IntToStr(Int64(error))+' '+WinHttpSysErrorMessage(error); + raise TTransportExceptionUnknown.Create(sMsg); + end; + + // end request and start receiving + if not http.FlushAndReceiveResponse then begin + error := Cardinal( GetLastError); + sMsg := 'WinHTTP recv error '+IntToStr(Int64(error))+' '+WinHttpSysErrorMessage(error); + if error = ERROR_WINHTTP_TIMEOUT + then raise TTransportExceptionTimedOut.Create( sMsg) + else raise TTransportExceptionInterrupted.Create( sMsg); + end; + + // we're about to receive a new message, so reset everyting + ResetConsumedMessageSize(-1); + FInputStream := THTTPResponseStream.Create( http); + if http.QueryTotalResponseSize( dwSize) // FALSE indicates "no info available" + then UpdateKnownMessageSize( dwSize); +end; + +procedure TWinHTTPClientImpl.Write( const pBuf : Pointer; off, len : Integer); +var pTmp : PByte; +begin + pTmp := pBuf; + Inc(pTmp,off); + FOutputMemoryStream.Write( pTmp^, len); +end; + + +{ TWinHTTPClientImpl.THTTPResponseStream } + +constructor TWinHTTPClientImpl.THTTPResponseStream.Create( const aRequest : IWinHTTPRequest); +begin + inherited Create; + FRequest := aRequest; +end; + +destructor TWinHTTPClientImpl.THTTPResponseStream.Destroy; +begin + try + Close; + finally + inherited Destroy; + end; +end; + +procedure TWinHTTPClientImpl.THTTPResponseStream.Close; +begin + FRequest := nil; +end; + +procedure TWinHTTPClientImpl.THTTPResponseStream.Flush; +begin + raise ENotImplemented(ClassName+'.Flush'); +end; + +function TWinHTTPClientImpl.THTTPResponseStream.IsOpen: Boolean; +begin + Result := FRequest <> nil; +end; + +procedure TWinHTTPClientImpl.THTTPResponseStream.Open; +begin + // nothing to do +end; + +procedure TWinHTTPClientImpl.THTTPResponseStream.Write(const pBuf : Pointer; offset, count: Integer); +begin + inherited; // check pointers + raise ENotImplemented(ClassName+'.Write'); +end; + +function TWinHTTPClientImpl.THTTPResponseStream.Read(const pBuf : Pointer; const buflen : Integer; offset, count: Integer): Integer; +var pTmp : PByte; +begin + inherited; // check pointers + + if count >= buflen-offset + then count := buflen-offset; + + if count > 0 then begin + pTmp := pBuf; + Inc( pTmp, offset); + Result := FRequest.ReadData( pTmp, count); + ASSERT( Result >= 0); + end + else Result := 0; +end; + +function TWinHTTPClientImpl.THTTPResponseStream.ToArray: TBytes; +begin + raise ENotImplemented(ClassName+'.ToArray'); +end; + + +end. diff --git a/lib/delphi/src/Thrift.Transport.pas b/lib/delphi/src/Thrift.Transport.pas index dad9ab7f305..6a69d93380f 100644 --- a/lib/delphi/src/Thrift.Transport.pas +++ b/lib/delphi/src/Thrift.Transport.pas @@ -29,28 +29,37 @@ interface Math, Generics.Collections, {$IFDEF OLD_UNIT_NAMES} - ActiveX, msxml, WinSock, Sockets, + WinSock, Sockets, {$ELSE} - Winapi.ActiveX, Winapi.msxml, Winapi.WinSock, + Winapi.WinSock, {$IFDEF OLD_SOCKETS} Web.Win.Sockets, {$ELSE} Thrift.Socket, {$ENDIF} {$ENDIF} + Thrift.Configuration, Thrift.Collections, Thrift.Exception, Thrift.Utils, + Thrift.WinHTTP, Thrift.Stream; +const + DEFAULT_MAX_MESSAGE_SIZE = 100 * 1024 * 1024; // 100 MB + DEFAULT_THRIFT_TIMEOUT = 5 * 1000; // ms + type + IStreamTransport = interface; + ITransport = interface - ['{DB84961E-8BB3-4532-99E1-A8C7AC2300F7}'] + ['{52F81383-F880-492F-8AA7-A66B85B93D6B}'] function GetIsOpen: Boolean; property IsOpen: Boolean read GetIsOpen; function Peek: Boolean; procedure Open; procedure Close; + function Read(var buf: TBytes; off: Integer; len: Integer): Integer; overload; function Read(const pBuf : Pointer; const buflen : Integer; off: Integer; len: Integer): Integer; overload; function ReadAll(var buf: TBytes; off: Integer; len: Integer): Integer; overload; @@ -60,15 +69,22 @@ interface procedure Write( const pBuf : Pointer; off, len : Integer); overload; procedure Write( const pBuf : Pointer; len : Integer); overload; procedure Flush; + + function Configuration : IThriftConfiguration; + function MaxMessageSize : Integer; + procedure ResetConsumedMessageSize( const knownSize : Int64 = -1); + procedure CheckReadBytesAvailable( const numBytes : Int64); + procedure UpdateKnownMessageSize( const size : Int64); end; - TTransportImpl = class( TInterfacedObject, ITransport) - protected + TTransportBase = class abstract( TInterfacedObject) + strict protected function GetIsOpen: Boolean; virtual; abstract; property IsOpen: Boolean read GetIsOpen; function Peek: Boolean; virtual; procedure Open(); virtual; abstract; procedure Close(); virtual; abstract; + function Read(var buf: TBytes; off: Integer; len: Integer): Integer; overload; inline; function Read(const pBuf : Pointer; const buflen : Integer; off: Integer; len: Integer): Integer; overload; virtual; abstract; function ReadAll(var buf: TBytes; off: Integer; len: Integer): Integer; overload; inline; @@ -78,9 +94,48 @@ TTransportImpl = class( TInterfacedObject, ITransport) procedure Write( const pBuf : Pointer; len : Integer); overload; inline; procedure Write( const pBuf : Pointer; off, len : Integer); overload; virtual; abstract; procedure Flush; virtual; + + function Configuration : IThriftConfiguration; virtual; abstract; + procedure UpdateKnownMessageSize( const size : Int64); virtual; abstract; + end; + + // base class for all endpoint transports, e.g. sockets, pipes or HTTP + TEndpointTransportBase = class abstract( TTransportBase, ITransport) + strict private + FRemainingMessageSize : Int64; + FKnownMessageSize : Int64; + FConfiguration : IThriftConfiguration; + strict protected + function Configuration : IThriftConfiguration; override; + function MaxMessageSize : Integer; + property RemainingMessageSize : Int64 read FRemainingMessageSize; + property KnownMessageSize : Int64 read FKnownMessageSize; + procedure ResetConsumedMessageSize( const newSize : Int64 = -1); + procedure UpdateKnownMessageSize(const size : Int64); override; + procedure CheckReadBytesAvailable(const numBytes : Int64); inline; + procedure CountConsumedMessageBytes(const numBytes : Int64); inline; + public + constructor Create( const aConfig : IThriftConfiguration); reintroduce; + end; + + // base class for all layered transports, e.g. framed + TLayeredTransportBase = class abstract( TTransportBase, ITransport) + strict private + FTransport : T; + strict protected + property InnerTransport : T read FTransport; + function GetUnderlyingTransport: ITransport; + function Configuration : IThriftConfiguration; override; + procedure UpdateKnownMessageSize( const size : Int64); override; + function MaxMessageSize : Integer; inline; + procedure ResetConsumedMessageSize( const knownSize : Int64 = -1); inline; + procedure CheckReadBytesAvailable( const numBytes : Int64); virtual; + public + constructor Create( const aTransport: T); reintroduce; + property UnderlyingTransport: ITransport read GetUnderlyingTransport; end; - TTransportException = class( TException) + TTransportException = class abstract( TException) public type TExceptionType = ( @@ -90,16 +145,16 @@ TTransportException = class( TException) TimedOut, EndOfFile, BadArgs, - Interrupted + Interrupted, + CorruptedData ); - private - function GetType: TExceptionType; - protected + strict protected constructor HiddenCreate(const Msg: string); + class function GetType: TExceptionType; virtual; abstract; public - class function Create( AType: TExceptionType): TTransportException; overload; deprecated 'Use specialized TTransportException types (or regenerate from IDL)'; + class function Create( aType: TExceptionType): TTransportException; overload; deprecated 'Use specialized TTransportException types (or regenerate from IDL)'; class function Create( const msg: string): TTransportException; reintroduce; overload; deprecated 'Use specialized TTransportException types (or regenerate from IDL)'; - class function Create( AType: TExceptionType; const msg: string): TTransportException; overload; deprecated 'Use specialized TTransportException types (or regenerate from IDL)'; + class function Create( aType: TExceptionType; const msg: string): TTransportException; overload; deprecated 'Use specialized TTransportException types (or regenerate from IDL)'; property Type_: TExceptionType read GetType; end; @@ -109,54 +164,55 @@ TTransportExceptionSpecialized = class abstract (TTransportException) constructor Create(const Msg: string); end; - TTransportExceptionUnknown = class (TTransportExceptionSpecialized); - TTransportExceptionNotOpen = class (TTransportExceptionSpecialized); - TTransportExceptionAlreadyOpen = class (TTransportExceptionSpecialized); - TTransportExceptionTimedOut = class (TTransportExceptionSpecialized); - TTransportExceptionEndOfFile = class (TTransportExceptionSpecialized); - TTransportExceptionBadArgs = class (TTransportExceptionSpecialized); - TTransportExceptionInterrupted = class (TTransportExceptionSpecialized); + TTransportExceptionUnknown = class (TTransportExceptionSpecialized) + strict protected + class function GetType: TTransportException.TExceptionType; override; + end; - IHTTPClient = interface( ITransport ) - ['{BA142D12-8AE6-4B50-9E33-6B7843B21D73}'] - procedure SetDnsResolveTimeout(const Value: Integer); - function GetDnsResolveTimeout: Integer; - procedure SetConnectionTimeout(const Value: Integer); - function GetConnectionTimeout: Integer; - procedure SetSendTimeout(const Value: Integer); - function GetSendTimeout: Integer; - procedure SetReadTimeout(const Value: Integer); - function GetReadTimeout: Integer; - function GetCustomHeaders: IThriftDictionary; - procedure SendRequest; + TTransportExceptionNotOpen = class (TTransportExceptionSpecialized) + strict protected + class function GetType: TTransportException.TExceptionType; override; + end; - property DnsResolveTimeout: Integer read GetDnsResolveTimeout write SetDnsResolveTimeout; - property ConnectionTimeout: Integer read GetConnectionTimeout write SetConnectionTimeout; - property SendTimeout: Integer read GetSendTimeout write SetSendTimeout; - property ReadTimeout: Integer read GetReadTimeout write SetReadTimeout; - property CustomHeaders: IThriftDictionary read GetCustomHeaders; + TTransportExceptionAlreadyOpen = class (TTransportExceptionSpecialized) + strict protected + class function GetType: TTransportException.TExceptionType; override; end; - THTTPClientImpl = class( TTransportImpl, IHTTPClient) - private - FUri : string; - FInputStream : IThriftStream; - FOutputStream : IThriftStream; - FDnsResolveTimeout : Integer; - FConnectionTimeout : Integer; - FSendTimeout : Integer; - FReadTimeout : Integer; - FCustomHeaders : IThriftDictionary; + TTransportExceptionTimedOut = class (TTransportExceptionSpecialized) + strict protected + class function GetType: TTransportException.TExceptionType; override; + end; + + TTransportExceptionEndOfFile = class (TTransportExceptionSpecialized) + strict protected + class function GetType: TTransportException.TExceptionType; override; + end; + + TTransportExceptionBadArgs = class (TTransportExceptionSpecialized) + strict protected + class function GetType: TTransportException.TExceptionType; override; + end; + + TTransportExceptionInterrupted = class (TTransportExceptionSpecialized) + strict protected + class function GetType: TTransportException.TExceptionType; override; + end; - function CreateRequest: IXMLHTTPRequest; + TTransportExceptionCorruptedData = class (TTransportExceptionSpecialized) protected - function GetIsOpen: Boolean; override; - procedure Open(); override; - procedure Close(); override; - function Read( const pBuf : Pointer; const buflen : Integer; off: Integer; len: Integer): Integer; override; - procedure Write( const pBuf : Pointer; off, len : Integer); override; - procedure Flush; override; + class function GetType: TTransportException.TExceptionType; override; + end; + + TSecureProtocol = ( + SSL_2, SSL_3, TLS_1, // outdated, for compatibilty only + TLS_1_1, TLS_1_2 // secure (as of today) + ); + TSecureProtocols = set of TSecureProtocol; + + IHTTPClient = interface( ITransport ) + ['{7BF615DD-8680-4004-A5B2-88947BA3BA3D}'] procedure SetDnsResolveTimeout(const Value: Integer); function GetDnsResolveTimeout: Integer; procedure SetConnectionTimeout(const Value: Integer); @@ -165,47 +221,55 @@ THTTPClientImpl = class( TTransportImpl, IHTTPClient) function GetSendTimeout: Integer; procedure SetReadTimeout(const Value: Integer); function GetReadTimeout: Integer; - function GetCustomHeaders: IThriftDictionary; procedure SendRequest; + function GetSecureProtocols : TSecureProtocols; + procedure SetSecureProtocols( const value : TSecureProtocols); + property DnsResolveTimeout: Integer read GetDnsResolveTimeout write SetDnsResolveTimeout; property ConnectionTimeout: Integer read GetConnectionTimeout write SetConnectionTimeout; property SendTimeout: Integer read GetSendTimeout write SetSendTimeout; property ReadTimeout: Integer read GetReadTimeout write SetReadTimeout; property CustomHeaders: IThriftDictionary read GetCustomHeaders; - public - constructor Create( const AUri: string); - destructor Destroy; override; + property SecureProtocols : TSecureProtocols read GetSecureProtocols write SetSecureProtocols; end; IServerTransport = interface - ['{C43B87ED-69EA-47C4-B77C-15E288252900}'] + ['{FA01363F-6B40-482F-971E-4A085535EFC8}'] procedure Listen; procedure Close; function Accept( const fnAccepting: TProc): ITransport; + function Configuration : IThriftConfiguration; end; TServerTransportImpl = class( TInterfacedObject, IServerTransport) - protected + strict private + FConfig : IThriftConfiguration; + strict protected + function Configuration : IThriftConfiguration; procedure Listen; virtual; abstract; procedure Close; virtual; abstract; - function Accept( const fnAccepting: TProc): ITransport; virtual; abstract; + function Accept( const fnAccepting: TProc): ITransport; virtual; abstract; + public + constructor Create( const aConfig : IThriftConfiguration); end; ITransportFactory = interface ['{DD809446-000F-49E1-9BFF-E0D0DC76A9D7}'] - function GetTransport( const ATrans: ITransport): ITransport; + function GetTransport( const aTransport: ITransport): ITransport; end; - TTransportFactoryImpl = class( TInterfacedObject, ITransportFactory) - function GetTransport( const ATrans: ITransport): ITransport; virtual; + TTransportFactoryImpl = class ( TInterfacedObject, ITransportFactory) + strict protected + function GetTransport( const aTransport: ITransport): ITransport; virtual; end; - TTcpSocketStreamImpl = class( TThriftStreamImpl ) + + TTcpSocketStreamImpl = class( TThriftStreamImpl) {$IFDEF OLD_SOCKETS} - private type + strict private type TWaitForData = ( wfd_HaveData, wfd_Timeout, wfd_Error); - private + strict private FTcpClient : TCustomIpClient; FTimeout : Integer; function Select( ReadReady, WriteReady, ExceptFlag: PBoolean; @@ -214,10 +278,10 @@ TTcpSocketStreamImpl = class( TThriftStreamImpl ) var wsaError, bytesReady : Integer): TWaitForData; {$ELSE} FTcpClient: TSocket; - protected const + strict protected const SLEEP_TIME = 200; {$ENDIF} - protected + strict protected procedure Write( const pBuf : Pointer; offset, count: Integer); override; function Read( const pBuf : Pointer; const buflen : Integer; offset: Integer; count: Integer): Integer; override; procedure Open; override; @@ -228,9 +292,9 @@ TTcpSocketStreamImpl = class( TThriftStreamImpl ) function ToArray: TBytes; override; public {$IFDEF OLD_SOCKETS} - constructor Create( const ATcpClient: TCustomIpClient; const aTimeout : Integer = 0); + constructor Create( const aTcpClient: TCustomIpClient; const aTimeout : Integer = DEFAULT_THRIFT_TIMEOUT); {$ELSE} - constructor Create( const ATcpClient: TSocket; const aTimeout : Longword = 0); + constructor Create( const aTcpClient: TSocket; const aTimeout : Longword = DEFAULT_THRIFT_TIMEOUT); {$ENDIF} end; @@ -242,35 +306,37 @@ TTcpSocketStreamImpl = class( TThriftStreamImpl ) property OutputStream : IThriftStream read GetOutputStream; end; - TStreamTransportImpl = class( TTransportImpl, IStreamTransport) - protected + TStreamTransportImpl = class( TEndpointTransportBase, IStreamTransport) + strict protected FInputStream : IThriftStream; FOutputStream : IThriftStream; - protected + strict protected function GetIsOpen: Boolean; override; function GetInputStream: IThriftStream; function GetOutputStream: IThriftStream; - public - property InputStream : IThriftStream read GetInputStream; - property OutputStream : IThriftStream read GetOutputStream; + strict protected procedure Open; override; procedure Close; override; procedure Flush; override; function Read( const pBuf : Pointer; const buflen : Integer; off: Integer; len: Integer): Integer; override; procedure Write( const pBuf : Pointer; off, len : Integer); override; - constructor Create( const AInputStream : IThriftStream; const AOutputStream : IThriftStream); + public + constructor Create( const aInputStream, aOutputStream : IThriftStream; const aConfig : IThriftConfiguration = nil); reintroduce; destructor Destroy; override; + + property InputStream : IThriftStream read GetInputStream; + property OutputStream : IThriftStream read GetOutputStream; end; TBufferedStreamImpl = class( TThriftStreamImpl) - private + strict private FStream : IThriftStream; FBufSize : Integer; FReadBuffer : TMemoryStream; FWriteBuffer : TMemoryStream; - protected + strict protected procedure Write( const pBuf : Pointer; offset: Integer; count: Integer); override; function Read( const pBuf : Pointer; const buflen : Integer; offset: Integer; count: Integer): Integer; override; procedure Open; override; @@ -278,13 +344,15 @@ TBufferedStreamImpl = class( TThriftStreamImpl) procedure Flush; override; function IsOpen: Boolean; override; function ToArray: TBytes; override; + function Size : Int64; override; + function Position : Int64; override; public - constructor Create( const AStream: IThriftStream; ABufSize: Integer); + constructor Create( const aStream: IThriftStream; const aBufSize : Integer); destructor Destroy; override; end; TServerSocketImpl = class( TServerTransportImpl) - private + strict private {$IFDEF OLD_SOCKETS} FServer : TTcpServer; FPort : Integer; @@ -294,46 +362,52 @@ TServerSocketImpl = class( TServerTransportImpl) {$ENDIF} FUseBufferedSocket : Boolean; FOwnsServer : Boolean; - protected + + strict protected function Accept( const fnAccepting: TProc) : ITransport; override; + public -{$IFDEF OLD_SOCKETS} - constructor Create( const AServer: TTcpServer; AClientTimeout: Integer = 0); overload; - constructor Create( APort: Integer; AClientTimeout: Integer = 0; AUseBufferedSockets: Boolean = FALSE); overload; -{$ELSE} - constructor Create( const AServer: TServerSocket; AClientTimeout: Longword = 0); overload; - constructor Create( APort: Integer; AClientTimeout: Longword = 0; AUseBufferedSockets: Boolean = FALSE); overload; -{$ENDIF} + {$IFDEF OLD_SOCKETS} + constructor Create( const aServer: TTcpServer; const aClientTimeout : Integer = DEFAULT_THRIFT_TIMEOUT; const aConfig : IThriftConfiguration = nil); overload; + constructor Create( const aPort: Integer; const aClientTimeout: Integer = DEFAULT_THRIFT_TIMEOUT; aUseBufferedSockets: Boolean = FALSE; const aConfig : IThriftConfiguration = nil); overload; + {$ELSE} + constructor Create( const aServer: TServerSocket; const aClientTimeout: Longword = DEFAULT_THRIFT_TIMEOUT; const aConfig : IThriftConfiguration = nil); overload; + constructor Create( const aPort: Integer; const aClientTimeout: Longword = DEFAULT_THRIFT_TIMEOUT; aUseBufferedSockets: Boolean = FALSE; const aConfig : IThriftConfiguration = nil); overload; + {$ENDIF} + destructor Destroy; override; procedure Listen; override; procedure Close; override; end; - TBufferedTransportImpl = class( TTransportImpl ) - private + TBufferedTransportImpl = class( TLayeredTransportBase) + strict private FInputBuffer : IThriftStream; FOutputBuffer : IThriftStream; - FTransport : IStreamTransport; FBufSize : Integer; procedure InitBuffers; - function GetUnderlyingTransport: ITransport; - protected + strict protected function GetIsOpen: Boolean; override; procedure Flush; override; public + type + TFactory = class( TTransportFactoryImpl ) + public + function GetTransport( const aTransport: ITransport): ITransport; override; + end; + + constructor Create( const aTransport : IStreamTransport; const aBufSize: Integer = 1024); procedure Open(); override; procedure Close(); override; function Read( const pBuf : Pointer; const buflen : Integer; off: Integer; len: Integer): Integer; override; procedure Write( const pBuf : Pointer; off, len : Integer); override; - constructor Create( const ATransport : IStreamTransport ); overload; - constructor Create( const ATransport : IStreamTransport; ABufSize: Integer); overload; - property UnderlyingTransport: ITransport read GetUnderlyingTransport; + procedure CheckReadBytesAvailable( const value : Int64); override; property IsOpen: Boolean read GetIsOpen; end; TSocketImpl = class(TStreamTransportImpl) - private + strict private {$IFDEF OLD_SOCKETS} FClient : TCustomIpClient; {$ELSE} @@ -349,18 +423,19 @@ TSocketImpl = class(TStreamTransportImpl) {$ENDIF} procedure InitSocket; - protected + strict protected function GetIsOpen: Boolean; override; public - procedure Open; override; {$IFDEF OLD_SOCKETS} - constructor Create( const AClient : TCustomIpClient; aOwnsClient : Boolean; ATimeout: Integer = 0); overload; - constructor Create( const AHost: string; APort: Integer; ATimeout: Integer = 0); overload; + constructor Create( const aClient : TCustomIpClient; const aOwnsClient : Boolean; const aTimeout: Integer = DEFAULT_THRIFT_TIMEOUT; const aConfig : IThriftConfiguration = nil); overload; + constructor Create( const aHost: string; const aPort: Integer; const aTimeout: Integer = DEFAULT_THRIFT_TIMEOUT; const aConfig : IThriftConfiguration = nil); overload; {$ELSE} - constructor Create(const AClient: TSocket; aOwnsClient: Boolean); overload; - constructor Create( const AHost: string; APort: Integer; ATimeout: Longword = 0); overload; + constructor Create(const aClient: TSocket; const aOwnsClient: Boolean; const aConfig : IThriftConfiguration = nil); overload; + constructor Create( const aHost: string; const aPort: Integer; const aTimeout: Longword = DEFAULT_THRIFT_TIMEOUT; const aConfig : IThriftConfiguration = nil); overload; {$ENDIF} destructor Destroy; override; + + procedure Open; override; procedure Close; override; {$IFDEF OLD_SOCKETS} property TcpClient: TCustomIpClient read FClient; @@ -371,91 +446,70 @@ TSocketImpl = class(TStreamTransportImpl) property Port: Integer read FPort; end; - TFramedTransportImpl = class( TTransportImpl) - private const - FHeaderSize : Integer = 4; - private class var - FHeader_Dummy : array of Byte; - protected - FTransport : ITransport; + TFramedTransportImpl = class( TLayeredTransportBase) + strict protected type + TFramedHeader = Int32; + strict protected FWriteBuffer : TMemoryStream; FReadBuffer : TMemoryStream; procedure InitWriteBuffer; procedure ReadFrame; - public - type - TFactory = class( TTransportFactoryImpl ) - public - function GetTransport( const ATrans: ITransport): ITransport; override; - end; - - {$IFDEF HAVE_CLASS_CTOR} - class constructor Create; - {$ENDIF} - - constructor Create; overload; - constructor Create( const ATrans: ITransport); overload; - destructor Destroy; override; procedure Open(); override; - function GetIsOpen: Boolean; override; + function GetIsOpen: Boolean; override; procedure Close(); override; function Read( const pBuf : Pointer; const buflen : Integer; off: Integer; len: Integer): Integer; override; procedure Write( const pBuf : Pointer; off, len : Integer); override; + procedure CheckReadBytesAvailable( const value : Int64); override; procedure Flush; override; + + public + type + TFactory = class( TTransportFactoryImpl ) + public + function GetTransport( const aTransport: ITransport): ITransport; override; + end; + + constructor Create( const aTransport: ITransport); overload; + destructor Destroy; override; end; -{$IFNDEF HAVE_CLASS_CTOR} -procedure TFramedTransportImpl_Initialize; -{$ENDIF} const - DEFAULT_THRIFT_TIMEOUT = 5 * 1000; // ms - + DEFAULT_THRIFT_SECUREPROTOCOLS = [ TSecureProtocol.TLS_1_1, TSecureProtocol.TLS_1_2]; implementation -{ TTransportImpl } -procedure TTransportImpl.Flush; +{ TTransportBase } + +procedure TTransportBase.Flush; begin // nothing to do end; -function TTransportImpl.Peek: Boolean; +function TTransportBase.Peek: Boolean; begin Result := IsOpen; end; -function TTransportImpl.Read(var buf: TBytes; off: Integer; len: Integer): Integer; +function TTransportBase.Read(var buf: TBytes; off: Integer; len: Integer): Integer; begin if Length(buf) > 0 then result := Read( @buf[0], Length(buf), off, len) else result := 0; end; -function TTransportImpl.ReadAll(var buf: TBytes; off: Integer; len: Integer): Integer; +function TTransportBase.ReadAll(var buf: TBytes; off: Integer; len: Integer): Integer; begin if Length(buf) > 0 then result := ReadAll( @buf[0], Length(buf), off, len) else result := 0; end; -procedure TTransportImpl.Write( const buf: TBytes); -begin - if Length(buf) > 0 - then Write( @buf[0], 0, Length(buf)); -end; - -procedure TTransportImpl.Write( const buf: TBytes; off: Integer; len: Integer); -begin - if Length(buf) > 0 - then Write( @buf[0], off, len); -end; - -function TTransportImpl.ReadAll(const pBuf : Pointer; const buflen : Integer; off: Integer; len: Integer): Integer; +function TTransportBase.ReadAll(const pBuf : Pointer; const buflen : Integer; off: Integer; len: Integer): Integer; var ret : Integer; begin result := 0; @@ -467,205 +521,162 @@ function TTransportImpl.ReadAll(const pBuf : Pointer; const buflen : Integer; of end; end; -procedure TTransportImpl.Write( const pBuf : Pointer; len : Integer); +procedure TTransportBase.Write( const buf: TBytes); begin - Self.Write( pBuf, 0, len); + if Length(buf) > 0 + then Write( @buf[0], 0, Length(buf)); end; -{ THTTPClientImpl } +procedure TTransportBase.Write( const buf: TBytes; off: Integer; len: Integer); +begin + if Length(buf) > 0 + then Write( @buf[0], off, len); +end; -constructor THTTPClientImpl.Create(const AUri: string); +procedure TTransportBase.Write( const pBuf : Pointer; len : Integer); begin - inherited Create; - FUri := AUri; + Self.Write( pBuf, 0, len); +end; - // defaults according to MSDN - FDnsResolveTimeout := 0; // no timeout - FConnectionTimeout := 60 * 1000; - FSendTimeout := 30 * 1000; - FReadTimeout := 30 * 1000; - FCustomHeaders := TThriftDictionaryImpl.Create; - FOutputStream := TThriftStreamAdapterDelphi.Create( TMemoryStream.Create, True); -end; +{ TEndpointTransportBase } -function THTTPClientImpl.CreateRequest: IXMLHTTPRequest; -var - pair : TPair; - srvHttp : IServerXMLHTTPRequest; +constructor TEndpointTransportBase.Create( const aConfig : IThriftConfiguration); begin - {$IF CompilerVersion >= 21.0} - Result := CoServerXMLHTTP.Create; - {$ELSE} - Result := CoXMLHTTPRequest.Create; - {$IFEND} - - // setting a timeout value to 0 (zero) means "no timeout" for that setting - if Supports( result, IServerXMLHTTPRequest, srvHttp) - then srvHttp.setTimeouts( DnsResolveTimeout, ConnectionTimeout, SendTimeout, ReadTimeout); + inherited Create; - Result.open('POST', FUri, False, '', ''); - Result.setRequestHeader( 'Content-Type', 'application/x-thrift'); - Result.setRequestHeader( 'Accept', 'application/x-thrift'); - Result.setRequestHeader( 'User-Agent', 'Delphi/IHTTPClient'); + if aConfig <> nil + then FConfiguration := aConfig + else FConfiguration := TThriftConfigurationImpl.Create; - for pair in FCustomHeaders do begin - Result.setRequestHeader( pair.Key, pair.Value ); - end; + ResetConsumedMessageSize; end; -destructor THTTPClientImpl.Destroy; -begin - Close; - inherited; -end; -function THTTPClientImpl.GetDnsResolveTimeout: Integer; +function TEndpointTransportBase.Configuration : IThriftConfiguration; begin - Result := FDnsResolveTimeout; + result := FConfiguration; end; -procedure THTTPClientImpl.SetDnsResolveTimeout(const Value: Integer); -begin - FDnsResolveTimeout := Value; -end; -function THTTPClientImpl.GetConnectionTimeout: Integer; +function TEndpointTransportBase.MaxMessageSize : Integer; begin - Result := FConnectionTimeout; + ASSERT( Configuration <> nil); + result := Configuration.MaxMessageSize; end; -procedure THTTPClientImpl.SetConnectionTimeout(const Value: Integer); -begin - FConnectionTimeout := Value; -end; -function THTTPClientImpl.GetSendTimeout: Integer; +procedure TEndpointTransportBase.ResetConsumedMessageSize( const newSize : Int64); +// Resets RemainingMessageSize to the configured maximum begin - Result := FSendTimeout; -end; + // full reset + if newSize < 0 then begin + FKnownMessageSize := MaxMessageSize; + FRemainingMessageSize := MaxMessageSize; + Exit; + end; -procedure THTTPClientImpl.SetSendTimeout(const Value: Integer); -begin - FSendTimeout := Value; -end; + // update only: message size can shrink, but not grow + ASSERT( KnownMessageSize <= MaxMessageSize); + if newSize > KnownMessageSize + then raise TTransportExceptionEndOfFile.Create('MaxMessageSize reached'); -function THTTPClientImpl.GetReadTimeout: Integer; -begin - Result := FReadTimeout; + FKnownMessageSize := newSize; + FRemainingMessageSize := newSize; end; -procedure THTTPClientImpl.SetReadTimeout(const Value: Integer); + +procedure TEndpointTransportBase.UpdateKnownMessageSize( const size : Int64); +// Updates RemainingMessageSize to reflect then known real message size (e.g. framed transport). +// Will throw if we already consumed too many bytes. +var consumed : Int64; begin - FReadTimeout := Value; + consumed := KnownMessageSize - RemainingMessageSize; + ResetConsumedMessageSize(size); + CountConsumedMessageBytes(consumed); end; -function THTTPClientImpl.GetCustomHeaders: IThriftDictionary; + +procedure TEndpointTransportBase.CheckReadBytesAvailable( const numBytes : Int64); +// Throws if there are not enough bytes in the input stream to satisfy a read of numBytes bytes of data begin - Result := FCustomHeaders; + if RemainingMessageSize < numBytes + then raise TTransportExceptionEndOfFile.Create('MaxMessageSize reached'); end; -function THTTPClientImpl.GetIsOpen: Boolean; + +procedure TEndpointTransportBase.CountConsumedMessageBytes( const numBytes : Int64); +// Consumes numBytes from the RemainingMessageSize. begin - Result := True; + if (RemainingMessageSize >= numBytes) + then Dec( FRemainingMessageSize, numBytes) + else begin + FRemainingMessageSize := 0; + raise TTransportExceptionEndOfFile.Create('MaxMessageSize reached'); + end; end; -procedure THTTPClientImpl.Open; +{ TLayeredTransportBase } + +constructor TLayeredTransportBase.Create( const aTransport: T); begin - FOutputStream := TThriftStreamAdapterDelphi.Create( TMemoryStream.Create, True); + inherited Create; + FTransport := aTransport; end; -procedure THTTPClientImpl.Close; +function TLayeredTransportBase.GetUnderlyingTransport: ITransport; begin - FInputStream := nil; - FOutputStream := nil; + result := InnerTransport; end; -procedure THTTPClientImpl.Flush; +function TLayeredTransportBase.Configuration : IThriftConfiguration; begin - try - SendRequest; - finally - FOutputStream := nil; - FOutputStream := TThriftStreamAdapterDelphi.Create( TMemoryStream.Create, True); - ASSERT( FOutputStream <> nil); - end; + result := InnerTransport.Configuration; end; -function THTTPClientImpl.Read( const pBuf : Pointer; const buflen : Integer; off: Integer; len: Integer): Integer; +procedure TLayeredTransportBase.UpdateKnownMessageSize( const size : Int64); begin - if FInputStream = nil then begin - raise TTransportExceptionNotOpen.Create('No request has been sent'); - end; - - try - Result := FInputStream.Read( pBuf, buflen, off, len) - except - on E: Exception - do raise TTransportExceptionUnknown.Create(E.Message); - end; + InnerTransport.UpdateKnownMessageSize( size); end; -procedure THTTPClientImpl.SendRequest; -var - xmlhttp : IXMLHTTPRequest; - ms : TMemoryStream; - a : TBytes; - len : Integer; -begin - xmlhttp := CreateRequest; - ms := TMemoryStream.Create; - try - a := FOutputStream.ToArray; - len := Length(a); - if len > 0 then begin - ms.WriteBuffer( Pointer(@a[0])^, len); - end; - ms.Position := 0; - xmlhttp.send( IUnknown( TStreamAdapter.Create( ms, soReference ))); - FInputStream := nil; - FInputStream := TThriftStreamAdapterCOM.Create( IUnknown( xmlhttp.responseStream) as IStream); - finally - ms.Free; - end; +function TLayeredTransportBase.MaxMessageSize : Integer; +begin + result := InnerTransport.MaxMessageSize; end; -procedure THTTPClientImpl.Write( const pBuf : Pointer; off, len : Integer); + +procedure TLayeredTransportBase.ResetConsumedMessageSize( const knownSize : Int64 = -1); begin - FOutputStream.Write( pBuf, off, len); + InnerTransport.ResetConsumedMessageSize( knownSize); end; -{ TTransportException } -function TTransportException.GetType: TExceptionType; +procedure TLayeredTransportBase.CheckReadBytesAvailable( const numBytes : Int64); begin - if Self is TTransportExceptionNotOpen then Result := TExceptionType.NotOpen - else if Self is TTransportExceptionAlreadyOpen then Result := TExceptionType.AlreadyOpen - else if Self is TTransportExceptionTimedOut then Result := TExceptionType.TimedOut - else if Self is TTransportExceptionEndOfFile then Result := TExceptionType.EndOfFile - else if Self is TTransportExceptionBadArgs then Result := TExceptionType.BadArgs - else if Self is TTransportExceptionInterrupted then Result := TExceptionType.Interrupted - else Result := TExceptionType.Unknown; + InnerTransport.CheckReadBytesAvailable( numBytes); end; + + +{ TTransportException } + constructor TTransportException.HiddenCreate(const Msg: string); begin inherited Create(Msg); end; -class function TTransportException.Create(AType: TExceptionType): TTransportException; +class function TTransportException.Create(aType: TExceptionType): TTransportException; begin //no inherited; {$WARN SYMBOL_DEPRECATED OFF} - Result := Create(AType, '') + Result := Create(aType, '') {$WARN SYMBOL_DEPRECATED DEFAULT} end; -class function TTransportException.Create(AType: TExceptionType; - const msg: string): TTransportException; +class function TTransportException.Create(aType: TExceptionType; const msg: string): TTransportException; begin - case AType of + case aType of TExceptionType.NotOpen: Result := TTransportExceptionNotOpen.Create(msg); TExceptionType.AlreadyOpen: Result := TTransportExceptionAlreadyOpen.Create(msg); TExceptionType.TimedOut: Result := TTransportExceptionTimedOut.Create(msg); @@ -673,6 +684,7 @@ class function TTransportException.Create(AType: TExceptionType; TExceptionType.BadArgs: Result := TTransportExceptionBadArgs.Create(msg); TExceptionType.Interrupted: Result := TTransportExceptionInterrupted.Create(msg); else + ASSERT( TExceptionType.Unknown = aType); Result := TTransportExceptionUnknown.Create(msg); end; end; @@ -689,42 +701,105 @@ constructor TTransportExceptionSpecialized.Create(const Msg: string); inherited HiddenCreate(Msg); end; +{ specialized TTransportExceptions } + +class function TTransportExceptionUnknown.GetType: TTransportException.TExceptionType; +begin + result := TExceptionType.Unknown; +end; + +class function TTransportExceptionNotOpen.GetType: TTransportException.TExceptionType; +begin + result := TExceptionType.NotOpen; +end; + +class function TTransportExceptionAlreadyOpen.GetType: TTransportException.TExceptionType; +begin + result := TExceptionType.AlreadyOpen; +end; + +class function TTransportExceptionTimedOut.GetType: TTransportException.TExceptionType; +begin + result := TExceptionType.TimedOut; +end; + +class function TTransportExceptionEndOfFile.GetType: TTransportException.TExceptionType; +begin + result := TExceptionType.EndOfFile; +end; + +class function TTransportExceptionBadArgs.GetType: TTransportException.TExceptionType; +begin + result := TExceptionType.BadArgs; +end; + +class function TTransportExceptionInterrupted.GetType: TTransportException.TExceptionType; +begin + result := TExceptionType.Interrupted; +end; + +class function TTransportExceptionCorruptedData.GetType: TTransportException.TExceptionType; +begin + result := TExceptionType.CorruptedData; +end; + { TTransportFactoryImpl } -function TTransportFactoryImpl.GetTransport( const ATrans: ITransport): ITransport; +function TTransportFactoryImpl.GetTransport( const aTransport: ITransport): ITransport; begin - Result := ATrans; + Result := aTransport; end; -{ TServerSocket } -{$IFDEF OLD_SOCKETS} -constructor TServerSocketImpl.Create( const AServer: TTcpServer; AClientTimeout: Integer); +{ TServerTransportImpl } + +constructor TServerTransportImpl.Create( const aConfig : IThriftConfiguration); begin inherited Create; - FServer := AServer; - FClientTimeout := AClientTimeout; + if aConfig <> nil + then FConfig := aConfig + else FConfig := TThriftConfigurationImpl.Create; end; -{$ELSE} -constructor TServerSocketImpl.Create( const AServer: TServerSocket; AClientTimeout: Longword); + +function TServerTransportImpl.Configuration : IThriftConfiguration; begin - inherited Create; - FServer := AServer; - FServer.RecvTimeout := AClientTimeout; - FServer.SendTimeout := AClientTimeout; + result := FConfig; end; + +{ TServerSocket } + +{$IFDEF OLD_SOCKETS} +constructor TServerSocketImpl.Create( const aServer: TTcpServer; const aClientTimeout : Integer; const aConfig : IThriftConfiguration); +{$ELSE} +constructor TServerSocketImpl.Create( const aServer: TServerSocket; const aClientTimeout: Longword; const aConfig : IThriftConfiguration); {$ENDIF} +begin + inherited Create( aConfig); + FServer := aServer; + + +{$IFDEF OLD_SOCKETS} + FClientTimeout := aClientTimeout; +{$ELSE} + FServer.RecvTimeout := aClientTimeout; + FServer.SendTimeout := aClientTimeout; +{$ENDIF} +end; + {$IFDEF OLD_SOCKETS} -constructor TServerSocketImpl.Create(APort, AClientTimeout: Integer; AUseBufferedSockets: Boolean); +constructor TServerSocketImpl.Create( const aPort: Integer; const aClientTimeout: Integer; aUseBufferedSockets: Boolean; const aConfig : IThriftConfiguration); {$ELSE} -constructor TServerSocketImpl.Create(APort: Integer; AClientTimeout: Longword; AUseBufferedSockets: Boolean); +constructor TServerSocketImpl.Create( const aPort: Integer; const aClientTimeout: Longword; aUseBufferedSockets: Boolean; const aConfig : IThriftConfiguration); {$ENDIF} begin - inherited Create; + inherited Create( aConfig); + {$IFDEF OLD_SOCKETS} - FPort := APort; - FClientTimeout := AClientTimeout; + FPort := aPort; + FClientTimeout := aClientTimeout; + + FOwnsServer := True; FServer := TTcpServer.Create( nil ); FServer.BlockMode := bmBlocking; {$IF CompilerVersion >= 21.0} @@ -733,10 +808,11 @@ constructor TServerSocketImpl.Create(APort: Integer; AClientTimeout: Longword; A FServer.LocalPort := IntToStr( FPort); {$IFEND} {$ELSE} - FServer := TServerSocket.Create(APort, AClientTimeout, AClientTimeout); -{$ENDIF} - FUseBufferedSocket := AUseBufferedSockets; FOwnsServer := True; + FServer := TServerSocket.Create(aPort, aClientTimeout, aClientTimeout); +{$ENDIF} + + FUseBufferedSocket := aUseBufferedSockets; end; destructor TServerSocketImpl.Destroy; @@ -780,7 +856,7 @@ function TServerSocketImpl.Accept( const fnAccepting: TProc): ITransport; Exit; end; - trans := TSocketImpl.Create( client, TRUE, FClientTimeout); + trans := TSocketImpl.Create( client, TRUE, FClientTimeout, Configuration); client := nil; // trans owns it now if FUseBufferedSocket @@ -799,7 +875,7 @@ function TServerSocketImpl.Accept( const fnAccepting: TProc): ITransport; client := FServer.Accept; try - trans := TSocketImpl.Create(client, True); + trans := TSocketImpl.Create(client, TRUE, Configuration); client := nil; if FUseBufferedSocket then @@ -848,37 +924,36 @@ procedure TServerSocketImpl.Close; { TSocket } {$IFDEF OLD_SOCKETS} -constructor TSocketImpl.Create( const AClient : TCustomIpClient; aOwnsClient : Boolean; ATimeout: Integer = 0); -var stream : IThriftStream; -begin - FClient := AClient; - FTimeout := ATimeout; - FOwnsClient := aOwnsClient; - stream := TTcpSocketStreamImpl.Create( FClient, FTimeout); - inherited Create( stream, stream); -end; +constructor TSocketImpl.Create( const aClient : TCustomIpClient; const aOwnsClient : Boolean; const aTimeout: Integer; const aConfig : IThriftConfiguration); {$ELSE} -constructor TSocketImpl.Create(const AClient: TSocket; aOwnsClient: Boolean); +constructor TSocketImpl.Create(const aClient: TSocket; const aOwnsClient: Boolean; const aConfig : IThriftConfiguration); +{$ENDIF} var stream : IThriftStream; begin - FClient := AClient; - FTimeout := AClient.RecvTimeout; + FClient := aClient; FOwnsClient := aOwnsClient; - stream := TTcpSocketStreamImpl.Create(FClient, FTimeout); - inherited Create(stream, stream); -end; + +{$IFDEF OLD_SOCKETS} + FTimeout := aTimeout; +{$ELSE} + FTimeout := aClient.RecvTimeout; {$ENDIF} + stream := TTcpSocketStreamImpl.Create( FClient, FTimeout); + inherited Create( stream, stream, aConfig); +end; + + {$IFDEF OLD_SOCKETS} -constructor TSocketImpl.Create(const AHost: string; APort, ATimeout: Integer); +constructor TSocketImpl.Create(const aHost: string; const aPort, aTimeout: Integer; const aConfig : IThriftConfiguration); {$ELSE} -constructor TSocketImpl.Create(const AHost: string; APort: Integer; ATimeout: Longword); +constructor TSocketImpl.Create(const aHost: string; const aPort : Integer; const aTimeout: Longword; const aConfig : IThriftConfiguration); {$ENDIF} begin - inherited Create(nil,nil); - FHost := AHost; - FPort := APort; - FTimeout := ATimeout; + inherited Create(nil,nil, aConfig); + FHost := aHost; + FPort := aPort; + FTimeout := aTimeout; InitSocket; end; @@ -973,11 +1048,11 @@ procedure TBufferedStreamImpl.Close; FWriteBuffer := nil; end; -constructor TBufferedStreamImpl.Create( const AStream: IThriftStream; ABufSize: Integer); +constructor TBufferedStreamImpl.Create( const aStream: IThriftStream; const aBufSize : Integer); begin inherited Create; - FStream := AStream; - FBufSize := ABufSize; + FStream := aStream; + FBufSize := aBufSize; FReadBuffer := TMemoryStream.Create; FWriteBuffer := TMemoryStream.Create; end; @@ -1052,14 +1127,13 @@ function TBufferedStreamImpl.Read( const pBuf : Pointer; const buflen : Integer; end; end; + function TBufferedStreamImpl.ToArray: TBytes; var len : Integer; begin - len := 0; - - if IsOpen then begin - len := FReadBuffer.Size; - end; + if IsOpen + then len := FReadBuffer.Size + else len := 0; SetLength( Result, len); @@ -1085,13 +1159,26 @@ procedure TBufferedStreamImpl.Write( const pBuf : Pointer; offset: Integer; coun end; end; + +function TBufferedStreamImpl.Size : Int64; +begin + result := FReadBuffer.Size; +end; + + +function TBufferedStreamImpl.Position : Int64; +begin + result := FReadBuffer.Position; +end; + + { TStreamTransportImpl } -constructor TStreamTransportImpl.Create( const AInputStream : IThriftStream; const AOutputStream : IThriftStream); +constructor TStreamTransportImpl.Create( const aInputStream, aOutputStream : IThriftStream; const aConfig : IThriftConfiguration); begin - inherited Create; - FInputStream := AInputStream; - FOutputStream := AOutputStream; + inherited Create( aConfig); + FInputStream := aInputStream; + FOutputStream := aOutputStream; end; destructor TStreamTransportImpl.Destroy; @@ -1133,48 +1220,41 @@ function TStreamTransportImpl.GetOutputStream: IThriftStream; procedure TStreamTransportImpl.Open; begin - + // nothing to do end; function TStreamTransportImpl.Read( const pBuf : Pointer; const buflen : Integer; off: Integer; len: Integer): Integer; begin - if FInputStream = nil then begin - raise TTransportExceptionNotOpen.Create('Cannot read from null inputstream' ); - end; + if FInputStream = nil + then raise TTransportExceptionNotOpen.Create('Cannot read from null inputstream' ); Result := FInputStream.Read( pBuf,buflen, off, len ); + CountConsumedMessageBytes( result); end; procedure TStreamTransportImpl.Write( const pBuf : Pointer; off, len : Integer); begin - if FOutputStream = nil then begin - raise TTransportExceptionNotOpen.Create('Cannot write to null outputstream' ); - end; + if FOutputStream = nil + then raise TTransportExceptionNotOpen.Create('Cannot write to null outputstream' ); FOutputStream.Write( pBuf, off, len ); end; { TBufferedTransportImpl } -constructor TBufferedTransportImpl.Create( const ATransport: IStreamTransport); +constructor TBufferedTransportImpl.Create( const aTransport : IStreamTransport; const aBufSize: Integer); begin - //no inherited; - Create( ATransport, 1024 ); -end; - -constructor TBufferedTransportImpl.Create( const ATransport: IStreamTransport; ABufSize: Integer); -begin - inherited Create; - FTransport := ATransport; - FBufSize := ABufSize; + ASSERT( aTransport <> nil); + inherited Create( aTransport); + FBufSize := aBufSize; InitBuffers; end; procedure TBufferedTransportImpl.Close; begin - FTransport.Close; + InnerTransport.Close; FInputBuffer := nil; - FOutputBuffer := nil; + FOutputBuffer := nil; end; procedure TBufferedTransportImpl.Flush; @@ -1186,36 +1266,30 @@ procedure TBufferedTransportImpl.Flush; function TBufferedTransportImpl.GetIsOpen: Boolean; begin - Result := FTransport.IsOpen; -end; - -function TBufferedTransportImpl.GetUnderlyingTransport: ITransport; -begin - Result := FTransport; + Result := InnerTransport.IsOpen; end; procedure TBufferedTransportImpl.InitBuffers; begin - if FTransport.InputStream <> nil then begin - FInputBuffer := TBufferedStreamImpl.Create( FTransport.InputStream, FBufSize ); + if InnerTransport.InputStream <> nil then begin + FInputBuffer := TBufferedStreamImpl.Create( InnerTransport.InputStream, FBufSize ); end; - if FTransport.OutputStream <> nil then begin - FOutputBuffer := TBufferedStreamImpl.Create( FTransport.OutputStream, FBufSize ); + if InnerTransport.OutputStream <> nil then begin + FOutputBuffer := TBufferedStreamImpl.Create( InnerTransport.OutputStream, FBufSize ); end; end; procedure TBufferedTransportImpl.Open; begin - FTransport.Open; + InnerTransport.Open; InitBuffers; // we need to get the buffers to match FTransport substreams again end; function TBufferedTransportImpl.Read( const pBuf : Pointer; const buflen : Integer; off: Integer; len: Integer): Integer; begin - Result := 0; - if FInputBuffer <> nil then begin - Result := FInputBuffer.Read( pBuf,buflen, off, len ); - end; + if FInputBuffer <> nil + then Result := FInputBuffer.Read( pBuf,buflen, off, len ) + else Result := 0; end; procedure TBufferedTransportImpl.Write( const pBuf : Pointer; off, len : Integer); @@ -1225,65 +1299,69 @@ procedure TBufferedTransportImpl.Write( const pBuf : Pointer; off, len : Integer end; end; -{ TFramedTransportImpl } - -{$IFDEF HAVE_CLASS_CTOR} -class constructor TFramedTransportImpl.Create; +procedure TBufferedTransportImpl.CheckReadBytesAvailable( const value : Int64); +var buffered, need : Int64; begin - SetLength( FHeader_Dummy, FHeaderSize); - FillChar( FHeader_Dummy[0], Length( FHeader_Dummy) * SizeOf( Byte ), 0); -end; -{$ELSE} -procedure TFramedTransportImpl_Initialize; -begin - SetLength( TFramedTransportImpl.FHeader_Dummy, TFramedTransportImpl.FHeaderSize); - FillChar( TFramedTransportImpl.FHeader_Dummy[0], - Length( TFramedTransportImpl.FHeader_Dummy) * SizeOf( Byte ), 0); -end; -{$ENDIF} + need := value; -constructor TFramedTransportImpl.Create; -begin - inherited Create; - InitWriteBuffer; + // buffered bytes + buffered := FInputBuffer.Size - FInputBuffer.Position; + if buffered < need + then InnerTransport.CheckReadBytesAvailable( need - buffered); end; -procedure TFramedTransportImpl.Close; + +{ TBufferedTransportImpl.TFactory } + +function TBufferedTransportImpl.TFactory.GetTransport( const aTransport: ITransport): ITransport; begin - FTransport.Close; + Result := TFramedTransportImpl.Create( aTransport); end; -constructor TFramedTransportImpl.Create( const ATrans: ITransport); + +{ TFramedTransportImpl } + +constructor TFramedTransportImpl.Create( const aTransport: ITransport); begin - inherited Create; + ASSERT( aTransport <> nil); + inherited Create( aTransport); + InitWriteBuffer; - FTransport := ATrans; end; destructor TFramedTransportImpl.Destroy; begin FWriteBuffer.Free; + FWriteBuffer := nil; FReadBuffer.Free; + FReadBuffer := nil; inherited; end; +procedure TFramedTransportImpl.Close; +begin + InnerTransport.Close; +end; + procedure TFramedTransportImpl.Flush; var buf : TBytes; len : Integer; - data_len : Integer; - + data_len : Int64; begin + if not IsOpen + then raise TTransportExceptionNotOpen.Create('not open'); + len := FWriteBuffer.Size; SetLength( buf, len); if len > 0 then begin System.Move( FWriteBuffer.Memory^, buf[0], len ); end; - data_len := len - FHeaderSize; - if (data_len < 0) then begin - raise TTransportExceptionUnknown.Create('TFramedTransport.Flush: data_len < 0' ); - end; + data_len := len - SizeOf(TFramedHeader); + if (0 > data_len) or (data_len > Configuration.MaxFrameSize) + then raise TTransportExceptionUnknown.Create('TFramedTransport.Flush: invalid frame size ('+IntToStr(data_len)+')') + else UpdateKnownMessageSize( len); InitWriteBuffer; @@ -1292,13 +1370,13 @@ procedure TFramedTransportImpl.Flush; buf[2] := Byte($FF and (data_len shr 8)); buf[3] := Byte($FF and data_len); - FTransport.Write( buf, 0, len ); - FTransport.Flush; + InnerTransport.Write( buf, 0, len ); + InnerTransport.Flush; end; function TFramedTransportImpl.GetIsOpen: Boolean; begin - Result := FTransport.IsOpen; + Result := InnerTransport.IsOpen; end; type @@ -1306,16 +1384,17 @@ TAccessMemoryStream = class(TMemoryStream) end; procedure TFramedTransportImpl.InitWriteBuffer; +const DUMMY_HEADER : TFramedHeader = 0; begin - FWriteBuffer.Free; + FreeAndNil( FWriteBuffer); FWriteBuffer := TMemoryStream.Create; TAccessMemoryStream(FWriteBuffer).Capacity := 1024; - FWriteBuffer.Write( Pointer(@FHeader_Dummy[0])^, FHeaderSize); + FWriteBuffer.Write( DUMMY_HEADER, SizeOf(DUMMY_HEADER)); end; procedure TFramedTransportImpl.Open; begin - FTransport.Open; + InnerTransport.Open; end; function TFramedTransportImpl.Read( const pBuf : Pointer; const buflen : Integer; off: Integer; len: Integer): Integer; @@ -1329,9 +1408,7 @@ function TFramedTransportImpl.Read( const pBuf : Pointer; const buflen : Integer if (FReadBuffer <> nil) and (len > 0) then begin result := FReadBuffer.Read( pTmp^, len); - if result > 0 then begin - Exit; - end; + if result > 0 then Exit; end; ReadFrame; @@ -1342,20 +1419,33 @@ function TFramedTransportImpl.Read( const pBuf : Pointer; const buflen : Integer procedure TFramedTransportImpl.ReadFrame; var - i32rd : TBytes; + i32rd : packed array[0..SizeOf(TFramedHeader)-1] of Byte; size : Integer; buff : TBytes; begin - SetLength( i32rd, FHeaderSize ); - FTransport.ReadAll( i32rd, 0, FHeaderSize); + InnerTransport.ReadAll( @i32rd[0], SizeOf(i32rd), 0, SizeOf(i32rd)); size := ((i32rd[0] and $FF) shl 24) or ((i32rd[1] and $FF) shl 16) or ((i32rd[2] and $FF) shl 8) or (i32rd[3] and $FF); + + if size < 0 then begin + Close(); + raise TTransportExceptionCorruptedData.Create('Read a negative frame size ('+IntToStr(size)+')'); + end; + + if Int64(size) > Int64(Configuration.MaxFrameSize) then begin + Close(); + raise TTransportExceptionCorruptedData.Create('Frame size ('+IntToStr(size)+') larger than allowed maximum ('+IntToStr(Configuration.MaxFrameSize)+')'); + end; + + UpdateKnownMessageSize(size + SizeOf(size)); + SetLength( buff, size ); - FTransport.ReadAll( buff, 0, size ); - FReadBuffer.Free; + InnerTransport.ReadAll( buff, 0, size ); + + FreeAndNil( FReadBuffer); FReadBuffer := TMemoryStream.Create; if Length(buff) > 0 then FReadBuffer.Write( Pointer(@buff[0])^, size ); @@ -1373,11 +1463,24 @@ procedure TFramedTransportImpl.Write( const pBuf : Pointer; off, len : Integer); end; end; + +procedure TFramedTransportImpl.CheckReadBytesAvailable( const value : Int64); +var buffered, need : Int64; +begin + need := value; + + // buffered bytes + buffered := FReadBuffer.Size - FReadBuffer.Position; + if buffered < need + then InnerTransport.CheckReadBytesAvailable( need - buffered); +end; + + { TFramedTransport.TFactory } -function TFramedTransportImpl.TFactory.GetTransport( const ATrans: ITransport): ITransport; +function TFramedTransportImpl.TFactory.GetTransport( const aTransport: ITransport): ITransport; begin - Result := TFramedTransportImpl.Create( ATrans ); + Result := TFramedTransportImpl.Create( aTransport); end; { TTcpSocketStreamImpl } @@ -1388,17 +1491,17 @@ procedure TTcpSocketStreamImpl.Close; end; {$IFDEF OLD_SOCKETS} -constructor TTcpSocketStreamImpl.Create( const ATcpClient: TCustomIpClient; const aTimeout : Integer); +constructor TTcpSocketStreamImpl.Create( const aTcpClient: TCustomIpClient; const aTimeout : Integer); begin inherited Create; - FTcpClient := ATcpClient; + FTcpClient := aTcpClient; FTimeout := aTimeout; end; {$ELSE} -constructor TTcpSocketStreamImpl.Create( const ATcpClient: TSocket; const aTimeout : Longword); +constructor TTcpSocketStreamImpl.Create( const aTcpClient: TSocket; const aTimeout : Longword); begin inherited Create; - FTcpClient := ATcpClient; + FTcpClient := aTcpClient; if aTimeout = 0 then FTcpClient.RecvTimeout := SLEEP_TIME else @@ -1409,9 +1512,10 @@ constructor TTcpSocketStreamImpl.Create( const ATcpClient: TSocket; const aTimeo procedure TTcpSocketStreamImpl.Flush; begin - + // nothing to do end; + function TTcpSocketStreamImpl.IsOpen: Boolean; begin {$IFDEF OLD_SOCKETS} @@ -1496,7 +1600,7 @@ function TTcpSocketStreamImpl.Select( ReadReady, WriteReady, ExceptFlag: PBoolea {$IFDEF LINUX} result := Libc.select( socket + 1, ReadFdsptr, WriteFdsptr, ExceptFdsptr, Timeptr); {$ENDIF} - + if result = SOCKET_ERROR then wsaError := WSAGetLastError; @@ -1567,7 +1671,7 @@ function TTcpSocketStreamImpl.Read( const pBuf : Pointer; const buflen : Integer result := 0; pTmp := pBuf; Inc( pTmp, offset); - while count > 0 do begin + while (count > 0) and (result = 0) do begin while TRUE do begin wfd := WaitForData( msecs, pTmp, count, wsaError, nBytes); @@ -1577,10 +1681,7 @@ function TTcpSocketStreamImpl.Read( const pBuf : Pointer; const buflen : Integer TWaitForData.wfd_Timeout : begin if (FTimeout = 0) then Exit - else begin - raise TTransportExceptionTimedOut.Create(SysErrorMessage(Cardinal(wsaError))); - - end; + else raise TTransportExceptionTimedOut.Create(SysErrorMessage(Cardinal(wsaError))); end; else ASSERT( FALSE); @@ -1704,12 +1805,4 @@ procedure TTcpSocketStreamImpl.Write( const pBuf : Pointer; offset, count: Integ {$ENDIF} -{$IF CompilerVersion < 21.0} -initialization -begin - TFramedTransportImpl_Initialize; -end; -{$IFEND} - - end. diff --git a/lib/delphi/src/Thrift.TypeRegistry.pas b/lib/delphi/src/Thrift.TypeRegistry.pas index c18e97fe6e8..3d31c52a3a3 100644 --- a/lib/delphi/src/Thrift.TypeRegistry.pas +++ b/lib/delphi/src/Thrift.TypeRegistry.pas @@ -29,7 +29,7 @@ interface TFactoryMethod = function:T; TypeRegistry = class - private + strict private class var FTypeInfoToFactoryLookup : TDictionary; public class constructor Create; diff --git a/lib/delphi/src/Thrift.Utils.pas b/lib/delphi/src/Thrift.Utils.pas index 7e57863b6fb..bfd020e2a29 100644 --- a/lib/delphi/src/Thrift.Utils.pas +++ b/lib/delphi/src/Thrift.Utils.pas @@ -25,12 +25,19 @@ interface uses {$IFDEF OLD_UNIT_NAMES} - Classes, Windows, SysUtils, Character, SyncObjs; + Classes, Windows, SysUtils, Character, SyncObjs, TypInfo, Rtti; {$ELSE} - System.Classes, Winapi.Windows, System.SysUtils, System.Character, System.SyncObjs; + System.Classes, Winapi.Windows, System.SysUtils, System.Character, + System.SyncObjs, System.TypInfo, System.Rtti; {$ENDIF} type + ISupportsToString = interface + ['{AF71C350-E0CD-4E94-B77C-0310DC8227FF}'] + function ToString : string; + end; + + IOverlappedHelper = interface ['{A1832EFA-2E02-4884-8F09-F0A0277157FA}'] function Overlapped : TOverlapped; @@ -55,6 +62,13 @@ TOverlappedHelperImpl = class( TInterfacedObject, IOverlappedHelper) end; + TThriftStringBuilder = class( TStringBuilder) + public + function Append(const Value: TBytes): TStringBuilder; overload; + function Append(const Value: ISupportsToString): TStringBuilder; overload; + end; + + Base64Utils = class sealed public class function Encode( const src : TBytes; srcOff, len : Integer; dst : TBytes; dstOff : Integer) : Integer; static; @@ -68,9 +82,22 @@ CharUtils = class sealed class function IsLowSurrogate( const c : Char) : Boolean; static; inline; end; + EnumUtils = class sealed + public + class function ToString(const value : Integer) : string; reintroduce; static; inline; + end; + + StringUtils = class sealed + public + class function ToString(const value : T) : string; reintroduce; static; inline; + end; + + +const + THRIFT_MIMETYPE = 'application/x-thrift'; {$IFDEF Win64} -function InterlockedExchangeAdd64( var Addend : Int64; Value : Int64) : Int64; +function InterlockedExchangeAdd64( var Addend : Int64; Value : Int64) : Int64; {$ENDIF} @@ -256,4 +283,73 @@ function InterlockedExchangeAdd64( var Addend : Int64; Value : Int64) : Int64; {$ENDIF} +{ EnumUtils } + +class function EnumUtils.ToString(const value : Integer) : string; +var pType : PTypeInfo; +begin + pType := PTypeInfo(TypeInfo(T)); + if Assigned(pType) + and (pType^.Kind = tkEnumeration) + {$IF CompilerVersion >= 23.0} // TODO: Range correct? What we know is that XE does not offer it, but Rio has it + and (pType^.TypeData^.MaxValue >= value) + and (pType^.TypeData^.MinValue <= value) + {$ELSE} + and FALSE // THRIFT-5048: pType^.TypeData^ member not supported -> prevent GetEnumName() from reading outside the legal range + {$IFEND} + then result := GetEnumName( PTypeInfo(pType), value) + else result := IntToStr(Ord(value)); +end; + + +{ StringUtils } + +class function StringUtils.ToString(const value : T) : string; +type PInterface = ^IInterface; +var pType : PTypeInfo; + stos : ISupportsToString; + pIntf : PInterface; // Workaround: Rio does not allow the direct typecast +begin + pType := PTypeInfo(TypeInfo(T)); + if Assigned(pType) then begin + case pType^.Kind of + + tkInterface : begin + pIntf := PInterface(@value); + if Supports( pIntf^, ISupportsToString, stos) then begin + result := stos.toString; + Exit; + end; + end; + + tkEnumeration : begin + case SizeOf(value) of + 1 : begin result := EnumUtils.ToString( PShortInt(@value)^); Exit; end; + 2 : begin result := EnumUtils.ToString( PSmallInt(@value)^); Exit; end; + 4 : begin result := EnumUtils.ToString( PLongInt(@value)^); Exit; end; + else + ASSERT(FALSE); // in theory, this should not happen + end; + end; + + end; + end; + + result := TValue.From(value).ToString; +end; + + +{ TThriftStringBuilder } + +function TThriftStringBuilder.Append(const Value: TBytes): TStringBuilder; +begin + Result := Append( string( RawByteString(Value)) ); +end; + +function TThriftStringBuilder.Append( const Value: ISupportsToString): TStringBuilder; +begin + Result := Append( Value.ToString ); +end; + + end. diff --git a/lib/delphi/src/Thrift.WinHTTP.pas b/lib/delphi/src/Thrift.WinHTTP.pas new file mode 100644 index 00000000000..e6642d9f835 --- /dev/null +++ b/lib/delphi/src/Thrift.WinHTTP.pas @@ -0,0 +1,1459 @@ +(* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + *) +unit Thrift.WinHTTP; + +{$I Thrift.Defines.inc} +{$SCOPEDENUMS ON} + +// packing according to winhttp.h +{$IFDEF Win64} {$ALIGN 8} {$ELSE} {$ALIGN 4} {$ENDIF} + +interface + +uses + Windows, + Classes, + SysUtils, + Math, + Generics.Collections; + + +type + HINTERNET = type Pointer; + INTERNET_PORT = type WORD; + INTERNET_SCHEME = type Integer; + LPLPCWSTR = ^LPCWSTR; + + LPURL_COMPONENTS = ^URL_COMPONENTS; + URL_COMPONENTS = record + dwStructSize : DWORD; // set to SizeOf(URL_COMPONENTS) + lpszScheme : LPWSTR; // scheme name + dwSchemeLength : DWORD; + nScheme : INTERNET_SCHEME; // enumerated scheme type + lpszHostName : LPWSTR; // host name + dwHostNameLength : DWORD; + nPort : INTERNET_PORT; // port number + lpszUserName : LPWSTR; // user name + dwUserNameLength : DWORD; + lpszPassword : LPWSTR; // password + dwPasswordLength : DWORD; + lpszUrlPath : LPWSTR; // URL-path + dwUrlPathLength : DWORD; + lpszExtraInfo : LPWSTR; // extra information + dwExtraInfoLength : DWORD; + end; + + URL_COMPONENTSW = URL_COMPONENTS; + LPURL_COMPONENTSW = LPURL_COMPONENTS; + + + // When retrieving proxy data, an application must free the lpszProxy and + // lpszProxyBypass strings contained in this structure (if they are non-NULL) + // using the GlobalFree function. + LPWINHTTP_PROXY_INFO = ^WINHTTP_PROXY_INFO; + WINHTTP_PROXY_INFO = record + dwAccessType : DWORD; // see WINHTTP_ACCESS_* types below + lpszProxy : LPWSTR; // proxy server list + lpszProxyBypass : LPWSTR; // proxy bypass list + end; + + LPWINHTTP_PROXY_INFOW = ^WINHTTP_PROXY_INFOW; + WINHTTP_PROXY_INFOW = WINHTTP_PROXY_INFO; + + + WINHTTP_AUTOPROXY_OPTIONS = record + dwFlags : DWORD; + dwAutoDetectFlags : DWORD; + lpszAutoConfigUrl : LPCWSTR; + lpvReserved : LPVOID; + dwReserved : DWORD; + fAutoLogonIfChallenged : BOOL; + end; + + + WINHTTP_CURRENT_USER_IE_PROXY_CONFIG = record + fAutoDetect : BOOL; + lpszAutoConfigUrl : LPWSTR; + lpszProxy : LPWSTR; + lpszProxyBypass : LPWSTR; + end; + + + + +function WinHttpCloseHandle( aHandle : HINTERNET) : BOOL; stdcall; + +function WinHttpOpen( const pszAgentW : LPCWSTR; + const dwAccessType : DWORD; + const pszProxyW : LPCWSTR; + const pszProxyBypassW : LPCWSTR; + const dwFlags : DWORD + ) : HINTERNET; stdcall; + +function WinHttpConnect( const hSession : HINTERNET; + const pswzServerName : LPCWSTR; + const nServerPort : INTERNET_PORT; + const dwReserved : DWORD + ) : HINTERNET; stdcall; + +function WinHttpOpenRequest( const hConnect : HINTERNET; + const pwszVerb, pwszObjectName, pwszVersion, pwszReferrer : LPCWSTR; + const ppwszAcceptTypes : LPLPCWSTR; + const dwFlags : DWORD + ) : HINTERNET; stdcall; + +function WinHttpQueryOption( const hInternet : HINTERNET; + const dwOption : DWORD; + const pBuffer : Pointer; + var dwBufferLength : DWORD) : BOOL; stdcall; + +function WinHttpSetOption( const hInternet : HINTERNET; + const dwOption : DWORD; + const pBuffer : Pointer; + const dwBufferLength : DWORD) : BOOL; stdcall; + +function WinHttpSetTimeouts( const hRequestOrSession : HINTERNET; + const aResolveTimeout, aConnectTimeout, aSendTimeout, aReceiveTimeout : Int32 + ) : BOOL; stdcall; + +function WinHttpAddRequestHeaders( const hRequest : HINTERNET; + const pwszHeaders : LPCWSTR; + const dwHeadersLengthInChars : DWORD; + const dwModifiers : DWORD + ) : BOOL; stdcall; + +function WinHttpGetProxyForUrl( const hSession : HINTERNET; + const lpcwszUrl : LPCWSTR; + const options : WINHTTP_AUTOPROXY_OPTIONS; + const info : WINHTTP_PROXY_INFO + ) : BOOL; stdcall; + +function WinHttpGetIEProxyConfigForCurrentUser( var config : WINHTTP_CURRENT_USER_IE_PROXY_CONFIG + ) : BOOL; stdcall; + + +function WinHttpSendRequest( const hRequest : HINTERNET; + const lpszHeaders : LPCWSTR; + const dwHeadersLength : DWORD; + const lpOptional : Pointer; + const dwOptionalLength : DWORD; + const dwTotalLength : DWORD; + const pContext : Pointer + ) : BOOL; stdcall; + +function WinHttpWriteData( const hRequest : HINTERNET; + const pBuf : Pointer; + const dwBytesToWrite : DWORD; + out dwBytesWritten : DWORD + ) : BOOL; stdcall; + +function WinHttpReceiveResponse( const hRequest : HINTERNET; const lpReserved : Pointer) : BOOL; stdcall; + +function WinHttpQueryHeaders( const hRequest : HINTERNET; + const dwInfoLevel : DWORD; + const pwszName : LPCWSTR; + const lpBuffer : Pointer; + var dwBufferLength : DWORD; + var dwIndex : DWORD + ) : BOOL; stdcall; + +function WinHttpQueryDataAvailable( const hRequest : HINTERNET; + var dwNumberOfBytesAvailable : DWORD + ) : BOOL; stdcall; + +function WinHttpReadData( const hRequest : HINTERNET; + const lpBuffer : Pointer; + const dwBytesToRead : DWORD; + out dwBytesRead : DWORD + ) : BOOL; stdcall; + +function WinHttpCrackUrl( const pwszUrl : LPCWSTR; + const dwUrlLength : DWORD; + const dwFlags : DWORD; + var urlComponents : URL_COMPONENTS + ) : BOOL; stdcall; + +function WinHttpCreateUrl( const UrlComponents : URL_COMPONENTS; + const dwFlags : DWORD; + const pwszUrl : LPCWSTR; + var pdwUrlLength : DWORD + ) : BOOL; stdcall; + + +const + // WinHttpOpen dwAccessType values + WINHTTP_ACCESS_TYPE_DEFAULT_PROXY = 0; + WINHTTP_ACCESS_TYPE_NO_PROXY = 1; + WINHTTP_ACCESS_TYPE_NAMED_PROXY = 3; + + // flags for WinHttpOpen(): + WINHTTP_FLAG_ASYNC = $10000000; // want async session, requires WinHttpSetStatusCallback() usage + + WINHTTP_IGNORE_REQUEST_TOTAL_LENGTH = 0; + + // ports + INTERNET_DEFAULT_PORT = 0; // use the protocol-specific default (80 or 443) + + // flags for WinHttpOpenRequest(): + WINHTTP_FLAG_SECURE = $00800000; // use SSL if applicable (HTTPS) + WINHTTP_FLAG_ESCAPE_PERCENT = $00000004; // if escaping enabled, escape percent as well + WINHTTP_FLAG_NULL_CODEPAGE = $00000008; // assume all symbols are ASCII, use fast convertion + WINHTTP_FLAG_BYPASS_PROXY_CACHE = $00000100; // add "pragma: no-cache" request header + WINHTTP_FLAG_REFRESH = WINHTTP_FLAG_BYPASS_PROXY_CACHE; + WINHTTP_FLAG_ESCAPE_DISABLE = $00000040; // disable escaping + WINHTTP_FLAG_ESCAPE_DISABLE_QUERY = $00000080; // if escaping enabled escape path part, but do not escape query + + // flags for WinHttpSendRequest(): + WINHTTP_NO_PROXY_NAME = nil; + WINHTTP_NO_PROXY_BYPASS = nil; + WINHTTP_NO_CLIENT_CERT_CONTEXT = nil; + WINHTTP_NO_REFERER = nil; + WINHTTP_DEFAULT_ACCEPT_TYPES = nil; + WINHTTP_NO_ADDITIONAL_HEADERS = nil; + WINHTTP_NO_REQUEST_DATA = nil; + WINHTTP_HEADER_NAME_BY_INDEX = nil; + WINHTTP_NO_OUTPUT_BUFFER = nil; + WINHTTP_NO_HEADER_INDEX = nil; + + // WinHttpAddRequestHeaders() dwModifiers + WINHTTP_ADDREQ_INDEX_MASK = $0000FFFF; + WINHTTP_ADDREQ_FLAGS_MASK = $FFFF0000; + + WINHTTP_ADDREQ_FLAG_ADD_IF_NEW = $10000000; + WINHTTP_ADDREQ_FLAG_ADD = $20000000; + WINHTTP_ADDREQ_FLAG_COALESCE_WITH_COMMA = $40000000; + WINHTTP_ADDREQ_FLAG_COALESCE_WITH_SEMICOLON = $01000000; + WINHTTP_ADDREQ_FLAG_COALESCE = WINHTTP_ADDREQ_FLAG_COALESCE_WITH_COMMA; + WINHTTP_ADDREQ_FLAG_REPLACE = $80000000; + + // URL functions + ICU_NO_ENCODE = $20000000; // Don't convert unsafe characters to escape sequence + ICU_DECODE = $10000000; // Convert %XX escape sequences to characters + ICU_NO_META = $08000000; // Don't convert .. etc. meta path sequences + ICU_ENCODE_SPACES_ONLY = $04000000; // Encode spaces only + ICU_BROWSER_MODE = $02000000; // Special encode/decode rules for browser + ICU_ENCODE_PERCENT = $00001000; // Encode any percent (ASCII25) + + ICU_ESCAPE = $80000000; // (un)escape URL characters + ICU_ESCAPE_AUTHORITY = $00002000; // causes InternetCreateUrlA to escape chars in authority components (user, pwd, host) + ICU_REJECT_USERPWD = $00004000; // rejects usrls whick have username/pwd sections + + INTERNET_SCHEME_HTTP = INTERNET_SCHEME(1); + INTERNET_SCHEME_HTTPS = INTERNET_SCHEME(2); + + // options manifests for WinHttp{Query|Set}Option + WINHTTP_OPTION_CALLBACK = 1; + WINHTTP_OPTION_RESOLVE_TIMEOUT = 2; + WINHTTP_OPTION_CONNECT_TIMEOUT = 3; + WINHTTP_OPTION_CONNECT_RETRIES = 4; + WINHTTP_OPTION_SEND_TIMEOUT = 5; + WINHTTP_OPTION_RECEIVE_TIMEOUT = 6; + WINHTTP_OPTION_RECEIVE_RESPONSE_TIMEOUT = 7; + WINHTTP_OPTION_HANDLE_TYPE = 9; + WINHTTP_OPTION_READ_BUFFER_SIZE = 12; + WINHTTP_OPTION_WRITE_BUFFER_SIZE = 13; + WINHTTP_OPTION_PARENT_HANDLE = 21; + WINHTTP_OPTION_EXTENDED_ERROR = 24; + WINHTTP_OPTION_SECURITY_FLAGS = 31; + WINHTTP_OPTION_SECURITY_CERTIFICATE_STRUCT = 32; + WINHTTP_OPTION_URL = 34; + WINHTTP_OPTION_SECURITY_KEY_BITNESS = 36; + WINHTTP_OPTION_PROXY = 38; + WINHTTP_OPTION_USER_AGENT = 41; + WINHTTP_OPTION_CONTEXT_VALUE = 45; + WINHTTP_OPTION_CLIENT_CERT_CONTEXT = 47; + WINHTTP_OPTION_REQUEST_PRIORITY = 58; + WINHTTP_OPTION_HTTP_VERSION = 59; + WINHTTP_OPTION_DISABLE_FEATURE = 63; + WINHTTP_OPTION_CODEPAGE = 68; + WINHTTP_OPTION_MAX_CONNS_PER_SERVER = 73; + WINHTTP_OPTION_MAX_CONNS_PER_1_0_SERVER = 74; + WINHTTP_OPTION_AUTOLOGON_POLICY = 77; + WINHTTP_OPTION_SERVER_CERT_CONTEXT = 78; + WINHTTP_OPTION_ENABLE_FEATURE = 79; + WINHTTP_OPTION_WORKER_THREAD_COUNT = 80; + WINHTTP_OPTION_PASSPORT_COBRANDING_TEXT = 81; + WINHTTP_OPTION_PASSPORT_COBRANDING_URL = 82; + WINHTTP_OPTION_CONFIGURE_PASSPORT_AUTH = 83; + WINHTTP_OPTION_SECURE_PROTOCOLS = 84; + WINHTTP_OPTION_ENABLETRACING = 85; + WINHTTP_OPTION_PASSPORT_SIGN_OUT = 86; + WINHTTP_OPTION_PASSPORT_RETURN_URL = 87; + WINHTTP_OPTION_REDIRECT_POLICY = 88; + WINHTTP_OPTION_MAX_HTTP_AUTOMATIC_REDIRECTS = 89; + WINHTTP_OPTION_MAX_HTTP_STATUS_CONTINUE = 90; + WINHTTP_OPTION_MAX_RESPONSE_HEADER_SIZE = 91; + WINHTTP_OPTION_MAX_RESPONSE_DRAIN_SIZE = 92; + WINHTTP_OPTION_CONNECTION_INFO = 93; + WINHTTP_OPTION_CLIENT_CERT_ISSUER_LIST = 94; + WINHTTP_OPTION_SPN = 96; + WINHTTP_OPTION_GLOBAL_PROXY_CREDS = 97; + WINHTTP_OPTION_GLOBAL_SERVER_CREDS = 98; + WINHTTP_OPTION_UNLOAD_NOTIFY_EVENT = 99; + WINHTTP_OPTION_REJECT_USERPWD_IN_URL = 100; + WINHTTP_OPTION_USE_GLOBAL_SERVER_CREDENTIALS = 101; + WINHTTP_OPTION_RECEIVE_PROXY_CONNECT_RESPONSE = 103; + WINHTTP_OPTION_IS_PROXY_CONNECT_RESPONSE = 104; + WINHTTP_OPTION_SERVER_SPN_USED = 106; + WINHTTP_OPTION_PROXY_SPN_USED = 107; + WINHTTP_OPTION_SERVER_CBT = 108; + // options for newer WinHTTP versions + WINHTTP_OPTION_DECOMPRESSION = 118; + // + WINHTTP_FIRST_OPTION = WINHTTP_OPTION_CALLBACK; + //WINHTTP_LAST_OPTION = WINHTTP_OPTION_SERVER_CBT; + + WINHTTP_OPTION_USERNAME = $1000; + WINHTTP_OPTION_PASSWORD = $1001; + WINHTTP_OPTION_PROXY_USERNAME = $1002; + WINHTTP_OPTION_PROXY_PASSWORD = $1003; + + // manifest value for WINHTTP_OPTION_MAX_CONNS_PER_SERVER and WINHTTP_OPTION_MAX_CONNS_PER_1_0_SERVER + WINHTTP_CONNS_PER_SERVER_UNLIMITED = $FFFFFFFF; + + // values for WINHTTP_OPTION_AUTOLOGON_POLICY + WINHTTP_AUTOLOGON_SECURITY_LEVEL_MEDIUM = 0; + WINHTTP_AUTOLOGON_SECURITY_LEVEL_LOW = 1; + WINHTTP_AUTOLOGON_SECURITY_LEVEL_HIGH = 2; + + WINHTTP_AUTOLOGON_SECURITY_LEVEL_DEFAULT = WINHTTP_AUTOLOGON_SECURITY_LEVEL_MEDIUM; + + // values for WINHTTP_OPTION_REDIRECT_POLICY + WINHTTP_OPTION_REDIRECT_POLICY_NEVER = 0; + WINHTTP_OPTION_REDIRECT_POLICY_DISALLOW_HTTPS_TO_HTTP = 1; + WINHTTP_OPTION_REDIRECT_POLICY_ALWAYS = 2; + + WINHTTP_OPTION_REDIRECT_POLICY_LAST = WINHTTP_OPTION_REDIRECT_POLICY_ALWAYS; + WINHTTP_OPTION_REDIRECT_POLICY_DEFAULT = WINHTTP_OPTION_REDIRECT_POLICY_DISALLOW_HTTPS_TO_HTTP; + + WINHTTP_DISABLE_PASSPORT_AUTH = $00000000; + WINHTTP_ENABLE_PASSPORT_AUTH = $10000000; + WINHTTP_DISABLE_PASSPORT_KEYRING = $20000000; + WINHTTP_ENABLE_PASSPORT_KEYRING = $40000000; + + // values for WINHTTP_OPTION_DISABLE_FEATURE + WINHTTP_DISABLE_COOKIES = $00000001; + WINHTTP_DISABLE_REDIRECTS = $00000002; + WINHTTP_DISABLE_AUTHENTICATION = $00000004; + WINHTTP_DISABLE_KEEP_ALIVE = $00000008; + + // values for WINHTTP_OPTION_ENABLE_FEATURE + WINHTTP_ENABLE_SSL_REVOCATION = $00000001; + WINHTTP_ENABLE_SSL_REVERT_IMPERSONATION = $00000002; + + // values for WINHTTP_OPTION_SPN + WINHTTP_DISABLE_SPN_SERVER_PORT = $00000000; + WINHTTP_ENABLE_SPN_SERVER_PORT = $00000001; + WINHTTP_OPTION_SPN_MASK = WINHTTP_ENABLE_SPN_SERVER_PORT; + + // winhttp handle types + WINHTTP_HANDLE_TYPE_SESSION = 1; + WINHTTP_HANDLE_TYPE_CONNECT = 2; + WINHTTP_HANDLE_TYPE_REQUEST = 3; + + // values for auth schemes + WINHTTP_AUTH_SCHEME_BASIC = $00000001; + WINHTTP_AUTH_SCHEME_NTLM = $00000002; + WINHTTP_AUTH_SCHEME_PASSPORT = $00000004; + WINHTTP_AUTH_SCHEME_DIGEST = $00000008; + WINHTTP_AUTH_SCHEME_NEGOTIATE = $00000010; + + // WinHttp supported Authentication Targets + WINHTTP_AUTH_TARGET_SERVER = $00000000; + WINHTTP_AUTH_TARGET_PROXY = $00000001; + + // options for WINHTTP_OPTION_DECOMPRESSION + WINHTTP_DECOMPRESSION_FLAG_GZIP = $00000001; + WINHTTP_DECOMPRESSION_FLAG_DEFLATE = $00000002; + WINHTTP_DECOMPRESSION_FLAG_ALL = WINHTTP_DECOMPRESSION_FLAG_GZIP + or WINHTTP_DECOMPRESSION_FLAG_DEFLATE; + + // values for WINHTTP_OPTION_SECURITY_FLAGS + + // query only + SECURITY_FLAG_SECURE = $00000001; // can query only + SECURITY_FLAG_STRENGTH_WEAK = $10000000; + SECURITY_FLAG_STRENGTH_MEDIUM = $40000000; + SECURITY_FLAG_STRENGTH_STRONG = $20000000; + + // query flags + WINHTTP_QUERY_MIME_VERSION = 0; + WINHTTP_QUERY_CONTENT_TYPE = 1; + WINHTTP_QUERY_CONTENT_TRANSFER_ENCODING = 2; + WINHTTP_QUERY_CONTENT_ID = 3; + WINHTTP_QUERY_CONTENT_DESCRIPTION = 4; + WINHTTP_QUERY_CONTENT_LENGTH = 5; + WINHTTP_QUERY_CONTENT_LANGUAGE = 6; + WINHTTP_QUERY_ALLOW = 7; + WINHTTP_QUERY_PUBLIC = 8; + WINHTTP_QUERY_DATE = 9; + WINHTTP_QUERY_EXPIRES = 10; + WINHTTP_QUERY_LAST_MODIFIED = 11; + WINHTTP_QUERY_MESSAGE_ID = 12; + WINHTTP_QUERY_URI = 13; + WINHTTP_QUERY_DERIVED_FROM = 14; + WINHTTP_QUERY_COST = 15; + WINHTTP_QUERY_LINK = 16; + WINHTTP_QUERY_PRAGMA = 17; + WINHTTP_QUERY_VERSION = 18; + WINHTTP_QUERY_STATUS_CODE = 19; + WINHTTP_QUERY_STATUS_TEXT = 20; + WINHTTP_QUERY_RAW_HEADERS = 21; + WINHTTP_QUERY_RAW_HEADERS_CRLF = 22; + WINHTTP_QUERY_CONNECTION = 23; + WINHTTP_QUERY_ACCEPT = 24; + WINHTTP_QUERY_ACCEPT_CHARSET = 25; + WINHTTP_QUERY_ACCEPT_ENCODING = 26; + WINHTTP_QUERY_ACCEPT_LANGUAGE = 27; + WINHTTP_QUERY_AUTHORIZATION = 28; + WINHTTP_QUERY_CONTENT_ENCODING = 29; + WINHTTP_QUERY_FORWARDED = 30; + WINHTTP_QUERY_FROM = 31; + WINHTTP_QUERY_IF_MODIFIED_SINCE = 32; + WINHTTP_QUERY_LOCATION = 33; + WINHTTP_QUERY_ORIG_URI = 34; + WINHTTP_QUERY_REFERER = 35; + WINHTTP_QUERY_RETRY_AFTER = 36; + WINHTTP_QUERY_SERVER = 37; + WINHTTP_QUERY_TITLE = 38; + WINHTTP_QUERY_USER_AGENT = 39; + WINHTTP_QUERY_WWW_AUTHENTICATE = 40; + WINHTTP_QUERY_PROXY_AUTHENTICATE = 41; + WINHTTP_QUERY_ACCEPT_RANGES = 42; + WINHTTP_QUERY_SET_COOKIE = 43; + WINHTTP_QUERY_COOKIE = 44; + WINHTTP_QUERY_REQUEST_METHOD = 45; + WINHTTP_QUERY_REFRESH = 46; + WINHTTP_QUERY_CONTENT_DISPOSITION = 47; + WINHTTP_QUERY_AGE = 48; + WINHTTP_QUERY_CACHE_CONTROL = 49; + WINHTTP_QUERY_CONTENT_BASE = 50; + WINHTTP_QUERY_CONTENT_LOCATION = 51; + WINHTTP_QUERY_CONTENT_MD5 = 52; + WINHTTP_QUERY_CONTENT_RANGE = 53; + WINHTTP_QUERY_ETAG = 54; + WINHTTP_QUERY_HOST = 55; + WINHTTP_QUERY_IF_MATCH = 56; + WINHTTP_QUERY_IF_NONE_MATCH = 57; + WINHTTP_QUERY_IF_RANGE = 58; + WINHTTP_QUERY_IF_UNMODIFIED_SINCE = 59; + WINHTTP_QUERY_MAX_FORWARDS = 60; + WINHTTP_QUERY_PROXY_AUTHORIZATION = 61; + WINHTTP_QUERY_RANGE = 62; + WINHTTP_QUERY_TRANSFER_ENCODING = 63; + WINHTTP_QUERY_UPGRADE = 64; + WINHTTP_QUERY_VARY = 65; + WINHTTP_QUERY_VIA = 66; + WINHTTP_QUERY_WARNING = 67; + WINHTTP_QUERY_EXPECT = 68; + WINHTTP_QUERY_PROXY_CONNECTION = 69; + WINHTTP_QUERY_UNLESS_MODIFIED_SINCE = 70; + WINHTTP_QUERY_PROXY_SUPPORT = 75; + WINHTTP_QUERY_AUTHENTICATION_INFO = 76; + WINHTTP_QUERY_PASSPORT_URLS = 77; + WINHTTP_QUERY_PASSPORT_CONFIG = 78; + WINHTTP_QUERY_MAX = 78; + WINHTTP_QUERY_CUSTOM = 65535; + WINHTTP_QUERY_FLAG_REQUEST_HEADERS = $80000000; + WINHTTP_QUERY_FLAG_SYSTEMTIME = $40000000; + WINHTTP_QUERY_FLAG_NUMBER = $20000000; + + // Secure connection error status flags + WINHTTP_CALLBACK_STATUS_FLAG_CERT_REV_FAILED = $00000001; + WINHTTP_CALLBACK_STATUS_FLAG_INVALID_CERT = $00000002; + WINHTTP_CALLBACK_STATUS_FLAG_CERT_REVOKED = $00000004; + WINHTTP_CALLBACK_STATUS_FLAG_INVALID_CA = $00000008; + WINHTTP_CALLBACK_STATUS_FLAG_CERT_CN_INVALID = $00000010; + WINHTTP_CALLBACK_STATUS_FLAG_CERT_DATE_INVALID = $00000020; + WINHTTP_CALLBACK_STATUS_FLAG_CERT_WRONG_USAGE = $00000040; + WINHTTP_CALLBACK_STATUS_FLAG_SECURITY_CHANNEL_ERROR = $80000000; + + WINHTTP_FLAG_SECURE_PROTOCOL_SSL2 = $00000008; + WINHTTP_FLAG_SECURE_PROTOCOL_SSL3 = $00000020; + WINHTTP_FLAG_SECURE_PROTOCOL_TLS1 = $00000080; + WINHTTP_FLAG_SECURE_PROTOCOL_TLS1_1 = $00000200; + WINHTTP_FLAG_SECURE_PROTOCOL_TLS1_2 = $00000800; + + // Note: SECURE_PROTOCOL_ALL does not include TLS1.1 and higher! + WINHTTP_FLAG_SECURE_PROTOCOL_ALL = WINHTTP_FLAG_SECURE_PROTOCOL_SSL2 + or WINHTTP_FLAG_SECURE_PROTOCOL_SSL3 + or WINHTTP_FLAG_SECURE_PROTOCOL_TLS1; + + // AutoProxy + WINHTTP_AUTOPROXY_AUTO_DETECT = $00000001; + WINHTTP_AUTOPROXY_CONFIG_URL = $00000002; + WINHTTP_AUTOPROXY_HOST_KEEPCASE = $00000004; + WINHTTP_AUTOPROXY_HOST_LOWERCASE = $00000008; + WINHTTP_AUTOPROXY_RUN_INPROCESS = $00010000; + WINHTTP_AUTOPROXY_RUN_OUTPROCESS_ONLY = $00020000; + + // Flags for dwAutoDetectFlags + WINHTTP_AUTO_DETECT_TYPE_DHCP = $00000001; + WINHTTP_AUTO_DETECT_TYPE_DNS_A = $00000002; + +const + WINHTTP_ERROR_BASE = 12000; + ERROR_WINHTTP_OUT_OF_HANDLES = WINHTTP_ERROR_BASE + 1; + ERROR_WINHTTP_TIMEOUT = WINHTTP_ERROR_BASE + 2; + ERROR_WINHTTP_INTERNAL_ERROR = WINHTTP_ERROR_BASE + 4; + ERROR_WINHTTP_INVALID_URL = WINHTTP_ERROR_BASE + 5; + ERROR_WINHTTP_UNRECOGNIZED_SCHEME = WINHTTP_ERROR_BASE + 6; + ERROR_WINHTTP_NAME_NOT_RESOLVED = WINHTTP_ERROR_BASE + 7; + ERROR_WINHTTP_INVALID_OPTION = WINHTTP_ERROR_BASE + 9; + ERROR_WINHTTP_OPTION_NOT_SETTABLE = WINHTTP_ERROR_BASE + 11; + ERROR_WINHTTP_SHUTDOWN = WINHTTP_ERROR_BASE + 12; + ERROR_WINHTTP_LOGIN_FAILURE = WINHTTP_ERROR_BASE + 15; + ERROR_WINHTTP_OPERATION_CANCELLED = WINHTTP_ERROR_BASE + 17; + ERROR_WINHTTP_INCORRECT_HANDLE_TYPE = WINHTTP_ERROR_BASE + 18; + ERROR_WINHTTP_INCORRECT_HANDLE_STATE = WINHTTP_ERROR_BASE + 19; + ERROR_WINHTTP_CANNOT_CONNECT = WINHTTP_ERROR_BASE + 29; + ERROR_WINHTTP_CONNECTION_ERROR = WINHTTP_ERROR_BASE + 30; + ERROR_WINHTTP_RESEND_REQUEST = WINHTTP_ERROR_BASE + 32; + ERROR_WINHTTP_CLIENT_AUTH_CERT_NEEDED = WINHTTP_ERROR_BASE + 44; + ERROR_WINHTTP_CANNOT_CALL_BEFORE_OPEN = WINHTTP_ERROR_BASE + 100; + ERROR_WINHTTP_CANNOT_CALL_BEFORE_SEND = WINHTTP_ERROR_BASE + 101; + ERROR_WINHTTP_CANNOT_CALL_AFTER_SEND = WINHTTP_ERROR_BASE + 102; + ERROR_WINHTTP_CANNOT_CALL_AFTER_OPEN = WINHTTP_ERROR_BASE + 103; + ERROR_WINHTTP_HEADER_NOT_FOUND = WINHTTP_ERROR_BASE + 150; + ERROR_WINHTTP_INVALID_SERVER_RESPONSE = WINHTTP_ERROR_BASE + 152; + ERROR_WINHTTP_INVALID_HEADER = WINHTTP_ERROR_BASE + 153; + ERROR_WINHTTP_INVALID_QUERY_REQUEST = WINHTTP_ERROR_BASE + 154; + ERROR_WINHTTP_HEADER_ALREADY_EXISTS = WINHTTP_ERROR_BASE + 155; + ERROR_WINHTTP_REDIRECT_FAILED = WINHTTP_ERROR_BASE + 156; + ERROR_WINHTTP_AUTO_PROXY_SERVICE_ERROR = WINHTTP_ERROR_BASE + 178; + ERROR_WINHTTP_BAD_AUTO_PROXY_SCRIPT = WINHTTP_ERROR_BASE + 166; + ERROR_WINHTTP_UNABLE_TO_DOWNLOAD_SCRIPT = WINHTTP_ERROR_BASE + 167; + ERROR_WINHTTP_NOT_INITIALIZED = WINHTTP_ERROR_BASE + 172; + ERROR_WINHTTP_SECURE_FAILURE = WINHTTP_ERROR_BASE + 175; + + // Certificate security errors. Additional information is provided + // via the WINHTTP_CALLBACK_STATUS_SECURE_FAILURE callback notification. + ERROR_WINHTTP_SECURE_CERT_DATE_INVALID = WINHTTP_ERROR_BASE + 37; + ERROR_WINHTTP_SECURE_CERT_CN_INVALID = WINHTTP_ERROR_BASE + 38; + ERROR_WINHTTP_SECURE_INVALID_CA = WINHTTP_ERROR_BASE + 45; + ERROR_WINHTTP_SECURE_CERT_REV_FAILED = WINHTTP_ERROR_BASE + 57; + ERROR_WINHTTP_SECURE_CHANNEL_ERROR = WINHTTP_ERROR_BASE + 157; + ERROR_WINHTTP_SECURE_INVALID_CERT = WINHTTP_ERROR_BASE + 169; + ERROR_WINHTTP_SECURE_CERT_REVOKED = WINHTTP_ERROR_BASE + 170; + ERROR_WINHTTP_SECURE_CERT_WRONG_USAGE = WINHTTP_ERROR_BASE + 179; + + ERROR_WINHTTP_AUTODETECTION_FAILED = WINHTTP_ERROR_BASE + 180; + ERROR_WINHTTP_HEADER_COUNT_EXCEEDED = WINHTTP_ERROR_BASE + 181; + ERROR_WINHTTP_HEADER_SIZE_OVERFLOW = WINHTTP_ERROR_BASE + 182; + ERROR_WINHTTP_CHUNKED_ENCODING_HEADER_SIZE_OVERFLOW = WINHTTP_ERROR_BASE + 183; + ERROR_WINHTTP_RESPONSE_DRAIN_OVERFLOW = WINHTTP_ERROR_BASE + 184; + ERROR_WINHTTP_CLIENT_CERT_NO_PRIVATE_KEY = WINHTTP_ERROR_BASE + 185; + ERROR_WINHTTP_CLIENT_CERT_NO_ACCESS_PRIVATE_KEY = WINHTTP_ERROR_BASE + 186; + + WINHTTP_ERROR_LAST = WINHTTP_ERROR_BASE + 186; + + +const + WINHTTP_THRIFT_DEFAULTS = WINHTTP_FLAG_NULL_CODEPAGE + or WINHTTP_FLAG_BYPASS_PROXY_CACHE + or WINHTTP_FLAG_ESCAPE_DISABLE; + + + +type + IWinHTTPSession = interface; + IWinHTTPConnection = interface; + + IWinHTTPRequest = interface + ['{FADA4B45-D447-4F07-8361-1AD6656E5E8C}'] + function Handle : HINTERNET; + function Connection : IWinHTTPConnection; + function AddRequestHeader( const aHeader : string; const addflag : DWORD = WINHTTP_ADDREQ_FLAG_ADD) : Boolean; + function SetTimeouts( const aResolveTimeout, aConnectTimeout, aSendTimeout, aReceiveTimeout : Int32) : Boolean; + procedure TryAutoProxy( const aUrl : string); + procedure EnableAutomaticContentDecompression( const aEnable : Boolean); + function SendRequest( const pBuf : Pointer; const dwBytes : DWORD; const dwExtra : DWORD = 0) : Boolean; + function WriteExtraData( const pBuf : Pointer; const dwBytes : DWORD) : DWORD; + function FlushAndReceiveResponse : Boolean; + function ReadData( const dwRead : DWORD) : TBytes; overload; + function ReadData( const pBuf : Pointer; const dwRead : DWORD) : DWORD; overload; + function QueryDataAvailable : DWORD; + function QueryTotalResponseSize( out dwSize : DWORD) : Boolean; + function QueryHttpStatus( out dwStatusCode : DWORD; out sStatusText : string) : Boolean; + end; + + IWinHTTPConnection = interface + ['{ED5BCA49-84D6-4CFE-BF18-3238B1FF2AFB}'] + function Handle : HINTERNET; + function Session : IWinHTTPSession; + function OpenRequest( const secure : Boolean; const aVerb, aObjName, aAcceptTypes : UnicodeString) : IWinHTTPRequest; + end; + + IWinHTTPSession = interface + ['{261ADCB7-5465-4407-8840-468C17F009F0}'] + function Handle : HINTERNET; + function Connect( const aHostName : UnicodeString; const aPort : INTERNET_PORT = INTERNET_DEFAULT_PORT) : IWinHTTPConnection; + function SetTimeouts( const aResolveTimeout, aConnectTimeout, aSendTimeout, aReceiveTimeout : Int32) : Boolean; + function EnableSecureProtocols( const aFlagSet : DWORD) : Boolean; + end; + + IWinHTTPUrl = interface + ['{78BE977C-4171-4AF5-A250-FD2890205E63}'] + // url parts getter + function GetScheme : UnicodeString; + function GetNumScheme : INTERNET_SCHEME; + function GetHostName : UnicodeString; + function GetPort : INTERNET_PORT; + function GetUserName : UnicodeString; + function GetPassword : UnicodeString; + function GetUrlPath : UnicodeString; + function GetExtraInfo : UnicodeString; + + // url parts setter + procedure SetScheme( const value : UnicodeString); + procedure SetHostName ( const value : UnicodeString); + procedure SetPort( const value : INTERNET_PORT); + procedure SetUserName( const value : UnicodeString); + procedure SetPassword( const value : UnicodeString); + procedure SetUrlPath( const value : UnicodeString); + procedure SetExtraInfo( const value : UnicodeString); + + // url as a whole + function BuildUrl : UnicodeString; + procedure CrackUrl( const value : UnicodeString); + + // url parts + property Scheme : UnicodeString read GetScheme write SetScheme; + property NumScheme : INTERNET_SCHEME read GetNumScheme; // readonly + property HostName : UnicodeString read GetHostName write SetHostName; + property Port : INTERNET_PORT read GetPort write SetPort; + property UserName : UnicodeString read GetUserName write SetUserName; + property Password : UnicodeString read GetPassword write SetPassword; + property UrlPath : UnicodeString read GetUrlPath write SetUrlPath; + property ExtraInfo : UnicodeString read GetExtraInfo write SetExtraInfo; + + // url as a whole + property CompleteURL : UnicodeString read BuildUrl write CrackUrl; + end; + + + + +type + TWinHTTPHandleObjectImpl = class( TInterfacedObject) + strict protected + FHandle : HINTERNET; + function Handle : HINTERNET; + public + constructor Create( const aHandle : HINTERNET); + destructor Destroy; override; + end; + + + TWinHTTPSessionImpl = class( TWinHTTPHandleObjectImpl, IWinHTTPSession) + strict protected + + // IWinHTTPSession + function Connect( const aHostName : UnicodeString; const aPort : INTERNET_PORT = INTERNET_DEFAULT_PORT) : IWinHTTPConnection; + function SetTimeouts( const aResolveTimeout, aConnectTimeout, aSendTimeout, aReceiveTimeout : Int32) : Boolean; + function EnableSecureProtocols( const aFlagSet : DWORD) : Boolean; + public + constructor Create( const aAgent : UnicodeString; + const aAccessType : DWORD = WINHTTP_ACCESS_TYPE_DEFAULT_PROXY; + const aProxy : UnicodeString = ''; + const aProxyBypass : UnicodeString = ''; + const aFlags : DWORD = 0); + destructor Destroy; override; + end; + + + TWinHTTPConnectionImpl = class( TWinHTTPHandleObjectImpl, IWinHTTPConnection) + strict protected + FSession : IWinHTTPSession; + + // IWinHTTPConnection + function OpenRequest( const secure : Boolean; const aVerb, aObjName, aAcceptTypes : UnicodeString) : IWinHTTPRequest; + function Session : IWinHTTPSession; + + public + constructor Create( const aSession : IWinHTTPSession; const aHostName : UnicodeString; const aPort : INTERNET_PORT); + destructor Destroy; override; + end; + + + TAcceptTypesArray = array of string; + + TWinHTTPRequestImpl = class( TWinHTTPHandleObjectImpl, IWinHTTPRequest) + strict protected + FConnection : IWinHTTPConnection; + + // IWinHTTPRequest + function Connection : IWinHTTPConnection; + function AddRequestHeader( const aHeader : string; const addflag : DWORD = WINHTTP_ADDREQ_FLAG_ADD) : Boolean; + function SetTimeouts( const aResolveTimeout, aConnectTimeout, aSendTimeout, aReceiveTimeout : Int32) : Boolean; + procedure TryAutoProxy( const aUrl : string); + procedure EnableAutomaticContentDecompression( const aEnable : Boolean); + function SendRequest( const pBuf : Pointer; const dwBytes : DWORD; const dwExtra : DWORD = 0) : Boolean; + function WriteExtraData( const pBuf : Pointer; const dwBytes : DWORD) : DWORD; + function FlushAndReceiveResponse : Boolean; + function ReadData( const dwRead : DWORD) : TBytes; overload; + function ReadData( const pBuf : Pointer; const dwRead : DWORD) : DWORD; overload; + function QueryDataAvailable : DWORD; + function QueryTotalResponseSize( out dwSize : DWORD) : Boolean; + function QueryHttpStatus( out dwStatusCode : DWORD; out sStatusText : string) : Boolean; + + public + constructor Create( const aConnection : IWinHTTPConnection; + const aVerb, aObjName : UnicodeString; + const aVersion : UnicodeString = ''; + const aReferrer : UnicodeString = ''; + const aAcceptTypes : UnicodeString = '*/*'; + const aFlags : DWORD = WINHTTP_THRIFT_DEFAULTS + ); + + destructor Destroy; override; + end; + + + TWinHTTPUrlImpl = class( TInterfacedObject, IWinHTTPUrl) + strict private + FScheme : UnicodeString; + FNumScheme : INTERNET_SCHEME; + FHostName : UnicodeString; + FPort : INTERNET_PORT; + FUserName : UnicodeString; + FPassword : UnicodeString; + FUrlPath : UnicodeString; + FExtraInfo : UnicodeString; + + strict protected + // url parts getter + function GetScheme : UnicodeString; + function GetNumScheme : INTERNET_SCHEME; + function GetHostName : UnicodeString; + function GetPort : INTERNET_PORT; + function GetUserName : UnicodeString; + function GetPassword : UnicodeString; + function GetUrlPath : UnicodeString; + function GetExtraInfo : UnicodeString; + + // url parts setter + procedure SetScheme( const value : UnicodeString); + procedure SetHostName ( const value : UnicodeString); + procedure SetPort( const value : INTERNET_PORT); + procedure SetUserName( const value : UnicodeString); + procedure SetPassword( const value : UnicodeString); + procedure SetUrlPath( const value : UnicodeString); + procedure SetExtraInfo( const value : UnicodeString); + + // url as a whole + function BuildUrl : UnicodeString; + procedure CrackUrl( const value : UnicodeString); + + public + constructor Create( const aUri : UnicodeString); + destructor Destroy; override; + end; + + + WINHTTP_PROXY_INFO_Helper = record helper for WINHTTP_PROXY_INFO + procedure Initialize; + procedure FreeAllocatedResources; + end; + + + WINHTTP_CURRENT_USER_IE_PROXY_CONFIG_Helper = record helper for WINHTTP_CURRENT_USER_IE_PROXY_CONFIG + procedure Initialize; + procedure FreeAllocatedResources; + end; + + + EWinHTTPException = class(Exception); + +{ helper functions } + +function WinHttpSysErrorMessage( const error : Cardinal): string; +procedure RaiseLastWinHttpError; + + +implementation + +const WINHTTP_DLL = 'WinHTTP.dll'; + +function WinHttpCloseHandle; stdcall; external WINHTTP_DLL; +function WinHttpOpen; stdcall; external WINHTTP_DLL; +function WinHttpConnect; stdcall; external WINHTTP_DLL; +function WinHttpOpenRequest; stdcall; external WINHTTP_DLL; +function WinHttpSendRequest; stdcall; external WINHTTP_DLL; +function WinHttpSetTimeouts; stdcall; external WINHTTP_DLL; +function WinHttpQueryOption; stdcall; external WINHTTP_DLL; +function WinHttpSetOption; stdcall; external WINHTTP_DLL; +function WinHttpAddRequestHeaders; stdcall; external WINHTTP_DLL; +function WinHttpGetProxyForUrl; stdcall; external WINHTTP_DLL; +function WinHttpGetIEProxyConfigForCurrentUser; stdcall; external WINHTTP_DLL; +function WinHttpWriteData; stdcall; external WINHTTP_DLL; +function WinHttpReceiveResponse; stdcall; external WINHTTP_DLL; +function WinHttpQueryHeaders; stdcall; external WINHTTP_DLL; +function WinHttpQueryDataAvailable; stdcall; external WINHTTP_DLL; +function WinHttpReadData; stdcall; external WINHTTP_DLL; +function WinHttpCrackUrl; stdcall; external WINHTTP_DLL; +function WinHttpCreateUrl; stdcall; external WINHTTP_DLL; + + +{ helper functions } + +function WinHttpSysErrorMessage( const error : Cardinal): string; +const FLAGS = FORMAT_MESSAGE_ALLOCATE_BUFFER + or FORMAT_MESSAGE_IGNORE_INSERTS + or FORMAT_MESSAGE_FROM_SYSTEM + or FORMAT_MESSAGE_FROM_HMODULE; +var pBuffer : PChar; + nChars : Cardinal; +begin + if (error < WINHTTP_ERROR_BASE) + or (error > WINHTTP_ERROR_LAST) + then Exit( SysUtils.SysErrorMessage( error)); + + pBuffer := nil; + try + nChars := FormatMessage( FLAGS, + Pointer( GetModuleHandle( WINHTTP_DLL)), + error, + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // default language + @pBuffer, 0, + nil); + SetString( result, pBuffer, nChars); + finally + LocalFree( NativeUInt( pBuffer)); + end; +end; + + +procedure RaiseLastWinHttpError; +var error : Cardinal; + sMsg : string; +begin + error := Cardinal( GetLastError); + if error <> NOERROR then begin + sMSg := IntToStr(Integer(error))+' '+WinHttpSysErrorMessage(error); + raise EWinHTTPException.Create( sMsg); + end; +end; + + + +{ misc. record helper } + + +procedure GlobalFreeAndNil( var p : LPWSTR); +begin + if p <> nil then begin + GlobalFree( HGLOBAL( p)); + p := nil; + end; +end; + + +procedure WINHTTP_PROXY_INFO_Helper.Initialize; +begin + FillChar( Self, SizeOf(Self), 0); +end; + + +procedure WINHTTP_PROXY_INFO_Helper.FreeAllocatedResources; +// The caller must free the lpszProxy and lpszProxyBypass strings +// if they are non-NULL. Use GlobalFree to free the strings. +begin + GlobalFreeAndNil( lpszProxy); + GlobalFreeAndNil( lpszProxyBypass); + Initialize; +end; + + +procedure WINHTTP_CURRENT_USER_IE_PROXY_CONFIG_Helper.Initialize; +begin + FillChar( Self, SizeOf(Self), 0); +end; + + +procedure WINHTTP_CURRENT_USER_IE_PROXY_CONFIG_Helper.FreeAllocatedResources; +// The caller must free the lpszProxy, lpszProxyBypass and lpszAutoConfigUrl strings +// if they are non-NULL. Use GlobalFree to free the strings. +begin + GlobalFreeAndNil( lpszProxy); + GlobalFreeAndNil( lpszProxyBypass); + GlobalFreeAndNil( lpszAutoConfigUrl); + Initialize; +end; + + +{ TWinHTTPHandleObjectImpl } + +constructor TWinHTTPHandleObjectImpl.Create( const aHandle : HINTERNET); +begin + inherited Create; + FHandle := aHandle; + + if FHandle = nil + then raise EWinHTTPException.Create('Invalid handle'); +end; + + +destructor TWinHTTPHandleObjectImpl.Destroy; +begin + try + if Assigned(FHandle) then begin + WinHttpCloseHandle(FHandle); + FHandle := nil; + end; + + finally + inherited Destroy; + end; +end; + + +function TWinHTTPHandleObjectImpl.Handle : HINTERNET; +begin + result := FHandle; +end; + + +{ TWinHTTPSessionImpl } + + +constructor TWinHTTPSessionImpl.Create( const aAgent : UnicodeString; const aAccessType : DWORD; + const aProxy, aProxyBypass : UnicodeString; const aFlags : DWORD); +var handle : HINTERNET; +begin + handle := WinHttpOpen( PWideChar(aAgent), aAccessType, + PWideChar(Pointer(aProxy)), // may be nil + PWideChar(Pointer(aProxyBypass)), // may be nil + aFlags); + if handle = nil then RaiseLastWinHttpError; + inherited Create( handle); +end; + + +destructor TWinHTTPSessionImpl.Destroy; +begin + inherited Destroy; + // add code here +end; + + +function TWinHTTPSessionImpl.Connect( const aHostName : UnicodeString; const aPort : INTERNET_PORT) : IWinHTTPConnection; +begin + result := TWinHTTPConnectionImpl.Create( Self, aHostName, aPort); +end; + + +function TWinHTTPSessionImpl.SetTimeouts( const aResolveTimeout, aConnectTimeout, aSendTimeout, aReceiveTimeout : Int32) : Boolean; +begin + result := WinHttpSetTimeouts( FHandle, aResolveTimeout, aConnectTimeout, aSendTimeout, aReceiveTimeout); +end; + + +function TWinHTTPSessionImpl.EnableSecureProtocols( const aFlagSet : DWORD) : Boolean; +var dwSize : DWORD; +begin + dwSize := SizeOf(aFlagSet); + result := WinHttpSetOption( Handle, WINHTTP_OPTION_SECURE_PROTOCOLS, @aFlagset, dwSize); +end; + + +{ TWinHTTPConnectionImpl } + +constructor TWinHTTPConnectionImpl.Create( const aSession : IWinHTTPSession; const aHostName : UnicodeString; const aPort : INTERNET_PORT); +var handle : HINTERNET; +begin + FSession := aSession; + handle := WinHttpConnect( FSession.Handle, PWideChar(aHostName), aPort, 0); + if handle = nil then RaiseLastWinHttpError; + inherited Create( handle); +end; + + +destructor TWinHTTPConnectionImpl.Destroy; +begin + inherited Destroy; + FSession := nil; +end; + + +function TWinHTTPConnectionImpl.Session : IWinHTTPSession; +begin + result := FSession; +end; + + +function TWinHTTPConnectionImpl.OpenRequest( const secure : Boolean; const aVerb, aObjName, aAcceptTypes : UnicodeString) : IWinHTTPRequest; +var dwFlags : DWORD; +begin + dwFlags := WINHTTP_THRIFT_DEFAULTS; + if secure + then dwFlags := dwFlags or WINHTTP_FLAG_SECURE + else dwFlags := dwFlags and not WINHTTP_FLAG_SECURE; + + result := TWinHTTPRequestImpl.Create( Self, aVerb, aObjName, '', '', aAcceptTypes, dwFlags); +end; + + +{ TWinHTTPRequestImpl } + +constructor TWinHTTPRequestImpl.Create( const aConnection : IWinHTTPConnection; + const aVerb, aObjName, aVersion, aReferrer : UnicodeString; + const aAcceptTypes : UnicodeString; + const aFlags : DWORD + ); +var handle : HINTERNET; + accept : array[0..1] of PWideChar; +begin + FConnection := aConnection; + + accept[0] := PWideChar(aAcceptTypes); + accept[1] := nil; + + handle := WinHttpOpenRequest( FConnection.Handle, + PWideChar(UpperCase(aVerb)), + PWideChar(aObjName), + PWideChar(aVersion), + PWideChar(aReferrer), + @accept, + aFlags); + if handle = nil then RaiseLastWinHttpError; + inherited Create( handle); +end; + + +destructor TWinHTTPRequestImpl.Destroy; +begin + inherited Destroy; + FConnection := nil; +end; + + +function TWinHTTPRequestImpl.Connection : IWinHTTPConnection; +begin + result := FConnection; +end; + + +function TWinHTTPRequestImpl.SetTimeouts( const aResolveTimeout, aConnectTimeout, aSendTimeout, aReceiveTimeout : Int32) : Boolean; +begin + result := WinHttpSetTimeouts( FHandle, aResolveTimeout, aConnectTimeout, aSendTimeout, aReceiveTimeout); +end; + + +function TWinHTTPRequestImpl.AddRequestHeader( const aHeader : string; const addflag : DWORD) : Boolean; +begin + result := WinHttpAddRequestHeaders( FHandle, PWideChar(aHeader), DWORD(-1), addflag); +end; + + +procedure TWinHTTPRequestImpl.TryAutoProxy( const aUrl : string); +// From MSDN: +// AutoProxy support is not fully integrated into the HTTP stack in WinHTTP. +// Before sending a request, the application must call WinHttpGetProxyForUrl +// to obtain the name of a proxy server and then call WinHttpSetOption using +// WINHTTP_OPTION_PROXY to set the proxy configuration on the WinHTTP request +// handle created by WinHttpOpenRequest. +// See https://docs.microsoft.com/en-us/windows/desktop/winhttp/winhttp-autoproxy-api +var + options : WINHTTP_AUTOPROXY_OPTIONS; + proxy : WINHTTP_PROXY_INFO; + ieProxy : WINHTTP_CURRENT_USER_IE_PROXY_CONFIG; + dwSize : DWORD; +begin + // try AutoProxy via PAC first + proxy.Initialize; + try + FillChar( options, SizeOf(options), 0); + options.dwFlags := WINHTTP_AUTOPROXY_AUTO_DETECT; + options.dwAutoDetectFlags := WINHTTP_AUTO_DETECT_TYPE_DHCP or WINHTTP_AUTO_DETECT_TYPE_DNS_A; + options.fAutoLogonIfChallenged := TRUE; + if WinHttpGetProxyForUrl( FConnection.Session.Handle, PChar(aUrl), options, proxy) then begin + dwSize := SizeOf(proxy); + WinHttpSetOption( Handle, WINHTTP_OPTION_PROXY, @proxy, dwSize); + Exit; + end; + + finally + proxy.FreeAllocatedResources; + end; + + // Use IE settings as a fallback, useful in client (i.e. non-server) environments + ieProxy.Initialize; + try + if WinHttpGetIEProxyConfigForCurrentUser( ieProxy) + then begin + + // lpszAutoConfigUrl = "Use automatic proxy configuration" + if ieProxy.lpszAutoConfigUrl <> nil then begin + options.lpszAutoConfigUrl := ieProxy.lpszAutoConfigUrl; + options.dwFlags := options.dwFlags or WINHTTP_AUTOPROXY_CONFIG_URL; + + proxy.Initialize; + try + if WinHttpGetProxyForUrl( FConnection.Session.Handle, PChar(aUrl), options, proxy) then begin + dwSize := SizeOf(proxy); + WinHttpSetOption( Handle, WINHTTP_OPTION_PROXY, @proxy, dwSize); + Exit; + end; + finally + proxy.FreeAllocatedResources; + end; + end; + + // lpszProxy = "use a proxy server" + if ieProxy.lpszProxy <> nil then begin + proxy.Initialize; + try + proxy.dwAccessType := WINHTTP_ACCESS_TYPE_NAMED_PROXY; + proxy.lpszProxy := ieProxy.lpszProxy; + proxy.lpszProxyBypass := ieProxy.lpszProxyBypass; + dwSize := SizeOf(proxy); + WinHttpSetOption( Handle, WINHTTP_OPTION_PROXY, @proxy, dwSize); + Exit; + finally + proxy.Initialize; // not FreeAllocatedResources, we only hold pointer copies! + end; + end; + + end; + + finally + ieProxy.FreeAllocatedResources; + end; +end; + + +procedure TWinHTTPRequestImpl.EnableAutomaticContentDecompression( const aEnable : Boolean); +// Enable automatic gzip,deflate decompression on systems that support this option +// From the docs: WinHTTP will automatically set an appropriate Accept-Encoding header, +// overriding any value supplied by the caller -> we don't have to do this +// Available on Win 8.1 or higher +var value : DWORD; +begin + if aEnable + then value := WINHTTP_DECOMPRESSION_FLAG_ALL + else value := 0; + + // ignore returned value, the option is not supported with older WinHTTP versions + WinHttpSetOption( Handle, WINHTTP_OPTION_DECOMPRESSION, @value, SizeOf(DWORD)); +end; + + +function TWinHTTPRequestImpl.SendRequest( const pBuf : Pointer; const dwBytes, dwExtra : DWORD) : Boolean; +begin + result := WinHttpSendRequest( FHandle, + WINHTTP_NO_ADDITIONAL_HEADERS, 0, + pBuf, dwBytes, // number of bytes in pBuf + dwBytes + dwExtra, // becomes the Content-Length + nil); // context for async operations +end; + + +function TWinHTTPRequestImpl.WriteExtraData( const pBuf : Pointer; const dwBytes : DWORD) : DWORD; +begin + if not WinHttpWriteData( FHandle, pBuf, dwBytes, result) + then result := 0; +end; + + +function TWinHTTPRequestImpl.FlushAndReceiveResponse : Boolean; +begin + result := WinHttpReceiveResponse( FHandle, nil); +end; + + +function TWinHTTPRequestImpl.ReadData( const dwRead : DWORD) : TBytes; +var dwAvailable, dwReceived : DWORD; +begin + if WinHttpQueryDataAvailable( FHandle, dwAvailable) + then dwAvailable := Min( dwRead, dwAvailable) + else dwAvailable := 0; + + SetLength( result, dwAvailable); + if dwAvailable = 0 then Exit; + + if WinHttpReadData( FHandle, @result[0], Length(result), dwReceived) + then SetLength( result, dwReceived) + else SetLength( result, 0); +end; + + +function TWinHTTPRequestImpl.ReadData( const pBuf : Pointer; const dwRead : DWORD) : DWORD; +var dwAvailable : DWORD; +begin + if WinHttpQueryDataAvailable( FHandle, dwAvailable) + then dwAvailable := Min( dwRead, dwAvailable) + else dwAvailable := 0; + + if (dwAvailable = 0) + or not WinHttpReadData( FHandle, pBuf, dwAvailable, result) + then result := 0; +end; + + +function TWinHTTPRequestImpl.QueryDataAvailable : DWORD; +begin + if not WinHttpQueryDataAvailable( FHandle, result) + then result := 0; +end; + + +function TWinHTTPRequestImpl.QueryTotalResponseSize( out dwSize : DWORD) : Boolean; +var dwBytes, dwError, dwIndex : DWORD; + bytes : array[0..32-1] of Byte; +begin + dwBytes := SizeOf( result); + dwIndex := DWORD( WINHTTP_NO_HEADER_INDEX); + result := WinHttpQueryHeaders( FHandle, + WINHTTP_QUERY_CONTENT_LENGTH or WINHTTP_QUERY_FLAG_NUMBER, + WINHTTP_HEADER_NAME_BY_INDEX, + @dwSize, dwBytes, + dwIndex); + if result then Exit; + dwError := GetLastError; + + // Hack: WinHttpQueryHeaders() sometimes returns error 122 even if the buffer is large enough + // According to the docs, WINHTTP_QUERY_FLAG_NUMBER always returns a 32 bit number (which it does!) + // but for some strange reason since win 10 build 18636.815 passing a 4 bytes buffer is not enough anymore + if dwError = ERROR_INSUFFICIENT_BUFFER then begin + dwBytes := sizeof(bytes); + FillChar( bytes[0], dwBytes, 0); + result := WinHttpQueryHeaders( FHandle, + WINHTTP_QUERY_CONTENT_LENGTH or WINHTTP_QUERY_FLAG_NUMBER, + WINHTTP_HEADER_NAME_BY_INDEX, + @bytes[0], dwBytes, + dwIndex); + if result then begin + ASSERT( dwBytes = SizeOf(dwSize)); // result is still a DWORD + Move( bytes[0], dwSize, SizeOf(dwSize)); // copy over result data + Exit; + end; + end; + + // header may just not be there + dwSize := MAXINT; // we don't know, just return something useful + ASSERT( dwError = ERROR_WINHTTP_HEADER_NOT_FOUND); // anything else would be an real error +end; + + +function TWinHTTPRequestImpl.QueryHttpStatus( out dwStatusCode : DWORD; out sStatusText : string) : Boolean; +var dwBytes, dwError, dwIndex : DWORD; +begin + // HTTP status code + dwIndex := DWORD( WINHTTP_NO_HEADER_INDEX); + dwBytes := SizeOf(dwStatusCode); + result := WinHttpQueryHeaders( FHandle, + WINHTTP_QUERY_STATUS_CODE or WINHTTP_QUERY_FLAG_NUMBER, + WINHTTP_HEADER_NAME_BY_INDEX, + @dwStatusCode, dwBytes, + dwIndex); + if not result then begin + dwStatusCode := 0; // no data + dwError := GetLastError; + if dwError <> ERROR_WINHTTP_HEADER_NOT_FOUND then ASSERT(FALSE); // anything else would be an real error + end; + + // HTTP status text + dwIndex := DWORD( WINHTTP_NO_HEADER_INDEX); + dwBytes := 0; + result := WinHttpQueryHeaders( FHandle, + WINHTTP_QUERY_STATUS_TEXT, + WINHTTP_HEADER_NAME_BY_INDEX, + WINHTTP_NO_OUTPUT_BUFFER, // we need to determine the size first + dwBytes, // will receive the required buffer size + dwIndex); + if dwBytes > 0 then begin // allocate buffer and call again to get the value + SetLength( sStatusText, dwBytes div SizeOf(Char)); + dwBytes := Length(sStatusText) * SizeOf(Char); + result := WinHttpQueryHeaders( FHandle, + WINHTTP_QUERY_STATUS_TEXT, + WINHTTP_HEADER_NAME_BY_INDEX, + @sStatusText[1], dwBytes, + dwIndex); + end; + if result + then SetLength( sStatusText, StrLen(PChar(sStatusText))) // get rid of any terminating #0 chars + else begin + sStatusText := ''; // no data + dwError := GetLastError; + if dwError <> ERROR_WINHTTP_HEADER_NOT_FOUND then ASSERT(FALSE); // anything else would be an real error + end; + + // do we have at least something? + result := (dwStatusCode <> 0) or (sStatusText <> ''); +end; + + +{ TWinHTTPUrlImpl } + +constructor TWinHTTPUrlImpl.Create(const aUri: UnicodeString); +begin + inherited Create; + CrackUrl( aUri) +end; + + +destructor TWinHTTPUrlImpl.Destroy; +begin + inherited Destroy; +end; + + +procedure TWinHTTPUrlImpl.CrackURL( const value : UnicodeString); +const FLAGS = 0; // no special operations, leave components as-is +var components : URL_COMPONENTS; +begin + FillChar(components, SizeOf(components), 0); + components.dwStructSize := SizeOf(components); + + if value <> '' then begin + { For the WinHttpCrackUrl function, [...] if the pointer member is NULL but the + length member is not zero, both the pointer and length members are returned. } + components.dwSchemeLength := DWORD(-1); + components.dwHostNameLength := DWORD(-1); + components.dwUserNameLength := DWORD(-1); + components.dwPasswordLength := DWORD(-1); + components.dwUrlPathLength := DWORD(-1); + components.dwExtraInfoLength := DWORD(-1); + + WinHttpCrackUrl( PWideChar(value), Length(value), FLAGS, components); + end; + + FNumScheme := components.nScheme; + FPort := components.nPort; + SetString( FScheme, components.lpszScheme, components.dwSchemeLength); + SetString( FHostName, components.lpszHostName, components.dwHostNameLength); + SetString( FUserName, components.lpszUserName, components.dwUserNameLength); + SetString( FPassword, components.lpszPassword, components.dwPasswordLength); + SetString( FUrlPath, components.lpszUrlPath, components.dwUrlPathLength); + SetString( FExtraInfo, components.lpszExtraInfo, components.dwExtraInfoLength); +end; + + +function TWinHTTPUrlImpl.BuildUrl : UnicodeString; +const FLAGS = 0; // no special operations, leave components as-is +var components : URL_COMPONENTS; + dwChars : DWORD; +begin + FillChar(components, SizeOf(components), 0); + components.dwStructSize := SizeOf(components); + components.lpszScheme := PWideChar(FScheme); + components.dwSchemeLength := Length(FScheme); + components.lpszHostName := PWideChar(FHostName); + components.dwHostNameLength := Length(FHostName); + components.nPort := FPort; + components.lpszUserName := PWideChar(FUserName); + components.dwUserNameLength := Length(FUserName); + components.lpszPassword := PWideChar(FPassword); + components.dwPasswordLength := Length(FPassword); + components.lpszUrlPath := PWideChar(FUrlPath); + components.dwUrlPathLength := Length(FUrlPath); + components.lpszExtraInfo := PWideChar(FExtraInfo); + components.dwExtraInfoLength := Length(FExtraInfo); + + WinHttpCreateUrl( components, FLAGS, nil, dwChars); + if dwChars = 0 + then result := '' + else begin + SetLength( result, dwChars + 1); + WinHttpCreateUrl( components, FLAGS, @result[1], dwChars); + SetLength( result, dwChars); // cut off terminating #0 + end; +end; + + +function TWinHTTPUrlImpl.GetExtraInfo: UnicodeString; +begin + result := FExtraInfo; +end; + +function TWinHTTPUrlImpl.GetHostName: UnicodeString; +begin + result := FHostName; +end; + +function TWinHTTPUrlImpl.GetNumScheme: INTERNET_SCHEME; +begin + result := FNumScheme; +end; + +function TWinHTTPUrlImpl.GetPassword: UnicodeString; +begin + result := FPassword; +end; + +function TWinHTTPUrlImpl.GetPort: INTERNET_PORT; +begin + result := FPort; +end; + +function TWinHTTPUrlImpl.GetScheme: UnicodeString; +begin + result := FScheme; +end; + +function TWinHTTPUrlImpl.GetUrlPath: UnicodeString; +begin + result := FUrlPath; +end; + +function TWinHTTPUrlImpl.GetUserName: UnicodeString; +begin + result := FUserName; +end; + +procedure TWinHTTPUrlImpl.SetExtraInfo(const value: UnicodeString); +begin + FExtraInfo := value; +end; + +procedure TWinHTTPUrlImpl.SetHostName(const value: UnicodeString); +begin + FHostName := value; +end; + +procedure TWinHTTPUrlImpl.SetPassword(const value: UnicodeString); +begin + FPassword := value; +end; + +procedure TWinHTTPUrlImpl.SetPort(const value: INTERNET_PORT); +begin + FPort := value; +end; + +procedure TWinHTTPUrlImpl.SetScheme(const value: UnicodeString); +begin + FScheme := value; +end; + +procedure TWinHTTPUrlImpl.SetUrlPath(const value: UnicodeString); +begin + FUrlPath := value; +end; + +procedure TWinHTTPUrlImpl.SetUserName(const value: UnicodeString); +begin + FUserName := value; +end; + + +end. + + diff --git a/lib/delphi/src/Thrift.pas b/lib/delphi/src/Thrift.pas index 8293d0778b1..1926b11ef32 100644 --- a/lib/delphi/src/Thrift.pas +++ b/lib/delphi/src/Thrift.pas @@ -23,18 +23,19 @@ interface uses SysUtils, + Thrift.Utils, Thrift.Exception, Thrift.Protocol; const - Version = '0.12.0'; + Version = '0.14.0'; type TException = Thrift.Exception.TException; // compatibility alias TApplicationExceptionSpecializedClass = class of TApplicationExceptionSpecialized; - TApplicationException = class( TException) + TApplicationException = class( TException, IBase, ISupportsToString) public type {$SCOPEDENUMS ON} @@ -52,10 +53,18 @@ TApplicationException = class( TException) UnsupportedClientType ); {$SCOPEDENUMS OFF} - private - function GetType: TExceptionType; - protected + strict private + FExceptionType : TExceptionType; + + strict protected + function QueryInterface(const IID: TGUID; out Obj): HResult; stdcall; + function _AddRef: Integer; stdcall; + function _Release: Integer; stdcall; + + strict protected constructor HiddenCreate(const Msg: string); + class function GetSpecializedExceptionType(AType: TExceptionType): TApplicationExceptionSpecializedClass; + public // purposefully hide inherited constructor class function Create(const Msg: string): TApplicationException; overload; deprecated 'Use specialized TApplicationException types (or regenerate from IDL)'; @@ -63,7 +72,10 @@ TApplicationException = class( TException) class function Create( AType: TExceptionType): TApplicationException; overload; deprecated 'Use specialized TApplicationException types (or regenerate from IDL)'; class function Create( AType: TExceptionType; const msg: string): TApplicationException; overload; deprecated 'Use specialized TApplicationException types (or regenerate from IDL)'; - class function GetSpecializedExceptionType(AType: TExceptionType): TApplicationExceptionSpecializedClass; + function Type_: TExceptionType; virtual; + + procedure IBase_Read( const iprot: IProtocol); + procedure IBase.Read = IBase_Read; class function Read( const iprot: IProtocol): TApplicationException; procedure Write( const oprot: IProtocol ); @@ -71,42 +83,74 @@ TApplicationException = class( TException) // Needed to remove deprecation warning TApplicationExceptionSpecialized = class abstract (TApplicationException) + strict protected + class function GetType: TApplicationException.TExceptionType; virtual; abstract; public constructor Create(const Msg: string); + function Type_: TApplicationException.TExceptionType; override; + end; + + TApplicationExceptionUnknown = class (TApplicationExceptionSpecialized) + strict protected + class function GetType: TApplicationException.TExceptionType; override; + end; + + TApplicationExceptionUnknownMethod = class (TApplicationExceptionSpecialized) + strict protected + class function GetType: TApplicationException.TExceptionType; override; + end; + + TApplicationExceptionInvalidMessageType = class (TApplicationExceptionSpecialized) + strict protected + class function GetType: TApplicationException.TExceptionType; override; + end; + + TApplicationExceptionWrongMethodName = class (TApplicationExceptionSpecialized) + strict protected + class function GetType: TApplicationException.TExceptionType; override; + end; + + TApplicationExceptionBadSequenceID = class (TApplicationExceptionSpecialized) + strict protected + class function GetType: TApplicationException.TExceptionType; override; + end; + + TApplicationExceptionMissingResult = class (TApplicationExceptionSpecialized) + strict protected + class function GetType: TApplicationException.TExceptionType; override; + end; + + TApplicationExceptionInternalError = class (TApplicationExceptionSpecialized) + strict protected + class function GetType: TApplicationException.TExceptionType; override; + end; + + TApplicationExceptionProtocolError = class (TApplicationExceptionSpecialized) + strict protected + class function GetType: TApplicationException.TExceptionType; override; + end; + + TApplicationExceptionInvalidTransform = class (TApplicationExceptionSpecialized) + strict protected + class function GetType: TApplicationException.TExceptionType; override; + end; + + TApplicationExceptionInvalidProtocol = class (TApplicationExceptionSpecialized) + strict protected + class function GetType: TApplicationException.TExceptionType; override; + end; + + TApplicationExceptionUnsupportedClientType = class (TApplicationExceptionSpecialized) + strict protected + class function GetType: TApplicationException.TExceptionType; override; end; - TApplicationExceptionUnknown = class (TApplicationExceptionSpecialized); - TApplicationExceptionUnknownMethod = class (TApplicationExceptionSpecialized); - TApplicationExceptionInvalidMessageType = class (TApplicationExceptionSpecialized); - TApplicationExceptionWrongMethodName = class (TApplicationExceptionSpecialized); - TApplicationExceptionBadSequenceID = class (TApplicationExceptionSpecialized); - TApplicationExceptionMissingResult = class (TApplicationExceptionSpecialized); - TApplicationExceptionInternalError = class (TApplicationExceptionSpecialized); - TApplicationExceptionProtocolError = class (TApplicationExceptionSpecialized); - TApplicationExceptionInvalidTransform = class (TApplicationExceptionSpecialized); - TApplicationExceptionInvalidProtocol = class (TApplicationExceptionSpecialized); - TApplicationExceptionUnsupportedClientType = class (TApplicationExceptionSpecialized); implementation { TApplicationException } -function TApplicationException.GetType: TExceptionType; -begin - if Self is TApplicationExceptionUnknownMethod then Result := TExceptionType.UnknownMethod - else if Self is TApplicationExceptionInvalidMessageType then Result := TExceptionType.InvalidMessageType - else if Self is TApplicationExceptionWrongMethodName then Result := TExceptionType.WrongMethodName - else if Self is TApplicationExceptionBadSequenceID then Result := TExceptionType.BadSequenceID - else if Self is TApplicationExceptionMissingResult then Result := TExceptionType.MissingResult - else if Self is TApplicationExceptionInternalError then Result := TExceptionType.InternalError - else if Self is TApplicationExceptionProtocolError then Result := TExceptionType.ProtocolError - else if Self is TApplicationExceptionInvalidTransform then Result := TExceptionType.InvalidTransform - else if Self is TApplicationExceptionInvalidProtocol then Result := TExceptionType.InvalidProtocol - else if Self is TApplicationExceptionUnsupportedClientType then Result := TExceptionType.UnsupportedClientType - else Result := TExceptionType.Unknown; -end; - constructor TApplicationException.HiddenCreate(const Msg: string); begin inherited Create(Msg); @@ -134,6 +178,31 @@ class function TApplicationException.Create( AType: TExceptionType; const msg: s Result := GetSpecializedExceptionType(AType).Create(msg); end; + +function TApplicationException.QueryInterface(const IID: TGUID; out Obj): HResult; +begin + if GetInterface(IID, Obj) + then result := S_OK + else result := E_NOINTERFACE; +end; + +function TApplicationException._AddRef: Integer; +begin + result := -1; // not refcounted +end; + +function TApplicationException._Release: Integer; +begin + result := -1; // not refcounted +end; + + +function TApplicationException.Type_: TExceptionType; +begin + result := FExceptionType; +end; + + class function TApplicationException.GetSpecializedExceptionType(AType: TExceptionType): TApplicationExceptionSpecializedClass; begin case AType of @@ -148,56 +217,66 @@ class function TApplicationException.GetSpecializedExceptionType(AType: TExcepti TExceptionType.InvalidProtocol: Result := TApplicationExceptionInvalidProtocol; TExceptionType.UnsupportedClientType: Result := TApplicationExceptionUnsupportedClientType; else + ASSERT( TExceptionType.Unknown = aType); Result := TApplicationExceptionUnknown; end; end; -class function TApplicationException.Read( const iprot: IProtocol): TApplicationException; + +procedure TApplicationException.IBase_Read( const iprot: IProtocol); var field : TThriftField; - msg : string; - typ : TExceptionType; struc : TThriftStruct; begin - msg := ''; - typ := TExceptionType.Unknown; struc := iprot.ReadStructBegin; while ( True ) do begin field := iprot.ReadFieldBegin; - if ( field.Type_ = TType.Stop) then - begin + if ( field.Type_ = TType.Stop) then begin Break; end; case field.Id of 1 : begin - if ( field.Type_ = TType.String_) then - begin - msg := iprot.ReadString; - end else - begin + if ( field.Type_ = TType.String_) then begin + Exception(Self).Message := iprot.ReadString; + end else begin TProtocolUtil.Skip( iprot, field.Type_ ); end; end; 2 : begin - if ( field.Type_ = TType.I32) then - begin - typ := TExceptionType( iprot.ReadI32 ); - end else - begin + if ( field.Type_ = TType.I32) then begin + FExceptionType := TExceptionType( iprot.ReadI32 ); + end else begin TProtocolUtil.Skip( iprot, field.Type_ ); end; - end else - begin + end else begin TProtocolUtil.Skip( iprot, field.Type_); end; end; iprot.ReadFieldEnd; end; iprot.ReadStructEnd; - Result := GetSpecializedExceptionType(typ).Create(msg); +end; + + +class function TApplicationException.Read( const iprot: IProtocol): TApplicationException; +var instance : TApplicationException; + base : IBase; +begin + instance := TApplicationException.CreateFmt('',[]); + try + if Supports( instance, IBase, base) then try + base.Read(iprot); + finally + base := nil; // clear ref before free + end; + + result := GetSpecializedExceptionType(instance.Type_).Create( Exception(instance).Message); + finally + instance.Free; + end; end; procedure TApplicationException.Write( const oprot: IProtocol); @@ -209,8 +288,7 @@ procedure TApplicationException.Write( const oprot: IProtocol); Init(field); oprot.WriteStructBegin( struc ); - if Message <> '' then - begin + if Message <> '' then begin field.Name := 'message'; field.Type_ := TType.String_; field.Id := 1; @@ -223,7 +301,7 @@ procedure TApplicationException.Write( const oprot: IProtocol); field.Type_ := TType.I32; field.Id := 2; oprot.WriteFieldBegin(field); - oprot.WriteI32(Integer(GetType)); + oprot.WriteI32(Integer(Type_)); oprot.WriteFieldEnd(); oprot.WriteFieldStop(); oprot.WriteStructEnd(); @@ -236,4 +314,68 @@ constructor TApplicationExceptionSpecialized.Create(const Msg: string); inherited HiddenCreate(Msg); end; +function TApplicationExceptionSpecialized.Type_: TApplicationException.TExceptionType; +begin + result := GetType; +end; + + +{ specialized TApplicationExceptions } + +class function TApplicationExceptionUnknownMethod.GetType : TApplicationException.TExceptionType; +begin + result := TExceptionType.UnknownMethod; +end; + +class function TApplicationExceptionInvalidMessageType.GetType : TApplicationException.TExceptionType; +begin + result := TExceptionType.InvalidMessageType; +end; + +class function TApplicationExceptionWrongMethodName.GetType : TApplicationException.TExceptionType; +begin + result := TExceptionType.WrongMethodName; +end; + +class function TApplicationExceptionBadSequenceID.GetType : TApplicationException.TExceptionType; +begin + result := TExceptionType.BadSequenceID; +end; + +class function TApplicationExceptionMissingResult.GetType : TApplicationException.TExceptionType; +begin + result := TExceptionType.MissingResult; +end; + +class function TApplicationExceptionInternalError.GetType : TApplicationException.TExceptionType; +begin + result := TExceptionType.InternalError; +end; + +class function TApplicationExceptionProtocolError.GetType : TApplicationException.TExceptionType; +begin + result := TExceptionType.ProtocolError; +end; + +class function TApplicationExceptionInvalidTransform.GetType : TApplicationException.TExceptionType; +begin + result := TExceptionType.InvalidTransform; +end; + +class function TApplicationExceptionInvalidProtocol.GetType : TApplicationException.TExceptionType; +begin + result := TExceptionType.InvalidProtocol; +end; + +class function TApplicationExceptionUnsupportedClientType.GetType : TApplicationException.TExceptionType; +begin + result := TExceptionType.UnsupportedClientType; +end; + +class function TApplicationExceptionUnknown.GetType : TApplicationException.TExceptionType; +begin + result := TExceptionType.Unknown; +end; + + end. diff --git a/lib/delphi/test/Performance/DataFactory.pas b/lib/delphi/test/Performance/DataFactory.pas new file mode 100644 index 00000000000..e131822a3a6 --- /dev/null +++ b/lib/delphi/test/Performance/DataFactory.pas @@ -0,0 +1,176 @@ +// Licensed to the Apache Software Foundation(ASF) under one +// or more contributor license agreements.See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership.The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +unit DataFactory; + +interface + +uses + SysUtils, + Thrift.Collections, + Thrift.Test; + +type + TestDataFactory = class + strict protected + class function CreateSetField(const count : Integer) : IHashSet< IInsanity>; static; + class function CreateInsanity(const count : Integer) : IInsanity; static; + class function CreateBytesArray(const count : Integer) : TBytes; static; + class function CreateXtructs(const count : Integer) : IThriftList< IXtruct>; static; + class function CreateXtruct(const count : Integer) : IXtruct; static; + class function CreateListField(const count : Integer) : IThriftList< IThriftDictionary< IHashSet< Integer>, IThriftDictionary< Integer, IHashSet< IThriftList< IThriftDictionary< IInsanity, string>>>>>>; static; + class function CreateUserMap(const count : Integer) : IThriftDictionary< TNumberz, Int64>; static; + class function CreateListFieldData(const count : Integer) : IThriftDictionary< IHashSet< Integer>, IThriftDictionary< Integer, IHashSet< IThriftList< IThriftDictionary< IInsanity, string>>>>>; static; + class function CreateIntHashSet(const count : Integer) : IHashSet< Integer>; static; + class function CreateListFieldDataDict(const count : Integer) : IThriftDictionary< Integer, IHashSet< IThriftList< IThriftDictionary< IInsanity, string>>>>; static; + class function CreateListFieldDataDictValue(const count : Integer) : IHashSet< IThriftList< IThriftDictionary< IInsanity, string>>>; static; + class function CreateListFieldDataDictValueList(const count : Integer) : IThriftList< IThriftDictionary< IInsanity, string>>; static; + class function CreateListFieldDataDictValueListDict(const count : Integer) : IThriftDictionary< IInsanity, string>; static; + public + class function CreateCrazyNesting(const count : Integer = 10) : ICrazyNesting; static; + end; + +implementation + + +class function TestDataFactory.CreateCrazyNesting(const count : Integer = 10) : ICrazyNesting; +begin + if (count <= 0) + then Exit(nil); + + result := TCrazyNestingImpl.Create; + result.Binary_field := CreateBytesArray(count); + result.List_field := CreateListField(count); + result.Set_field := CreateSetField(count); + result.String_field := Format('data level %d', [count]); +end; + +class function TestDataFactory.CreateSetField(const count : Integer) : IHashSet< IInsanity>; +var i : Integer; +begin + result := THashSetImpl< IInsanity>.Create; + for i := 0 to count-1 do begin + result.Add(CreateInsanity(count)); + end; +end; + +class function TestDataFactory.CreateInsanity(const count : Integer) : IInsanity; +begin + result := TInsanityImpl.Create; + result.UserMap := CreateUserMap(count); + result.Xtructs := CreateXtructs(count); +end; + +class function TestDataFactory.CreateXtructs(const count : Integer) : IThriftList< IXtruct>; +var i : Integer; +begin + result := TThriftListImpl< IXtruct>.Create; + for i := 0 to count-1 do begin + result.Add(CreateXtruct(count)); + end; +end; + +class function TestDataFactory.CreateXtruct(const count : Integer) : IXtruct; +begin + result := TXtructImpl.Create; + result.Byte_thing := SmallInt(count mod 128); + result.I32_thing := count; + result.I64_thing := count; + result.String_thing := Format('data level %d', [count]); +end; + +class function TestDataFactory.CreateUserMap(const count : Integer) : IThriftDictionary< TNumberz, Int64>; +begin + result := TThriftDictionaryImpl< TNumberz, Int64>.Create; + result.Add(TNumberz.ONE, count); + result.Add(TNumberz.TWO, count); + result.Add(TNumberz.THREE, count); + result.Add(TNumberz.FIVE, count); + result.Add(TNumberz.SIX, count); + result.Add(TNumberz.EIGHT, count); +end; + +class function TestDataFactory.CreateListField(const count : Integer) : IThriftList< IThriftDictionary< IHashSet< Integer>, IThriftDictionary< Integer, IHashSet< IThriftList< IThriftDictionary< IInsanity, string>>>>>>; +var i : Integer; +begin + result := TThriftListImpl< IThriftDictionary< IHashSet< Integer>, IThriftDictionary< Integer, IHashSet< IThriftList< IThriftDictionary< IInsanity, string>>>>>>.Create; + for i := 0 to count-1 do begin + result.Add(CreateListFieldData(count)); + end; +end; + +class function TestDataFactory.CreateListFieldData(const count : Integer) : IThriftDictionary< IHashSet< Integer>, IThriftDictionary< Integer, IHashSet< IThriftList< IThriftDictionary< IInsanity, string>>>>>; +var i : Integer; +begin + result := TThriftDictionaryImpl< IHashSet< Integer>, IThriftDictionary< Integer, IHashSet< IThriftList< IThriftDictionary< IInsanity, string>>>>>.Create; + for i := 0 to count-1 do begin + result.Add( CreateIntHashSet(count), CreateListFieldDataDict(count)); + end; +end; + +class function TestDataFactory.CreateIntHashSet(const count : Integer) : IHashSet< Integer>; +var i : Integer; +begin + result := THashSetImpl< Integer>.Create; + for i := 0 to count-1 do begin + result.Add(i); + end; +end; + +class function TestDataFactory.CreateListFieldDataDict(const count : Integer) : IThriftDictionary< Integer, IHashSet< IThriftList< IThriftDictionary< IInsanity, string>>>>; +var i : Integer; +begin + result := TThriftDictionaryImpl< Integer, IHashSet< IThriftList< IThriftDictionary< IInsanity, string>>>>.Create; + for i := 0 to count-1 do begin + result.Add(i, CreateListFieldDataDictValue(count)); + end; +end; + +class function TestDataFactory.CreateListFieldDataDictValue(const count : Integer) : IHashSet< IThriftList< IThriftDictionary< IInsanity, string>>>; +var i : Integer; +begin + result := THashSetImpl< IThriftList< IThriftDictionary< IInsanity, string>>>.Create; + for i := 0 to count-1 do begin + result.Add( CreateListFieldDataDictValueList(count)); + end; +end; + +class function TestDataFactory.CreateListFieldDataDictValueList(const count : Integer) : IThriftList< IThriftDictionary< IInsanity, string>>; +var i : Integer; +begin + result := TThriftListImpl< IThriftDictionary< IInsanity, string>>.Create; + for i := 0 to count-1 do begin + result.Add(CreateListFieldDataDictValueListDict(count)); + end; +end; + +class function TestDataFactory.CreateListFieldDataDictValueListDict(const count : Integer) : IThriftDictionary< IInsanity, string>; +begin + result := TThriftDictionaryImpl< IInsanity, string>.Create; + result.Add(CreateInsanity(count), Format('data level %d', [count])); +end; + +class function TestDataFactory.CreateBytesArray(const count : Integer) : TBytes; +var i : Integer; +begin + SetLength( result, count); + for i := 0 to count-1 do begin + result[i] := i mod $FF; + end; +end; + +end. + diff --git a/lib/delphi/test/Performance/PerfTests.pas b/lib/delphi/test/Performance/PerfTests.pas new file mode 100644 index 00000000000..e485212b0c0 --- /dev/null +++ b/lib/delphi/test/Performance/PerfTests.pas @@ -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. +unit PerfTests; + +interface + +uses + Windows, Classes, SysUtils, + Thrift.Collections, + Thrift.Configuration, + Thrift.Test, + Thrift.Protocol, + Thrift.Protocol.JSON, + Thrift.Protocol.Compact, + Thrift.Transport, + Thrift.Stream, + ConsoleHelper, + TestConstants, + DataFactory; + +type + TPerformanceTests = class + strict private + FTestdata : ICrazyNesting; + FMemBuffer : TMemoryStream; + FTransport : ITransport; + FConfig : IThriftConfiguration; + + procedure ProtocolPeformanceTest; + procedure RunTest( const ptyp : TKnownProtocol; const layered : TLayeredTransport); + function GenericProtocolFactory(const ptyp : TKnownProtocol; const layered : TLayeredTransport; const forWrite : Boolean) : IProtocol; + function GetProtocolTransportName(const ptyp : TKnownProtocol; const layered : TLayeredTransport) : string; + public + class function Execute : Integer; + end; + + +implementation + + +// not available in all versions, so make sure we have this one imported +function IsDebuggerPresent: BOOL; stdcall; external KERNEL32 name 'IsDebuggerPresent'; + + +class function TPerformanceTests.Execute : Integer; +var instance : TPerformanceTests; +begin + instance := TPerformanceTests.Create; + instance.ProtocolPeformanceTest; + + // debug only + if IsDebuggerPresent then begin + Console.Write('Hit ENTER ...'); + ReadLn; + end; + + result := 0; +end; + + +procedure TPerformanceTests.ProtocolPeformanceTest; +var layered : TLayeredTransport; +begin + Console.WriteLine('Setting up for ProtocolPeformanceTest ...'); + FTestdata := TestDataFactory.CreateCrazyNesting(); + + for layered := Low(TLayeredTransport) to High(TLayeredTransport) do begin + RunTest( TKnownProtocol.prot_Binary, layered); + RunTest( TKnownProtocol.prot_Compact, layered); + RunTest( TKnownProtocol.prot_JSON, layered); + end; +end; + + +procedure TPerformanceTests.RunTest( const ptyp : TKnownProtocol; const layered : TLayeredTransport); +var freq, start, stop : Int64; + proto : IProtocol; + restored : ICrazyNesting; +begin + QueryPerformanceFrequency( freq); + + FConfig := TThriftConfigurationImpl.Create; + + proto := GenericProtocolFactory( ptyp, layered, TRUE); + QueryPerformanceCounter( start); + FTestdata.Write(proto); + FTransport.Flush; + QueryPerformanceCounter( stop); + Console.WriteLine( Format('RunTest(%s): write = %d msec', [ + GetProtocolTransportName(ptyp,layered), + Round(1000.0*(stop-start)/freq) + ])); + + restored := TCrazyNestingImpl.Create; + proto := GenericProtocolFactory( ptyp, layered, FALSE); + QueryPerformanceCounter( start); + restored.Read(proto); + QueryPerformanceCounter( stop); + Console.WriteLine( Format('RunTest(%s): read = %d msec', [ + GetProtocolTransportName(ptyp,layered), + Round(1000.0*(stop-start)/freq) + ])); +end; + + +function TPerformanceTests.GenericProtocolFactory(const ptyp : TKnownProtocol; const layered : TLayeredTransport; const forWrite : Boolean) : IProtocol; +var newBuf : TMemoryStream; + stream : IThriftStream; + trans : IStreamTransport; +const COPY_ENTIRE_STREAM = 0; +begin + // read happens after write here, so let's take over the written bytes + newBuf := TMemoryStream.Create; + if not forWrite then newBuf.CopyFrom( FMemBuffer, COPY_ENTIRE_STREAM); + FMemBuffer := newBuf; + FMemBuffer.Position := 0; + + // layered transports anyone? + stream := TThriftStreamAdapterDelphi.Create( newBuf, TRUE); + if forWrite + then trans := TStreamTransportImpl.Create( nil, stream, FConfig) + else trans := TStreamTransportImpl.Create( stream, nil, FConfig); + case layered of + trns_Framed : FTransport := TFramedTransportImpl.Create( trans); + trns_Buffered : FTransport := TBufferedTransportImpl.Create( trans); + else + FTransport := trans; + end; + + if not FTransport.IsOpen + then FTransport.Open; + + case ptyp of + prot_Binary : result := TBinaryProtocolImpl.Create(trans); + prot_Compact : result := TCompactProtocolImpl.Create(trans); + prot_JSON : result := TJSONProtocolImpl.Create(trans); + else + ASSERT(FALSE); + end; +end; + + +function TPerformanceTests.GetProtocolTransportName(const ptyp : TKnownProtocol; const layered : TLayeredTransport) : string; +begin + case layered of + trns_Framed : result := ' + framed'; + trns_Buffered : result := ' + buffered'; + else + result := ''; + end; + + case ptyp of + prot_Binary : result := 'binary' + result; + prot_Compact : result := 'compact' + result; + prot_JSON : result := 'JSON' + result; + else + ASSERT(FALSE); + end; +end; + + +end. + diff --git a/lib/delphi/test/TestClient.pas b/lib/delphi/test/TestClient.pas index 0fa43b0be66..6c962ab17ab 100644 --- a/lib/delphi/test/TestClient.pas +++ b/lib/delphi/test/TestClient.pas @@ -29,6 +29,8 @@ {$DEFINE SupportsAsync} {$ifend} +{$WARN SYMBOL_PLATFORM OFF} // Win32Check + interface uses @@ -38,15 +40,21 @@ interface Generics.Collections, TestConstants, ConsoleHelper, + PerfTests, Thrift, Thrift.Protocol.Compact, Thrift.Protocol.JSON, Thrift.Protocol, Thrift.Transport.Pipes, + Thrift.Transport.WinHTTP, + Thrift.Transport.MsxmlHTTP, Thrift.Transport, Thrift.Stream, Thrift.Test, + Thrift.WinHTTP, Thrift.Utils, + + Thrift.Configuration, Thrift.Collections; type @@ -86,7 +94,8 @@ TClientThread = class( TThread ) Empty, // Edge case: the zero-length empty binary Normal, // Fairly small array of usual size (256 bytes) ByteArrayTest, // THRIFT-4454 Large writes/reads may cause range check errors in debug mode - PipeWriteLimit // THRIFT-4372 Pipe write operations across a network are limited to 65,535 bytes per write. + PipeWriteLimit, // THRIFT-4372 Pipe write operations across a network are limited to 65,535 bytes per write. + FifteenMB // quite a bit of data, but still below the default max frame size ); private @@ -115,6 +124,7 @@ TClientThread = class( TThread ) procedure InitializeProtocolTransportStack; procedure ShutdownProtocolTransportStack; + function InitializeHttpTransport( const aTimeoutSetting : Integer; const aConfig : IThriftConfiguration = nil) : IHTTPClient; procedure JSONProtocolReadWriteTest; function PrepareBinaryData( aRandomDist : Boolean; aSize : TTestSize) : TBytes; @@ -140,7 +150,7 @@ TTestClient = class class procedure PrintCmdLineHelp; class procedure InvalidArgs; public - class function Execute( const args: array of string) : Byte; + class function Execute( const arguments: array of string) : Byte; end; @@ -181,18 +191,17 @@ class procedure TTestClient.PrintCmdLineHelp; const HELPTEXT = ' [options]'#10 + #10 + 'Allowed options:'#10 - + ' -h [ --help ] produce help message'#10 - + ' --host arg (=localhost) Host to connect'#10 - + ' --port arg (=9090) Port number to connect'#10 - + ' --domain-socket arg Domain Socket (e.g. /tmp/ThriftTest.thrift),'#10 - + ' instead of host and port'#10 - + ' --named-pipe arg Windows Named Pipe (e.g. MyThriftPipe)'#10 - + ' --anon-pipes hRead hWrite Windows Anonymous Pipes pair (handles)'#10 - + ' --transport arg (=sockets) Transport: buffered, framed, http, evhttp'#10 - + ' --protocol arg (=binary) Protocol: binary, compact, json'#10 - + ' --ssl Encrypted Transport using SSL'#10 - + ' -n [ --testloops ] arg (=1) Number of Tests'#10 - + ' -t [ --threads ] arg (=1) Number of Test threads'#10 + + ' -h | --help Produces this help message'#10 + + ' --host=arg (localhost) Host to connect'#10 + + ' --port=arg (9090) Port number to connect'#10 + + ' --pipe=arg Windows Named Pipe (e.g. MyThriftPipe)'#10 + + ' --anon-pipes hRead hWrite Windows Anonymous Pipes pair (handles)'#10 + + ' --transport=arg (sockets) Transport: buffered, framed, http, winhttp'#10 + + ' --protocol=arg (binary) Protocol: binary, compact, json'#10 + + ' --ssl Encrypted Transport using SSL'#10 + + ' -n=num | --testloops=num (1) Number of Tests'#10 + + ' -t=num | --threads=num (1) Number of Test threads'#10 + + ' --performance Run the built-in performance test (no other arguments)'#10 ; begin Writeln( ChangeFileExt(ExtractFileName(ParamStr(0)),'') + HELPTEXT); @@ -205,11 +214,22 @@ class procedure TTestClient.InvalidArgs; Abort; end; -class function TTestClient.Execute(const args: array of string) : Byte; +class function TTestClient.Execute(const arguments: array of string) : Byte; + + function IsSwitch( const aArgument, aSwitch : string; out sValue : string) : Boolean; + begin + sValue := ''; + result := (Copy( aArgument, 1, Length(aSwitch)) = aSwitch); + if result then begin + if (Copy( aArgument, 1, Length(aSwitch)+1) = (aSwitch+'=')) + then sValue := Copy( aArgument, Length(aSwitch)+2, MAXINT); + end; + end; + var - i : Integer; + iArg : Integer; threadExitCode : Byte; - s : string; + sArg, sValue : string; threads : array of TThread; dtStart : TDateTime; test : Integer; @@ -230,90 +250,86 @@ class function TTestClient.Execute(const args: array of string) : Byte; end; try - i := 0; - while ( i < Length(args) ) do begin - s := args[i]; - Inc( i); - - if (s = '-h') or (s = '--help') then begin + iArg := 0; + while iArg < Length(arguments) do begin + sArg := arguments[iArg]; + Inc(iArg); + + if IsSwitch( sArg, '-h', sValue) + or IsSwitch( sArg, '--help', sValue) + then begin // -h [ --help ] produce help message PrintCmdLineHelp; result := $FF; // all tests failed Exit; end - else if s = '--host' then begin + else if IsSwitch( sArg, '--host', sValue) then begin // --host arg (=localhost) Host to connect - setup.host := args[i]; - Inc( i); + setup.host := sValue; end - else if s = '--port' then begin + else if IsSwitch( sArg, '--port', sValue) then begin // --port arg (=9090) Port number to connect - s := args[i]; - Inc( i); - setup.port := StrToIntDef(s,0); + setup.port := StrToIntDef(sValue,0); if setup.port <= 0 then InvalidArgs; end - else if s = '--domain-socket' then begin + else if IsSwitch( sArg, '--domain-socket', sValue) then begin // --domain-socket arg Domain Socket (e.g. /tmp/ThriftTest.thrift), instead of host and port raise Exception.Create('domain-socket not supported'); end - else if s = '--named-pipe' then begin - // --named-pipe arg Windows Named Pipe (e.g. MyThriftPipe) + // --pipe arg Windows Named Pipe (e.g. MyThriftPipe) + else if IsSwitch( sArg, '--pipe', sValue) then begin + // --pipe arg Windows Named Pipe (e.g. MyThriftPipe) setup.endpoint := trns_NamedPipes; - setup.sPipeName := args[i]; - Inc( i); + setup.sPipeName := sValue; Console.WriteLine('Using named pipe ('+setup.sPipeName+')'); end - else if s = '--anon-pipes' then begin + else if IsSwitch( sArg, '--anon-pipes', sValue) then begin // --anon-pipes hRead hWrite Windows Anonymous Pipes pair (handles) setup.endpoint := trns_AnonPipes; - setup.hAnonRead := THandle( StrToIntDef( args[i], Integer(INVALID_HANDLE_VALUE))); - Inc( i); - setup.hAnonWrite := THandle( StrToIntDef( args[i], Integer(INVALID_HANDLE_VALUE))); - Inc( i); + setup.hAnonRead := THandle( StrToIntDef( arguments[iArg], Integer(INVALID_HANDLE_VALUE))); + Inc(iArg); + setup.hAnonWrite := THandle( StrToIntDef( arguments[iArg], Integer(INVALID_HANDLE_VALUE))); + Inc(iArg); Console.WriteLine('Using anonymous pipes ('+IntToStr(Integer(setup.hAnonRead))+' and '+IntToStr(Integer(setup.hAnonWrite))+')'); end - else if s = '--transport' then begin - // --transport arg (=sockets) Transport: buffered, framed, http, evhttp - s := args[i]; - Inc( i); - - if s = 'buffered' then Include( setup.layered, trns_Buffered) - else if s = 'framed' then Include( setup.layered, trns_Framed) - else if s = 'http' then setup.endpoint := trns_Http - else if s = 'evhttp' then setup.endpoint := trns_EvHttp + else if IsSwitch( sArg, '--transport', sValue) then begin + // --transport arg (=sockets) Transport: buffered, framed, http, winhttp, evhttp + if sValue = 'buffered' then Include( setup.layered, trns_Buffered) + else if sValue = 'framed' then Include( setup.layered, trns_Framed) + else if sValue = 'http' then setup.endpoint := trns_MsXmlHttp + else if sValue = 'winhttp' then setup.endpoint := trns_WinHttp + else if sValue = 'evhttp' then setup.endpoint := trns_EvHttp // recognized, but not supported else InvalidArgs; end - else if s = '--protocol' then begin + else if IsSwitch( sArg, '--protocol', sValue) then begin // --protocol arg (=binary) Protocol: binary, compact, json - s := args[i]; - Inc( i); - - if s = 'binary' then setup.protType := prot_Binary - else if s = 'compact' then setup.protType := prot_Compact - else if s = 'json' then setup.protType := prot_JSON + if sValue = 'binary' then setup.protType := prot_Binary + else if sValue = 'compact' then setup.protType := prot_Compact + else if sValue = 'json' then setup.protType := prot_JSON else InvalidArgs; end - else if s = '--ssl' then begin + else if IsSwitch( sArg, '--ssl', sValue) then begin // --ssl Encrypted Transport using SSL setup.useSSL := TRUE; end - else if (s = '-n') or (s = '--testloops') then begin + else if IsSwitch( sArg, '-n', sValue) or IsSwitch( sArg, '--testloops', sValue) then begin // -n [ --testloops ] arg (=1) Number of Tests - FNumIteration := StrToIntDef( args[i], 0); - Inc( i); + FNumIteration := StrToIntDef( sValue, 0); if FNumIteration <= 0 then InvalidArgs; end - else if (s = '-t') or (s = '--threads') then begin + else if IsSwitch( sArg, '-t', sValue) or IsSwitch( sArg, '--threads', sValue) then begin // -t [ --threads ] arg (=1) Number of Test threads - FNumThread := StrToIntDef( args[i], 0); - Inc( i); + FNumThread := StrToIntDef( sValue, 0); if FNumThread <= 0 then InvalidArgs; end + else if IsSwitch( sArg, '--performance', sValue) then begin + result := TPerformanceTests.Execute; + Exit; + end else begin InvalidArgs; end; @@ -524,12 +540,12 @@ procedure TClientThread.ClientTest; // random binary small for testsize := Low(TTestSize) to High(TTestSize) do begin binOut := PrepareBinaryData( TRUE, testsize); - Console.WriteLine('testBinary('+BytesToHex(binOut)+')'); + Console.WriteLine('testBinary('+IntToStr(Length(binOut))+' bytes)'); try binIn := client.testBinary(binOut); - Expect( Length(binOut) = Length(binIn), 'testBinary(): length '+IntToStr(Length(binOut))+' = '+IntToStr(Length(binIn))); + Expect( Length(binOut) = Length(binIn), 'testBinary('+IntToStr(Length(binOut))+' bytes): '+IntToStr(Length(binIn))+' bytes received'); i32 := Min( Length(binOut), Length(binIn)); - Expect( CompareMem( binOut, binIn, i32), 'testBinary('+BytesToHex(binOut)+') = '+BytesToHex(binIn)); + Expect( CompareMem( binOut, binIn, i32), 'testBinary('+IntToStr(Length(binOut))+' bytes): validating received data'); except on e:TApplicationException do Console.WriteLine('testBinary(): '+e.Message); on e:Exception do Expect( FALSE, 'testBinary(): Unexpected exception "'+e.ClassName+'": '+e.Message); @@ -1010,6 +1026,7 @@ function TClientThread.PrepareBinaryData( aRandomDist : Boolean; aSize : TTestSi Normal : SetLength( result, $100); ByteArrayTest : SetLength( result, SizeOf(TByteArray) + 128); PipeWriteLimit : SetLength( result, 65535 + 128); + FifteenMB : SetLength( result, 15 * 1024 * 1024); else raise EArgumentException.Create('aSize'); end; @@ -1053,6 +1070,7 @@ procedure TClientThread.JSONProtocolReadWriteTest; var prot : IProtocol; stm : TStringStream; list : TThriftList; + config : IThriftConfiguration; binary, binRead, emptyBinary : TBytes; i,iErr : Integer; const @@ -1074,6 +1092,8 @@ procedure TClientThread.JSONProtocolReadWriteTest; try StartTestGroup( 'JsonProtocolTest', test_Unknown); + config := TThriftConfigurationImpl.Create; + // prepare binary data binary := PrepareBinaryData( FALSE, Normal); SetLength( emptyBinary, 0); // empty binary data block @@ -1081,7 +1101,7 @@ procedure TClientThread.JSONProtocolReadWriteTest; // output setup prot := TJSONProtocolImpl.Create( TStreamTransportImpl.Create( - nil, TThriftStreamAdapterDelphi.Create( stm, FALSE))); + nil, TThriftStreamAdapterDelphi.Create( stm, FALSE), config)); // write Init( list, TType.String_, 9); @@ -1104,7 +1124,7 @@ procedure TClientThread.JSONProtocolReadWriteTest; stm.Position := 0; prot := TJSONProtocolImpl.Create( TStreamTransportImpl.Create( - TThriftStreamAdapterDelphi.Create( stm, FALSE), nil)); + TThriftStreamAdapterDelphi.Create( stm, FALSE), nil, config)); // read and compare list := prot.ReadListBegin; @@ -1146,7 +1166,7 @@ procedure TClientThread.JSONProtocolReadWriteTest; stm.Position := 0; prot := TJSONProtocolImpl.Create( TStreamTransportImpl.Create( - TThriftStreamAdapterDelphi.Create( stm, FALSE), nil)); + TThriftStreamAdapterDelphi.Create( stm, FALSE), nil, config)); Expect( prot.ReadString = SOLIDUS_EXCPECTED, 'Solidus encoding'); @@ -1157,12 +1177,12 @@ procedure TClientThread.JSONProtocolReadWriteTest; stm.Size := 0; prot := TJSONProtocolImpl.Create( TStreamTransportImpl.Create( - nil, TThriftStreamAdapterDelphi.Create( stm, FALSE))); + nil, TThriftStreamAdapterDelphi.Create( stm, FALSE), config)); prot.WriteString( G_CLEF_AND_CYRILLIC_TEXT); stm.Position := 0; prot := TJSONProtocolImpl.Create( TStreamTransportImpl.Create( - TThriftStreamAdapterDelphi.Create( stm, FALSE), nil)); + TThriftStreamAdapterDelphi.Create( stm, FALSE), nil, config)); Expect( prot.ReadString = G_CLEF_AND_CYRILLIC_TEXT, 'Writing JSON with chars > 8 bit'); // Widechars should work with hex-encoding too. Do they? @@ -1172,7 +1192,7 @@ procedure TClientThread.JSONProtocolReadWriteTest; stm.Position := 0; prot := TJSONProtocolImpl.Create( TStreamTransportImpl.Create( - TThriftStreamAdapterDelphi.Create( stm, FALSE), nil)); + TThriftStreamAdapterDelphi.Create( stm, FALSE), nil, config)); Expect( prot.ReadString = G_CLEF_AND_CYRILLIC_TEXT, 'Reading JSON with chars > 8 bit'); @@ -1315,11 +1335,59 @@ procedure TClientThread.Execute; end; +function TClientThread.InitializeHttpTransport( const aTimeoutSetting : Integer; const aConfig : IThriftConfiguration) : IHTTPClient; +var sUrl : string; + comps : URL_COMPONENTS; + dwChars : DWORD; +begin + ASSERT( FSetup.endpoint in [trns_MsxmlHttp, trns_WinHttp]); + + if FSetup.useSSL + then sUrl := 'https://' + else sUrl := 'http://'; + + sUrl := sUrl + FSetup.host; + + // add the port number if necessary and at the right place + FillChar( comps, SizeOf(comps), 0); + comps.dwStructSize := SizeOf(comps); + comps.dwSchemeLength := MAXINT; + comps.dwHostNameLength := MAXINT; + comps.dwUserNameLength := MAXINT; + comps.dwPasswordLength := MAXINT; + comps.dwUrlPathLength := MAXINT; + comps.dwExtraInfoLength := MAXINT; + Win32Check( WinHttpCrackUrl( PChar(sUrl), Length(sUrl), 0, comps)); + case FSetup.port of + 80 : if FSetup.useSSL then comps.nPort := FSetup.port; + 443 : if not FSetup.useSSL then comps.nPort := FSetup.port; + else + if FSetup.port > 0 then comps.nPort := FSetup.port; + end; + dwChars := Length(sUrl) + 64; + SetLength( sUrl, dwChars); + Win32Check( WinHttpCreateUrl( comps, 0, @sUrl[1], dwChars)); + SetLength( sUrl, dwChars); + + + Console.WriteLine('Target URL: '+sUrl); + case FSetup.endpoint of + trns_MsxmlHttp : result := TMsxmlHTTPClientImpl.Create( sUrl, aConfig); + trns_WinHttp : result := TWinHTTPClientImpl.Create( sUrl, aConfig); + else + raise Exception.Create(ENDPOINT_TRANSPORTS[FSetup.endpoint]+' unhandled case'); + end; + + result.DnsResolveTimeout := aTimeoutSetting; + result.ConnectionTimeout := aTimeoutSetting; + result.SendTimeout := aTimeoutSetting; + result.ReadTimeout := aTimeoutSetting; +end; + + procedure TClientThread.InitializeProtocolTransportStack; -var - streamtrans : IStreamTransport; - http : IHTTPClient; - sUrl : string; +var streamtrans : IStreamTransport; + canSSL : Boolean; const DEBUG_TIMEOUT = 30 * 1000; RELEASE_TIMEOUT = DEFAULT_THRIFT_TIMEOUT; @@ -1329,31 +1397,19 @@ procedure TClientThread.InitializeProtocolTransportStack; // needed for HTTP clients as they utilize the MSXML COM components OleCheck( CoInitialize( nil)); + canSSL := FALSE; case FSetup.endpoint of trns_Sockets: begin Console.WriteLine('Using sockets ('+FSetup.host+' port '+IntToStr(FSetup.port)+')'); - streamtrans := TSocketImpl.Create( FSetup.host, FSetup.port ); + streamtrans := TSocketImpl.Create( FSetup.host, FSetup.port); FTransport := streamtrans; end; - trns_Http: begin + trns_MsxmlHttp, + trns_WinHttp: begin Console.WriteLine('Using HTTPClient'); - if FSetup.useSSL - then sUrl := 'http://' - else sUrl := 'https://'; - sUrl := sUrl + FSetup.host; - case FSetup.port of - 80 : if FSetup.useSSL then sUrl := sUrl + ':'+ IntToStr(FSetup.port); - 443 : if not FSetup.useSSL then sUrl := sUrl + ':'+ IntToStr(FSetup.port); - else - if FSetup.port > 0 then sUrl := sUrl + ':'+ IntToStr(FSetup.port); - end; - http := THTTPClientImpl.Create( sUrl); - http.DnsResolveTimeout := HTTP_TIMEOUTS; - http.ConnectionTimeout := HTTP_TIMEOUTS; - http.SendTimeout := HTTP_TIMEOUTS; - http.ReadTimeout := HTTP_TIMEOUTS; - FTransport := http; + FTransport := InitializeHttpTransport( HTTP_TIMEOUTS); + canSSL := TRUE; end; trns_EvHttp: begin @@ -1366,7 +1422,7 @@ procedure TClientThread.InitializeProtocolTransportStack; end; trns_AnonPipes: begin - streamtrans := TAnonymousPipeTransportImpl.Create( FSetup.hAnonRead, FSetup.hAnonWrite, FALSE); + streamtrans := TAnonymousPipeTransportImpl.Create( FSetup.hAnonRead, FSetup.hAnonWrite, FALSE, PIPE_TIMEOUT); FTransport := streamtrans; end; @@ -1383,7 +1439,7 @@ procedure TClientThread.InitializeProtocolTransportStack; FTransport := TBufferedTransportImpl.Create( streamtrans, 32); // small buffer to test read() end; - if FSetup.useSSL then begin + if FSetup.useSSL and not canSSL then begin raise Exception.Create('SSL/TLS not implemented'); end; diff --git a/lib/delphi/test/TestConstants.pas b/lib/delphi/test/TestConstants.pas index 37969dc205a..ae3b3e8a355 100644 --- a/lib/delphi/test/TestConstants.pas +++ b/lib/delphi/test/TestConstants.pas @@ -39,13 +39,15 @@ interface TEndpointTransport = ( trns_Sockets, - trns_Http, + trns_MsxmlHttp, + trns_WinHttp, trns_NamedPipes, trns_AnonPipes, trns_EvHttp // as listed on http://thrift.apache.org/test ); TLayeredTransport = ( + trns_None, trns_Buffered, trns_Framed ); @@ -60,10 +62,10 @@ interface = ('Binary', 'JSON', 'Compact'); LAYERED_TRANSPORTS : array[TLayeredTransport] of string - = ('Buffered', 'Framed'); + = ('None', 'Buffered', 'Framed'); ENDPOINT_TRANSPORTS : array[TEndpointTransport] of string - = ('Sockets', 'Http', 'Named Pipes','Anon Pipes', 'EvHttp'); + = ('Sockets', 'Http', 'WinHttp', 'Named Pipes','Anon Pipes', 'EvHttp'); // defaults are: read=false, write=true BINARY_STRICT_READ = FALSE; diff --git a/lib/delphi/test/TestServer.pas b/lib/delphi/test/TestServer.pas index 69cb17521d2..c9b374d1fbd 100644 --- a/lib/delphi/test/TestServer.pas +++ b/lib/delphi/test/TestServer.pas @@ -36,6 +36,7 @@ interface Thrift.Protocol.JSON, Thrift.Protocol.Compact, Thrift.Collections, + Thrift.Configuration, Thrift.Utils, Thrift.Test, Thrift, @@ -55,9 +56,9 @@ TTestServer = class end; TTestHandlerImpl = class( TInterfacedObject, ITestHandler ) - private + strict private FServer : IServer; - protected + strict protected procedure testVoid(); function testBool(thing: Boolean): Boolean; function testString(const thing: string): string; @@ -87,9 +88,10 @@ TTestHandlerImpl = class( TInterfacedObject, ITestHandler ) class procedure PrintCmdLineHelp; class procedure InvalidArgs; + class function IsSwitch( const aArgument, aSwitch : string; out sValue : string) : Boolean; class procedure LaunchAnonPipeChild( const app : string; const transport : IAnonymousPipeServerTransport); - class procedure Execute( const args: array of string); + class procedure Execute( const arguments : array of string); end; implementation @@ -144,26 +146,24 @@ function TTestServer.TTestHandlerImpl.testDouble( const thing: Double): Double; function TTestServer.TTestHandlerImpl.testBinary(const thing: TBytes): TBytes; begin - Console.WriteLine('testBinary("' + BytesToHex( thing ) + '")'); + Console.WriteLine('testBinary('+IntToStr(Length(thing)) + ' bytes)'); Result := thing; end; function TTestServer.TTestHandlerImpl.testEnum(thing: TNumberz): TNumberz; begin - Console.WriteLine('testEnum(' + IntToStr( Integer( thing)) + ')'); + Console.WriteLine('testEnum(' + EnumUtils.ToString(Ord(thing)) + ')'); Result := thing; end; procedure TTestServer.TTestHandlerImpl.testException(const arg: string); begin Console.WriteLine('testException(' + arg + ')'); - if ( arg = 'Xception') then - begin + if ( arg = 'Xception') then begin raise TXception.Create( 1001, arg); end; - if (arg = 'TException') then - begin + if (arg = 'TException') then begin raise TException.Create('TException'); end; @@ -191,7 +191,10 @@ function TTestServer.TTestHandlerImpl.testInsanity( insane : IThriftDictionary>; begin - Console.WriteLine('testInsanity()'); + Console.Write('testInsanity('); + if argument <> nil then Console.Write(argument.ToString); + Console.WriteLine(')'); + (** * So you think you've got this all worked, out eh? @@ -222,49 +225,20 @@ function TTestServer.TTestHandlerImpl.testInsanity( Result := insane; end; -function TTestServer.TTestHandlerImpl.testList( - const thing: IThriftList): IThriftList; -var - first : Boolean; - elem : Integer; +function TTestServer.TTestHandlerImpl.testList( const thing: IThriftList): IThriftList; begin - Console.Write('testList({'); - first := True; - for elem in thing do - begin - if first then - begin - first := False; - end else - begin - Console.Write(', '); - end; - Console.Write( IntToStr( elem)); - end; - Console.WriteLine('})'); + Console.Write('testList('); + if thing <> nil then Console.Write(thing.ToString); + Console.WriteLine(')'); Result := thing; end; function TTestServer.TTestHandlerImpl.testMap( const thing: IThriftDictionary): IThriftDictionary; -var - first : Boolean; - key : Integer; begin - Console.Write('testMap({'); - first := True; - for key in thing.Keys do - begin - if (first) then - begin - first := false; - end else - begin - Console.Write(', '); - end; - Console.Write(IntToStr(key) + ' => ' + IntToStr( thing[key])); - end; - Console.WriteLine('})'); + Console.Write('testMap('); + if thing <> nil then Console.Write(thing.ToString); + Console.WriteLine(')'); Result := thing; end; @@ -313,12 +287,11 @@ function TTestServer.TTestHandlerImpl.testMultiException( const arg0, arg1: stri x2 : TXception2; begin Console.WriteLine('testMultiException(' + arg0 + ', ' + arg1 + ')'); - if ( arg0 = 'Xception') then - begin + if ( arg0 = 'Xception') then begin raise TXception.Create( 1001, 'This is an Xception'); // test the new rich CTOR - end else - if ( arg0 = 'Xception2') then - begin + end; + + if ( arg0 = 'Xception2') then begin x2 := TXception2.Create; // the old way still works too? x2.ErrorCode := 2002; x2.Struct_thing := TXtructImpl.Create; @@ -332,17 +305,11 @@ function TTestServer.TTestHandlerImpl.testMultiException( const arg0, arg1: stri end; function TTestServer.TTestHandlerImpl.testNest( const thing: IXtruct2): IXtruct2; -var - temp : IXtruct; begin - temp := thing.Struct_thing; - Console.WriteLine('testNest({' + - IntToStr( thing.Byte_thing) + ', {' + - '"' + temp.String_thing + '", ' + - IntToStr( temp.Byte_thing) + ', ' + - IntToStr( temp.I32_thing) + ', ' + - IntToStr( temp.I64_thing) + '}, ' + - IntToStr( temp.I32_thing) + '})'); + Console.Write('testNest('); + if thing <> nil then Console.Write(thing.ToString); + Console.WriteLine(')'); + Result := thing; end; @@ -353,34 +320,18 @@ procedure TTestServer.TTestHandlerImpl.testOneway(secondsToSleep: Integer); Console.WriteLine('testOneway finished'); end; -function TTestServer.TTestHandlerImpl.testSet( - const thing: IHashSet):IHashSet; -var - first : Boolean; - elem : Integer; +function TTestServer.TTestHandlerImpl.testSet( const thing: IHashSet):IHashSet; begin - Console.Write('testSet({'); - first := True; + Console.Write('testSet('); + if thing <> nil then Console.Write(thing.ToString); + Console.WriteLine(')');; - for elem in thing do - begin - if first then - begin - first := False; - end else - begin - Console.Write( ', '); - end; - Console.Write( IntToStr( elem)); - end; - Console.WriteLine('})'); Result := thing; end; procedure TTestServer.TTestHandlerImpl.testStop; begin - if FServer <> nil then - begin + if FServer <> nil then begin FServer.Stop; end; end; @@ -399,24 +350,11 @@ function TTestServer.TTestHandlerImpl.testString( const thing: string): string; function TTestServer.TTestHandlerImpl.testStringMap( const thing: IThriftDictionary): IThriftDictionary; -var - first : Boolean; - key : string; begin - Console.Write('testStringMap({'); - first := True; - for key in thing.Keys do - begin - if (first) then - begin - first := false; - end else - begin - Console.Write(', '); - end; - Console.Write(key + ' => ' + thing[key]); - end; - Console.WriteLine('})'); + Console.Write('testStringMap('); + if thing <> nil then Console.Write(thing.ToString); + Console.WriteLine(')'); + Result := thing; end; @@ -433,11 +371,10 @@ procedure TTestServer.TTestHandlerImpl.TestVoid; function TTestServer.TTestHandlerImpl.testStruct( const thing: IXtruct): IXtruct; begin - Console.WriteLine('testStruct({' + - '"' + thing.String_thing + '", ' + - IntToStr( thing.Byte_thing) + ', ' + - IntToStr( thing.I32_thing) + ', ' + - IntToStr( thing.I64_thing)); + Console.Write('testStruct('); + if thing <> nil then Console.Write(thing.ToString); + Console.WriteLine(')'); + Result := thing; end; @@ -449,18 +386,16 @@ class procedure TTestServer.PrintCmdLineHelp; const HELPTEXT = ' [options]'#10 + #10 + 'Allowed options:'#10 - + ' -h [ --help ] produce help message'#10 - + ' --port arg (=9090) Port number to listen'#10 - + ' --domain-socket arg Unix Domain Socket (e.g. /tmp/ThriftTest.thrift)'#10 - + ' --named-pipe arg Windows Named Pipe (e.g. MyThriftPipe)'#10 - + ' --server-type arg (=simple) type of server, "simple", "thread-pool",'#10 - + ' "threaded", or "nonblocking"'#10 - + ' --transport arg (=socket) transport: buffered, framed, http, anonpipe'#10 - + ' --protocol arg (=binary) protocol: binary, compact, json'#10 - + ' --ssl Encrypted Transport using SSL'#10 - + ' --processor-events processor-events'#10 - + ' -n [ --workers ] arg (=4) Number of thread pools workers. Only valid for'#10 - + ' thread-pool server type'#10 + + ' -h | --help Produces this help message'#10 + + ' --port=arg (9090) Port number to connect'#10 + + ' --pipe=arg Windows Named Pipe (e.g. MyThriftPipe)'#10 + + ' --anon-pipes Windows Anonymous Pipes server, auto-starts client.exe'#10 + + ' --server-type=arg (simple) Type of server (simple, thread-pool, threaded, nonblocking)'#10 + + ' --transport=arg (sockets) Transport: buffered, framed, anonpipe'#10 + + ' --protocol=arg (binary) Protocol: binary, compact, json'#10 + + ' --ssl Encrypted Transport using SSL'#10 + + ' --processor-events Enable processor-events'#10 + + ' -n=num | --workers=num (4) Number of thread-pool server workers'#10 ; begin Console.WriteLine( ChangeFileExt(ExtractFileName(ParamStr(0)),'') + HELPTEXT); @@ -473,6 +408,16 @@ class procedure TTestServer.InvalidArgs; Abort; end; +class function TTestServer.IsSwitch( const aArgument, aSwitch : string; out sValue : string) : Boolean; +begin + sValue := ''; + result := (Copy( aArgument, 1, Length(aSwitch)) = aSwitch); + if result then begin + if (Copy( aArgument, 1, Length(aSwitch)+1) = (aSwitch+'=')) + then sValue := Copy( aArgument, Length(aSwitch)+2, MAXINT); + end; +end; + class procedure TTestServer.LaunchAnonPipeChild( const app : string; const transport : IAnonymousPipeServerTransport); //Launch child process and pass R/W anonymous pipe handles on cmd line. //This is a simple example and does not include elevation or other @@ -495,7 +440,7 @@ class procedure TTestServer.LaunchAnonPipeChild( const app : string; const trans sArg := ParamStr(i); // add anonymous handles and quote strings where appropriate - if sArg = '-anon' + if sArg = '--anon-pipes' then sArg := sArg +' '+ sHandles else begin if Pos(' ',sArg) > 0 @@ -510,11 +455,11 @@ class procedure TTestServer.LaunchAnonPipeChild( const app : string; const trans Win32Check( CreateProcess( nil, PChar(sCmdLine), nil,nil,TRUE,0,nil,nil,si,pi)); CloseHandle( pi.hThread); - CloseHandle( pi.hProcess); + CloseHandle( pi.hProcess); end; -class procedure TTestServer.Execute( const args: array of string); +class procedure TTestServer.Execute( const arguments : array of string); var Port : Integer; ServerEvents : Boolean; @@ -527,8 +472,8 @@ class procedure TTestServer.Execute( const args: array of string); namedpipe : INamedPipeServerTransport; TransportFactory : ITransportFactory; ProtocolFactory : IProtocolFactory; - i, numWorker : Integer; - s : string; + iArg, numWorker : Integer; + sArg, sValue : string; protType : TKnownProtocol; servertype : TServerType; endpoint : TEndpointTransport; @@ -546,82 +491,68 @@ class procedure TTestServer.Execute( const args: array of string); sPipeName := ''; numWorker := 4; - i := 0; - while ( i < Length(args) ) do begin - s := args[i]; - Inc(i); + iArg := 0; + while iArg < Length(arguments) do begin + sArg := arguments[iArg]; + Inc(iArg); // Allowed options: - if (s = '-h') or (s = '--help') then begin - // -h [ --help ] produce help message + if IsSwitch( sArg, '-h', sValue) + or IsSwitch( sArg, '--help', sValue) + then begin + // -h | --help produce help message PrintCmdLineHelp; Exit; end - else if (s = '--port') then begin + else if IsSwitch( sArg, '--port', sValue) then begin // --port arg (=9090) Port number to listen - s := args[i]; - Inc(i); - Port := StrToIntDef( s, Port); + Port := StrToIntDef( sValue, Port); end - else if (s = '--domain-socket') then begin - // --domain-socket arg Unix Domain Socket (e.g. /tmp/ThriftTest.thrift) - raise Exception.Create('domain-socket not supported'); + else if IsSwitch( sArg, '--anon-pipes', sValue) then begin + endpoint := trns_AnonPipes; end - else if (s = '--named-pipe') then begin - // --named-pipe arg Windows Named Pipe (e.g. MyThriftPipe) + else if IsSwitch( sArg, '--pipe', sValue) then begin + // --pipe arg Windows Named Pipe (e.g. MyThriftPipe) endpoint := trns_NamedPipes; - sPipeName := args[i]; // -pipe - Inc( i ); + sPipeName := sValue; // --pipe end - else if (s = '--server-type') then begin + else if IsSwitch( sArg, '--server-type', sValue) then begin // --server-type arg (=simple) type of server, // arg = "simple", "thread-pool", "threaded", or "nonblocking" - s := args[i]; - Inc(i); - - if s = 'simple' then servertype := srv_Simple - else if s = 'thread-pool' then servertype := srv_Threadpool - else if s = 'threaded' then servertype := srv_Threaded - else if s = 'nonblocking' then servertype := srv_Nonblocking + if sValue = 'simple' then servertype := srv_Simple + else if sValue = 'thread-pool' then servertype := srv_Threadpool + else if sValue = 'threaded' then servertype := srv_Threaded + else if sValue = 'nonblocking' then servertype := srv_Nonblocking else InvalidArgs; end - else if (s = '--transport') then begin + else if IsSwitch( sArg, '--transport', sValue) then begin // --transport arg (=buffered) transport: buffered, framed, http - s := args[i]; - Inc(i); - - if s = 'buffered' then Include( layered, trns_Buffered) - else if s = 'framed' then Include( layered, trns_Framed) - else if s = 'http' then endpoint := trns_Http - else if s = 'anonpipe' then endpoint := trns_AnonPipes + if sValue = 'buffered' then Include( layered, trns_Buffered) + else if sValue = 'framed' then Include( layered, trns_Framed) + else if sValue = 'http' then endpoint := trns_MsxmlHttp + else if sValue = 'winhttp' then endpoint := trns_WinHttp + else if sValue = 'anonpipe' then endpoint := trns_AnonPipes else InvalidArgs; end - else if (s = '--protocol') then begin + else if IsSwitch( sArg, '--protocol', sValue) then begin // --protocol arg (=binary) protocol: binary, compact, json - s := args[i]; - Inc(i); - - if s = 'binary' then protType := prot_Binary - else if s = 'compact' then protType := prot_Compact - else if s = 'json' then protType := prot_JSON + if sValue = 'binary' then protType := prot_Binary + else if sValue = 'compact' then protType := prot_Compact + else if sValue = 'json' then protType := prot_JSON else InvalidArgs; end - else if (s = '--ssl') then begin + else if IsSwitch( sArg, '--ssl', sValue) then begin // --ssl Encrypted Transport using SSL UseSSL := TRUE; end - else if (s = '--processor-events') then begin + else if IsSwitch( sArg, '--processor-events', sValue) then begin // --processor-events processor-events ServerEvents := TRUE; end - else if (s = '-n') or (s = '--workers') then begin + else if IsSwitch( sArg, '-n', sValue) or IsSwitch( sArg, '--workers', sValue) then begin // -n [ --workers ] arg (=4) Number of thread pools workers. // Only valid for thread-pool server type - s := args[i]; - numWorker := StrToIntDef(s,0); - if numWorker > 0 - then Inc(i) - else numWorker := 4; + numWorker := StrToIntDef(sValue,4); end else begin InvalidArgs; @@ -647,16 +578,17 @@ class procedure TTestServer.Execute( const args: array of string); trns_Sockets : begin Console.WriteLine('- sockets (port '+IntToStr(port)+')'); if (trns_Buffered in layered) then Console.WriteLine('- buffered'); - servertrans := TServerSocketImpl.Create( Port, 0, (trns_Buffered in layered)); + servertrans := TServerSocketImpl.Create( Port, DEFAULT_THRIFT_TIMEOUT, (trns_Buffered in layered)); end; - trns_Http : begin - raise Exception.Create(ENDPOINT_TRANSPORTS[endpoint]+' server transport not implemented'); + trns_MsxmlHttp, + trns_WinHttp : begin + raise Exception.Create('HTTP server transport not implemented'); end; trns_NamedPipes : begin Console.WriteLine('- named pipe ('+sPipeName+')'); - namedpipe := TNamedPipeServerTransportImpl.Create( sPipeName, 4096, PIPE_UNLIMITED_INSTANCES); + namedpipe := TNamedPipeServerTransportImpl.Create( sPipeName, 4096, PIPE_UNLIMITED_INSTANCES, INFINITE); servertrans := namedpipe; end; @@ -677,7 +609,7 @@ class procedure TTestServer.Execute( const args: array of string); if (trns_Framed in layered) then begin Console.WriteLine('- framed transport'); - TransportFactory := TFramedTransportImpl.TFactory.Create + TransportFactory := TFramedTransportImpl.TFactory.Create; end else begin TransportFactory := TTransportFactoryImpl.Create; diff --git a/lib/delphi/test/client.dpr b/lib/delphi/test/client.dpr index 06dbd3df767..d4875b8a933 100644 --- a/lib/delphi/test/client.dpr +++ b/lib/delphi/test/client.dpr @@ -24,13 +24,18 @@ program client; uses SysUtils, + DataFactory in 'Performance\DataFactory.pas', + PerfTests in 'Performance\PerfTests.pas', TestClient in 'TestClient.pas', Thrift.Test, // in 'gen-delphi\Thrift.Test.pas', Thrift in '..\src\Thrift.pas', Thrift.Transport in '..\src\Thrift.Transport.pas', Thrift.Socket in '..\src\Thrift.Socket.pas', + Thrift.Configuration in '..\src\Thrift.Configuration.pas', Thrift.Exception in '..\src\Thrift.Exception.pas', Thrift.Transport.Pipes in '..\src\Thrift.Transport.Pipes.pas', + Thrift.Transport.WinHTTP in '..\src\Thrift.Transport.WinHTTP.pas', + Thrift.Transport.MsxmlHTTP in '..\src\Thrift.Transport.MsxmlHTTP.pas', Thrift.Protocol in '..\src\Thrift.Protocol.pas', Thrift.Protocol.JSON in '..\src\Thrift.Protocol.JSON.pas', Thrift.Protocol.Compact in '..\src\Thrift.Protocol.Compact.pas', @@ -39,6 +44,7 @@ uses Thrift.Server in '..\src\Thrift.Server.pas', Thrift.Stream in '..\src\Thrift.Stream.pas', Thrift.TypeRegistry in '..\src\Thrift.TypeRegistry.pas', + Thrift.WinHTTP in '..\src\Thrift.WinHTTP.pas', Thrift.Utils in '..\src\Thrift.Utils.pas'; var diff --git a/lib/delphi/test/keywords/ReservedIncluded.thrift b/lib/delphi/test/keywords/ReservedIncluded.thrift index 8b47a50bce7..1d94dd974bd 100644 --- a/lib/delphi/test/keywords/ReservedIncluded.thrift +++ b/lib/delphi/test/keywords/ReservedIncluded.thrift @@ -22,4 +22,11 @@ namespace delphi SysUtils const i32 integer = 42 +service deprecate_included_inner { + void Foo( ) ( deprecated = "This method has neither 'x' nor \"y\"" ) + void Bar( ) ( deprecated = "Fails to deliver 中文 колбаса" ) + void Baz( ) ( deprecated = "Need this to work with tabs (\t) or Umlauts (äöüÄÖÜß) too" ) + void Deprecated() ( deprecated ) // no comment +} + // EOF diff --git a/lib/delphi/test/keywords/ReservedKeywords.dpr b/lib/delphi/test/keywords/ReservedKeywords.dpr index 1fbc8c1d754..3742b198a6e 100644 --- a/lib/delphi/test/keywords/ReservedKeywords.dpr +++ b/lib/delphi/test/keywords/ReservedKeywords.dpr @@ -3,7 +3,7 @@ program ReservedKeywords; {$APPTYPE CONSOLE} uses - SysUtils, System_; + SysUtils, System_, AnnotationTest; begin try diff --git a/lib/delphi/test/keywords/ReservedKeywords.dproj b/lib/delphi/test/keywords/ReservedKeywords.dproj index 6bd9544bcb3..cc369883a41 100644 --- a/lib/delphi/test/keywords/ReservedKeywords.dproj +++ b/lib/delphi/test/keywords/ReservedKeywords.dproj @@ -65,7 +65,9 @@ - + Delphi.Personality.12 diff --git a/lib/delphi/test/keywords/ReservedKeywords.thrift b/lib/delphi/test/keywords/ReservedKeywords.thrift index 2f49d742c13..08a4d757566 100644 --- a/lib/delphi/test/keywords/ReservedKeywords.thrift +++ b/lib/delphi/test/keywords/ReservedKeywords.thrift @@ -134,5 +134,9 @@ struct Thrift4554_Struct { 4 : optional Thrift4554_Enum Foo } +service deprecate_included_outer extends ReservedIncluded.deprecate_included_inner { + void FooBarBaz() +} + // EOF diff --git a/lib/delphi/test/multiplexed/Multiplex.Client.Main.pas b/lib/delphi/test/multiplexed/Multiplex.Client.Main.pas index 35fdf6f5b73..4b6a0a22168 100644 --- a/lib/delphi/test/multiplexed/Multiplex.Client.Main.pas +++ b/lib/delphi/test/multiplexed/Multiplex.Client.Main.pas @@ -35,6 +35,7 @@ interface Thrift.Transport, Thrift.Stream, Thrift.Collections, + Thrift.Configuration, Benchmark, // in gen-delphi folder Aggr, // in gen-delphi folder Multiplex.Test.Common; @@ -93,8 +94,10 @@ procedure TTestClient.ParseArgs( const args: array of string); procedure TTestClient.Setup; var trans : ITransport; + config : IThriftConfiguration; begin - trans := TSocketImpl.Create( 'localhost', 9090); + config := TThriftConfigurationImpl.Create; + trans := TSocketImpl.Create( 'localhost', 9090, DEFAULT_THRIFT_TIMEOUT, config); trans := TFramedTransportImpl.Create( trans); trans.Open; FProtocol := TBinaryProtocolImpl.Create( trans, TRUE, TRUE); diff --git a/lib/delphi/test/multiplexed/Multiplex.Server.Main.pas b/lib/delphi/test/multiplexed/Multiplex.Server.Main.pas index 3860f5ace0b..a23ff37ea8a 100644 --- a/lib/delphi/test/multiplexed/Multiplex.Server.Main.pas +++ b/lib/delphi/test/multiplexed/Multiplex.Server.Main.pas @@ -35,6 +35,7 @@ interface Thrift.Protocol.Multiplex, Thrift.Processor.Multiplex, Thrift.Collections, + Thrift.Configuration, Thrift.Utils, Thrift, Benchmark, // in gen-delphi folder @@ -156,11 +157,14 @@ class procedure TTestServer.Execute( const args: array of string); aggrProcessor : IProcessor; multiplex : IMultiplexedProcessor; ServerEngine : IServer; + config : IThriftConfiguration; begin try + config := TThriftConfigurationImpl.Create; + // create protocol factory, default to BinaryProtocol ProtocolFactory := TBinaryProtocolImpl.TFactory.Create( TRUE, TRUE); - servertrans := TServerSocketImpl.Create( 9090, 0, FALSE); + servertrans := TServerSocketImpl.Create( 9090, DEFAULT_THRIFT_TIMEOUT, FALSE, config); TransportFactory := TFramedTransportImpl.TFactory.Create; benchHandler := TBenchmarkServiceImpl.Create; diff --git a/lib/delphi/test/multiplexed/Multiplex.Test.Client.dpr b/lib/delphi/test/multiplexed/Multiplex.Test.Client.dpr index 4278d8f0c58..19f8f6adf45 100644 --- a/lib/delphi/test/multiplexed/Multiplex.Test.Client.dpr +++ b/lib/delphi/test/multiplexed/Multiplex.Test.Client.dpr @@ -33,9 +33,11 @@ uses Thrift.Protocol in '..\..\src\Thrift.Protocol.pas', Thrift.Protocol.Multiplex in '..\..\src\Thrift.Protocol.Multiplex.pas', Thrift.Collections in '..\..\src\Thrift.Collections.pas', + Thrift.Configuration in '..\..\src\Thrift.Configuration.pas', Thrift.Server in '..\..\src\Thrift.Server.pas', Thrift.Stream in '..\..\src\Thrift.Stream.pas', Thrift.TypeRegistry in '..\..\src\Thrift.TypeRegistry.pas', + Thrift.WinHTTP in '..\..\src\Thrift.WinHTTP.pas', Thrift.Utils in '..\..\src\Thrift.Utils.pas'; var diff --git a/lib/delphi/test/multiplexed/Multiplex.Test.Server.dpr b/lib/delphi/test/multiplexed/Multiplex.Test.Server.dpr index 120462bf955..307a9c2dc70 100644 --- a/lib/delphi/test/multiplexed/Multiplex.Test.Server.dpr +++ b/lib/delphi/test/multiplexed/Multiplex.Test.Server.dpr @@ -33,9 +33,11 @@ uses Thrift.Protocol in '..\..\src\Thrift.Protocol.pas', Thrift.Protocol.Multiplex in '..\..\src\Thrift.Protocol.Multiplex.pas', Thrift.Processor.Multiplex in '..\..\src\Thrift.Processor.Multiplex.pas', + Thrift.Configuration in '..\..\src\Thrift.Configuration.pas', Thrift.Collections in '..\..\src\Thrift.Collections.pas', Thrift.Server in '..\..\src\Thrift.Server.pas', Thrift.Utils in '..\..\src\Thrift.Utils.pas', + Thrift.WinHTTP in '..\..\src\Thrift.WinHTTP.pas', Thrift.TypeRegistry in '..\..\src\Thrift.TypeRegistry.pas', Thrift.Stream in '..\..\src\Thrift.Stream.pas'; diff --git a/lib/delphi/test/serializer/TestSerializer.Data.pas b/lib/delphi/test/serializer/TestSerializer.Data.pas index 5fc0070e88f..2420e9a2f0b 100644 --- a/lib/delphi/test/serializer/TestSerializer.Data.pas +++ b/lib/delphi/test/serializer/TestSerializer.Data.pas @@ -336,6 +336,10 @@ class function Fixtures.CreateCompactProtoTestStruct : ICompactProtoTestStruct; result.Byte_set_map := TDebugProtoTestConstants.COMPACT_TEST.Byte_set_map; result.Byte_list_map := TDebugProtoTestConstants.COMPACT_TEST.Byte_list_map; + result.Field500 := 500; + result.Field5000 := 5000; + result.Field20000 := 20000; + {$IF cDebugProtoTest_Option_AnsiStr_Binary} result.A_binary := AnsiString( #0#1#2#3#4#5#6#7#8); {$ELSE} diff --git a/lib/delphi/test/serializer/TestSerializer.Tests.pas b/lib/delphi/test/serializer/TestSerializer.Tests.pas new file mode 100644 index 00000000000..83d67b1dc66 --- /dev/null +++ b/lib/delphi/test/serializer/TestSerializer.Tests.pas @@ -0,0 +1,381 @@ +unit TestSerializer.Tests; +(* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + *) + +interface + +uses + Classes, + Windows, + SysUtils, + Generics.Collections, + Thrift, + Thrift.Exception, + Thrift.Socket, + Thrift.Transport, + Thrift.Protocol, + Thrift.Protocol.JSON, + Thrift.Protocol.Compact, + Thrift.Collections, + Thrift.Configuration, + Thrift.Server, + Thrift.Utils, + Thrift.Serializer, + Thrift.Stream, + Thrift.WinHTTP, + Thrift.TypeRegistry, + System_, + DebugProtoTest, + TestSerializer.Data; + + +type + TFactoryPair = record + prot : IProtocolFactory; + trans : ITransportFactory; + end; + + TTestSerializer = class //extends TestCase { + private type + TMethod = ( + mt_Bytes, + mt_Stream + ); + + private + FProtocols : TList< TFactoryPair>; + procedure AddFactoryCombination( const aProto : IProtocolFactory; const aTrans : ITransportFactory); + class function UserFriendlyName( const factory : TFactoryPair) : string; overload; + class function UserFriendlyName( const method : TMethod) : string; overload; + + class function Serialize(const input : IBase; const factory : TFactoryPair) : TBytes; overload; + class procedure Serialize(const input : IBase; const factory : TFactoryPair; const aStream : TStream); overload; + + class procedure Deserialize( const input : TBytes; const target : IBase; const factory : TFactoryPair); overload; + class procedure Deserialize( const input : TStream; const target : IBase; const factory : TFactoryPair); overload; + + class procedure ValidateReadToEnd( const input : TBytes; const serial : TDeserializer); overload; + class procedure ValidateReadToEnd( const input : TStream; const serial : TDeserializer); overload; + + procedure Test_Serializer_Deserializer; + procedure Test_OneOfEach( const method : TMethod; const factory : TFactoryPair; const stream : TFileStream); + procedure Test_CompactStruct( const method : TMethod; const factory : TFactoryPair; const stream : TFileStream); + + public + constructor Create; + destructor Destroy; override; + + procedure RunTests; + end; + + +implementation + + +{ TTestSerializer } + +constructor TTestSerializer.Create; +begin + inherited Create; + FProtocols := TList< TFactoryPair>.Create; + + AddFactoryCombination( TBinaryProtocolImpl.TFactory.Create, nil); + AddFactoryCombination( TCompactProtocolImpl.TFactory.Create, nil); + AddFactoryCombination( TJSONProtocolImpl.TFactory.Create, nil); + + AddFactoryCombination( TBinaryProtocolImpl.TFactory.Create, TFramedTransportImpl.TFactory.Create); + AddFactoryCombination( TCompactProtocolImpl.TFactory.Create, TFramedTransportImpl.TFactory.Create); + AddFactoryCombination( TJSONProtocolImpl.TFactory.Create, TFramedTransportImpl.TFactory.Create); + + AddFactoryCombination( TBinaryProtocolImpl.TFactory.Create, TBufferedTransportImpl.TFactory.Create); + AddFactoryCombination( TCompactProtocolImpl.TFactory.Create, TBufferedTransportImpl.TFactory.Create); + AddFactoryCombination( TJSONProtocolImpl.TFactory.Create, TBufferedTransportImpl.TFactory.Create); +end; + + +destructor TTestSerializer.Destroy; +begin + try + FreeAndNil( FProtocols); + finally + inherited Destroy; + end; +end; + + +procedure TTestSerializer.AddFactoryCombination( const aProto : IProtocolFactory; const aTrans : ITransportFactory); +var rec : TFactoryPair; +begin + rec.prot := aProto; + rec.trans := aTrans; + FProtocols.Add( rec); +end; + + +procedure TTestSerializer.Test_OneOfEach( const method : TMethod; const factory : TFactoryPair; const stream : TFileStream); +var tested, correct : IOneOfEach; + bytes : TBytes; + i : Integer; +begin + // write + tested := Fixtures.CreateOneOfEach; + case method of + mt_Bytes: bytes := Serialize( tested, factory); + mt_Stream: begin + stream.Size := 0; + Serialize( tested, factory, stream); + end + else + ASSERT( FALSE); + end; + + // init + read + tested := TOneOfEachImpl.Create; + case method of + mt_Bytes: Deserialize( bytes, tested, factory); + mt_Stream: begin + stream.Position := 0; + Deserialize( stream, tested, factory); + end + else + ASSERT( FALSE); + end; + + // check + correct := Fixtures.CreateOneOfEach; + ASSERT( tested.Im_true = correct.Im_true); + ASSERT( tested.Im_false = correct.Im_false); + ASSERT( tested.A_bite = correct.A_bite); + ASSERT( tested.Integer16 = correct.Integer16); + ASSERT( tested.Integer32 = correct.Integer32); + ASSERT( tested.Integer64 = correct.Integer64); + ASSERT( Abs( tested.Double_precision - correct.Double_precision) < 1E-12); + ASSERT( tested.Some_characters = correct.Some_characters); + ASSERT( tested.Zomg_unicode = correct.Zomg_unicode); + ASSERT( tested.What_who = correct.What_who); + + ASSERT( Length(tested.Base64) = Length(correct.Base64)); + ASSERT( CompareMem( @tested.Base64[0], @correct.Base64[0], Length(correct.Base64))); + + ASSERT( tested.Byte_list.Count = correct.Byte_list.Count); + for i := 0 to tested.Byte_list.Count-1 + do ASSERT( tested.Byte_list[i] = correct.Byte_list[i]); + + ASSERT( tested.I16_list.Count = correct.I16_list.Count); + for i := 0 to tested.I16_list.Count-1 + do ASSERT( tested.I16_list[i] = correct.I16_list[i]); + + ASSERT( tested.I64_list.Count = correct.I64_list.Count); + for i := 0 to tested.I64_list.Count-1 + do ASSERT( tested.I64_list[i] = correct.I64_list[i]); +end; + + +procedure TTestSerializer.Test_CompactStruct( const method : TMethod; const factory : TFactoryPair; const stream : TFileStream); +var tested, correct : ICompactProtoTestStruct; + bytes : TBytes; +begin + // write + tested := Fixtures.CreateCompactProtoTestStruct; + case method of + mt_Bytes: bytes := Serialize( tested, factory); + mt_Stream: begin + stream.Size := 0; + Serialize( tested, factory, stream); + end + else + ASSERT( FALSE); + end; + + // init + read + correct := TCompactProtoTestStructImpl.Create; + case method of + mt_Bytes: Deserialize( bytes, tested, factory); + mt_Stream: begin + stream.Position := 0; + Deserialize( stream, tested, factory); + end + else + ASSERT( FALSE); + end; + + // check + correct := Fixtures.CreateCompactProtoTestStruct; + ASSERT( correct.Field500 = tested.Field500); + ASSERT( correct.Field5000 = tested.Field5000); + ASSERT( correct.Field20000 = tested.Field20000); +end; + + +procedure TTestSerializer.Test_Serializer_Deserializer; +var factory : TFactoryPair; + stream : TFileStream; + method : TMethod; +begin + stream := TFileStream.Create( 'TestSerializer.dat', fmCreate); + try + for method in [Low(TMethod)..High(TMethod)] do begin + Writeln( UserFriendlyName(method)); + + for factory in FProtocols do begin + Writeln('- '+UserFriendlyName(factory)); + + Test_OneOfEach( method, factory, stream); + Test_CompactStruct( method, factory, stream); + end; + + Writeln; + end; + + finally + stream.Free; + end; +end; + + +class function TTestSerializer.UserFriendlyName( const factory : TFactoryPair) : string; +begin + result := Copy( (factory.prot as TObject).ClassName, 2, MAXINT); + + if factory.trans <> nil + then result := Copy( (factory.trans as TObject).ClassName, 2, MAXINT) +' '+ result; + + result := StringReplace( result, 'Impl', '', [rfReplaceAll]); + result := StringReplace( result, 'Transport.TFactory', '', [rfReplaceAll]); + result := StringReplace( result, 'Protocol.TFactory', '', [rfReplaceAll]); +end; + + +class function TTestSerializer.UserFriendlyName( const method : TMethod) : string; +begin + result := EnumUtils.ToString(Ord(method)); + result := StringReplace( result, 'mt_', '', [rfReplaceAll]); +end; + + +procedure TTestSerializer.RunTests; +begin + try + Test_Serializer_Deserializer; + except + on e:Exception do begin + Writeln( e.ClassName+': '+ e.Message); + Write('Hit ENTER to close ... '); Readln; + end; + end; +end; + + +class function TTestSerializer.Serialize(const input : IBase; const factory : TFactoryPair) : TBytes; +var serial : TSerializer; + config : IThriftConfiguration; +begin + config := TThriftConfigurationImpl.Create; + config.MaxMessageSize := 0; // we don't read anything here + + serial := TSerializer.Create( factory.prot, factory.trans, config); + try + result := serial.Serialize( input); + finally + serial.Free; + end; +end; + + +class procedure TTestSerializer.Serialize(const input : IBase; const factory : TFactoryPair; const aStream : TStream); +var serial : TSerializer; + config : IThriftConfiguration; +begin + config := TThriftConfigurationImpl.Create; + config.MaxMessageSize := 0; // we don't read anything here + + serial := TSerializer.Create( factory.prot, factory.trans, config); + try + serial.Serialize( input, aStream); + finally + serial.Free; + end; +end; + + +class procedure TTestSerializer.Deserialize( const input : TBytes; const target : IBase; const factory : TFactoryPair); +var serial : TDeserializer; + config : IThriftConfiguration; +begin + config := TThriftConfigurationImpl.Create; + config.MaxMessageSize := Length(input); + + serial := TDeserializer.Create( factory.prot, factory.trans, config); + try + serial.Deserialize( input, target); + ValidateReadToEnd( input, serial); + finally + serial.Free; + end; +end; + + +class procedure TTestSerializer.Deserialize( const input : TStream; const target : IBase; const factory : TFactoryPair); +var serial : TDeserializer; + config : IThriftConfiguration; +begin + config := TThriftConfigurationImpl.Create; + config.MaxMessageSize := input.Size; + + serial := TDeserializer.Create( factory.prot, factory.trans, config); + try + serial.Deserialize( input, target); + ValidateReadToEnd( input, serial); + finally + serial.Free; + end; +end; + + +class procedure TTestSerializer.ValidateReadToEnd( const input : TBytes; const serial : TDeserializer); +// we should not have any more byte to read +var dummy : IBase; +begin + try + dummy := TOneOfEachImpl.Create; + serial.Deserialize( input, dummy); + raise EInOutError.Create('Expected exception not thrown?'); + except + on e:TTransportExceptionEndOfFile do {expected}; + on e:Exception do raise; // unexpected + end; +end; + + +class procedure TTestSerializer.ValidateReadToEnd( const input : TStream; const serial : TDeserializer); +// we should not have any more byte to read +var dummy : IBase; +begin + try + input.Position := 0; + dummy := TOneOfEachImpl.Create; + serial.Deserialize( input, dummy); + raise EInOutError.Create('Expected exception not thrown?'); + except + on e:TTransportExceptionEndOfFile do {expected}; + on e:Exception do raise; // unexpected + end; +end; + +end. diff --git a/lib/delphi/test/serializer/TestSerializer.dpr b/lib/delphi/test/serializer/TestSerializer.dpr index 51e22a4cf90..062001461d2 100644 --- a/lib/delphi/test/serializer/TestSerializer.dpr +++ b/lib/delphi/test/serializer/TestSerializer.dpr @@ -22,201 +22,29 @@ program TestSerializer; {$APPTYPE CONSOLE} uses - Classes, Windows, SysUtils, Generics.Collections, + Classes, + Windows, + SysUtils, + Generics.Collections, Thrift in '..\..\src\Thrift.pas', Thrift.Exception in '..\..\src\Thrift.Exception.pas', Thrift.Socket in '..\..\src\Thrift.Socket.pas', Thrift.Transport in '..\..\src\Thrift.Transport.pas', Thrift.Protocol in '..\..\src\Thrift.Protocol.pas', Thrift.Protocol.JSON in '..\..\src\Thrift.Protocol.JSON.pas', + Thrift.Protocol.Compact in '..\..\src\Thrift.Protocol.Compact.pas', Thrift.Collections in '..\..\src\Thrift.Collections.pas', + Thrift.Configuration in '..\..\src\Thrift.Configuration.pas', Thrift.Server in '..\..\src\Thrift.Server.pas', Thrift.Utils in '..\..\src\Thrift.Utils.pas', Thrift.Serializer in '..\..\src\Thrift.Serializer.pas', Thrift.Stream in '..\..\src\Thrift.Stream.pas', + Thrift.WinHTTP in '..\..\src\Thrift.WinHTTP.pas', Thrift.TypeRegistry in '..\..\src\Thrift.TypeRegistry.pas', System_, DebugProtoTest, - TestSerializer.Data; - - - -type - TTestSerializer = class //extends TestCase { - private - FProtocols : TList< IProtocolFactory>; - - class function Serialize(const input : IBase; const factory : IProtocolFactory) : TBytes; overload; - class procedure Serialize(const input : IBase; const factory : IProtocolFactory; const aStream : TStream); overload; - class procedure Deserialize( const input : TBytes; const target : IBase; const factory : IProtocolFactory); overload; - class procedure Deserialize( const input : TStream; const target : IBase; const factory : IProtocolFactory); overload; - - procedure Test_Serializer_Deserializer; - - public - constructor Create; - destructor Destroy; override; - - procedure RunTests; - end; - - - -{ TTestSerializer } - -constructor TTestSerializer.Create; -begin - inherited Create; - FProtocols := TList< IProtocolFactory>.Create; - FProtocols.Add( TBinaryProtocolImpl.TFactory.Create); - //FProtocols.Add( TCompactProtocolImpl.TFactory.Create); - FProtocols.Add( TJSONProtocolImpl.TFactory.Create); -end; - - -destructor TTestSerializer.Destroy; -begin - try - FreeAndNil( FProtocols); - finally - inherited Destroy; - end; -end; - -type TMethod = (mt_Bytes, mt_Stream); - - -procedure TTestSerializer.Test_Serializer_Deserializer; -var level3ooe, correct : IOneOfEach; - factory : IProtocolFactory; - bytes : TBytes; - stream : TFileStream; - i : Integer; - method : TMethod; -begin - correct := Fixtures.CreateOneOfEach; - stream := TFileStream.Create( 'TestSerializer.dat', fmCreate); - try - - for method in [Low(TMethod)..High(TMethod)] do begin - for factory in FProtocols do begin - - // write - level3ooe := Fixtures.CreateOneOfEach; - case method of - mt_Bytes: bytes := Serialize( level3ooe, factory); - mt_Stream: begin - stream.Size := 0; - Serialize( level3ooe, factory, stream); - end - else - ASSERT( FALSE); - end; - - // init + read - level3ooe := TOneOfEachImpl.Create; - case method of - mt_Bytes: Deserialize( bytes, level3ooe, factory); - mt_Stream: begin - stream.Position := 0; - Deserialize( stream, level3ooe, factory); - end - else - ASSERT( FALSE); - end; - - - // check - ASSERT( level3ooe.Im_true = correct.Im_true); - ASSERT( level3ooe.Im_false = correct.Im_false); - ASSERT( level3ooe.A_bite = correct.A_bite); - ASSERT( level3ooe.Integer16 = correct.Integer16); - ASSERT( level3ooe.Integer32 = correct.Integer32); - ASSERT( level3ooe.Integer64 = correct.Integer64); - ASSERT( Abs( level3ooe.Double_precision - correct.Double_precision) < 1E-12); - ASSERT( level3ooe.Some_characters = correct.Some_characters); - ASSERT( level3ooe.Zomg_unicode = correct.Zomg_unicode); - ASSERT( level3ooe.What_who = correct.What_who); - ASSERT( level3ooe.Base64 = correct.Base64); - - ASSERT( level3ooe.Byte_list.Count = correct.Byte_list.Count); - for i := 0 to level3ooe.Byte_list.Count-1 - do ASSERT( level3ooe.Byte_list[i] = correct.Byte_list[i]); - - ASSERT( level3ooe.I16_list.Count = correct.I16_list.Count); - for i := 0 to level3ooe.I16_list.Count-1 - do ASSERT( level3ooe.I16_list[i] = correct.I16_list[i]); - - ASSERT( level3ooe.I64_list.Count = correct.I64_list.Count); - for i := 0 to level3ooe.I64_list.Count-1 - do ASSERT( level3ooe.I64_list[i] = correct.I64_list[i]); - end; - end; - - finally - stream.Free; - end; -end; - - -procedure TTestSerializer.RunTests; -begin - try - Test_Serializer_Deserializer; - except - on e:Exception do begin - Writeln( e.Message); - Write('Hit ENTER to close ... '); Readln; - end; - end; -end; - - -class function TTestSerializer.Serialize(const input : IBase; const factory : IProtocolFactory) : TBytes; -var serial : TSerializer; -begin - serial := TSerializer.Create( factory); - try - result := serial.Serialize( input); - finally - serial.Free; - end; -end; - - -class procedure TTestSerializer.Serialize(const input : IBase; const factory : IProtocolFactory; const aStream : TStream); -var serial : TSerializer; -begin - serial := TSerializer.Create( factory); - try - serial.Serialize( input, aStream); - finally - serial.Free; - end; -end; - - -class procedure TTestSerializer.Deserialize( const input : TBytes; const target : IBase; const factory : IProtocolFactory); -var serial : TDeserializer; -begin - serial := TDeserializer.Create( factory); - try - serial.Deserialize( input, target); - finally - serial.Free; - end; -end; - -class procedure TTestSerializer.Deserialize( const input : TStream; const target : IBase; const factory : IProtocolFactory); -var serial : TDeserializer; -begin - serial := TDeserializer.Create( factory); - try - serial.Deserialize( input, target); - finally - serial.Free; - end; -end; + TestSerializer.Tests in 'TestSerializer.Tests.pas', + TestSerializer.Data in 'TestSerializer.Data.pas'; var test : TTestSerializer; diff --git a/lib/delphi/test/server.dpr b/lib/delphi/test/server.dpr index b5e48a6b6e6..954d0b60684 100644 --- a/lib/delphi/test/server.dpr +++ b/lib/delphi/test/server.dpr @@ -37,9 +37,11 @@ uses Thrift.Protocol.Multiplex in '..\src\Thrift.Protocol.Multiplex.pas', Thrift.Processor.Multiplex in '..\src\Thrift.Processor.Multiplex.pas', Thrift.Collections in '..\src\Thrift.Collections.pas', + Thrift.Configuration in '..\src\Thrift.Configuration.pas', Thrift.Server in '..\src\Thrift.Server.pas', Thrift.TypeRegistry in '..\src\Thrift.TypeRegistry.pas', Thrift.Utils in '..\src\Thrift.Utils.pas', + Thrift.WinHTTP in '..\src\Thrift.WinHTTP.pas', Thrift.Stream in '..\src\Thrift.Stream.pas'; var diff --git a/lib/delphi/test/skip/idl/skiptest_version_1.thrift b/lib/delphi/test/skip/idl/skiptest_version_1.thrift index 8353c5e12f2..4221177e0f2 100644 --- a/lib/delphi/test/skip/idl/skiptest_version_1.thrift +++ b/lib/delphi/test/skip/idl/skiptest_version_1.thrift @@ -24,12 +24,14 @@ namespace * Skiptest.One const i32 SKIPTESTSERVICE_VERSION = 1 -struct Pong { - 1 : optional i32 version1 +enum PingPongEnum { + PingOne = 0, + PongOne = 1, } struct Ping { 1 : optional i32 version1 + 100 : PingPongEnum EnumTest } exception PongFailed { @@ -38,7 +40,7 @@ exception PongFailed { service SkipTestService { - void PingPong( 1: Ping pong) throws (444: PongFailed pof); + Ping PingPong( 1: Ping ping) throws (444: PongFailed pof); } diff --git a/lib/delphi/test/skip/idl/skiptest_version_2.thrift b/lib/delphi/test/skip/idl/skiptest_version_2.thrift index f3352d32774..3ea69f768a5 100644 --- a/lib/delphi/test/skip/idl/skiptest_version_2.thrift +++ b/lib/delphi/test/skip/idl/skiptest_version_2.thrift @@ -24,9 +24,17 @@ namespace * Skiptest.Two const i32 SKIPTESTSERVICE_VERSION = 2 +enum PingPongEnum { + PingOne = 0, + PongOne = 1, + PingTwo = 2, + PongTwo = 3, +} + struct Pong { 1 : optional i32 version1 2 : optional i16 version2 + 100 : PingPongEnum EnumTest } struct Ping { @@ -40,6 +48,7 @@ struct Ping { 16 : optional string strVal 17 : optional Pong structVal 18 : optional map< list< Pong>, set< string>> mapVal + 100 : PingPongEnum EnumTest } exception PingFailed { diff --git a/lib/delphi/test/skip/skiptest_version1.dpr b/lib/delphi/test/skip/skiptest_version1.dpr index 803d6bd00fd..f7cde2f5df9 100644 --- a/lib/delphi/test/skip/skiptest_version1.dpr +++ b/lib/delphi/test/skip/skiptest_version1.dpr @@ -30,9 +30,12 @@ uses Thrift.Transport in '..\..\src\Thrift.Transport.pas', Thrift.Protocol in '..\..\src\Thrift.Protocol.pas', Thrift.Protocol.JSON in '..\..\src\Thrift.Protocol.JSON.pas', + Thrift.Protocol.Compact in '..\..\src\Thrift.Protocol.Compact.pas', Thrift.Collections in '..\..\src\Thrift.Collections.pas', + Thrift.Configuration in '..\..\src\Thrift.Configuration.pas', Thrift.Server in '..\..\src\Thrift.Server.pas', Thrift.Utils in '..\..\src\Thrift.Utils.pas', + Thrift.WinHTTP in '..\..\src\Thrift.WinHTTP.pas', Thrift.TypeRegistry in '..\..\src\Thrift.TypeRegistry.pas', Thrift.Stream in '..\..\src\Thrift.Stream.pas'; @@ -45,6 +48,7 @@ function CreatePing : IPing; begin result := TPingImpl.Create; result.Version1 := Tskiptest_version_1Constants.SKIPTESTSERVICE_VERSION; + result.EnumTest := TPingPongEnum.PingOne; end; @@ -52,14 +56,16 @@ type TDummyServer = class( TInterfacedObject, TSkipTestService.Iface) protected // TSkipTestService.Iface - procedure PingPong(const ping: IPing); + function PingPong(const ping: IPing): IPing; end; -procedure TDummyServer.PingPong(const ping: IPing); +function TDummyServer.PingPong(const ping: IPing): IPing; // TSkipTestService.Iface begin Writeln('- performing request from version '+IntToStr(ping.Version1)+' client'); + Writeln( ping.ToString); + result := CreatePing; end; @@ -69,8 +75,8 @@ var adapt : IThriftStream; begin adapt := TThriftStreamAdapterDelphi.Create( stm, FALSE); if aForInput - then trans := TStreamTransportImpl.Create( adapt, nil) - else trans := TStreamTransportImpl.Create( nil, adapt); + then trans := TStreamTransportImpl.Create( adapt, nil, TThriftConfigurationImpl.Create) + else trans := TStreamTransportImpl.Create( nil, adapt, TThriftConfigurationImpl.Create); result := protfact.GetProtocol( trans); end; @@ -107,6 +113,7 @@ end; procedure ReadResponse( protfact : IProtocolFactory; fname : string); var stm : TFileStream; + ping : IPing; proto : IProtocol; client : TSkipTestService.TClient; // we need access to send/recv_pingpong() cliRef : IUnknown; // holds the refcount @@ -114,11 +121,11 @@ begin Writeln('- reading response'); stm := TFileStream.Create( fname+RESPONSE_EXT, fmOpenRead); try - // save request data + // load request data proto := CreateProtocol( protfact, stm, TRUE); client := TSkipTestService.TClient.Create( proto, nil); cliRef := client as IUnknown; - client.recv_PingPong; + ping := client.recv_PingPong; finally client := nil; // not Free! @@ -162,12 +169,14 @@ end; procedure Test( protfact : IProtocolFactory; fname : string); begin // try to read an existing request + Writeln('Reading data file '+fname); if FileExists( fname + REQUEST_EXT) then begin ProcessFile( protfact, fname); ReadResponse( protfact, fname); end; // create a new request and try to process + Writeln('Writing data file '+fname); CreateRequest( protfact, fname); ProcessFile( protfact, fname); ReadResponse( protfact, fname); @@ -175,8 +184,9 @@ end; const - FILE_BINARY = 'pingpong.bin'; - FILE_JSON = 'pingpong.json'; + FILE_BINARY = 'pingpong.bin'; + FILE_JSON = 'pingpong.json'; + FILE_COMPACT = 'pingpong.compact'; begin try Writeln( 'Delphi SkipTest '+IntToStr(Tskiptest_version_1Constants.SKIPTESTSERVICE_VERSION)+' using '+Thrift.Version); @@ -189,6 +199,10 @@ begin Writeln('JSON protocol'); Test( TJSONProtocolImpl.TFactory.Create, FILE_JSON); + Writeln; + Writeln('Compact protocol'); + Test( TCompactProtocolImpl.TFactory.Create, FILE_COMPACT); + Writeln; Writeln('Test completed without errors.'); Writeln; diff --git a/lib/delphi/test/skip/skiptest_version2.dpr b/lib/delphi/test/skip/skiptest_version2.dpr index 633b247edf2..478ea7c7ad0 100644 --- a/lib/delphi/test/skip/skiptest_version2.dpr +++ b/lib/delphi/test/skip/skiptest_version2.dpr @@ -30,9 +30,12 @@ uses Thrift.Transport in '..\..\src\Thrift.Transport.pas', Thrift.Protocol in '..\..\src\Thrift.Protocol.pas', Thrift.Protocol.JSON in '..\..\src\Thrift.Protocol.JSON.pas', + Thrift.Protocol.Compact in '..\..\src\Thrift.Protocol.Compact.pas', Thrift.Collections in '..\..\src\Thrift.Collections.pas', + Thrift.Configuration in '..\..\src\Thrift.Configuration.pas', Thrift.Server in '..\..\src\Thrift.Server.pas', Thrift.Utils in '..\..\src\Thrift.Utils.pas', + Thrift.WinHTTP in '..\..\src\Thrift.WinHTTP.pas', Thrift.TypeRegistry in '..\..\src\Thrift.TypeRegistry.pas', Thrift.Stream in '..\..\src\Thrift.Stream.pas'; @@ -40,12 +43,15 @@ const REQUEST_EXT = '.request'; RESPONSE_EXT = '.response'; + function CreatePing : IPing; var list : IThriftList; set_ : IHashSet; begin result := TPingImpl.Create; result.Version1 := Tskiptest_version_2Constants.SKIPTESTSERVICE_VERSION; + result.EnumTest := TPingPongEnum.PingTwo; + result.BoolVal := TRUE; result.ByteVal := 2; result.DbVal := 3; @@ -57,6 +63,7 @@ begin result.StructVal := TPongImpl.Create; result.StructVal.Version1 := -1; result.StructVal.Version2 := -2; + result.StructVal.EnumTest := TPingPongEnum.PongTwo; list := TThriftListImpl.Create; list.Add( result.StructVal); @@ -85,6 +92,7 @@ function TDummyServer.PingPong(const ping: IPing; const pong: IPong): IPing; // TSkipTestService.Iface begin Writeln('- performing request from version '+IntToStr(ping.Version1)+' client'); + Writeln( ping.ToString); result := CreatePing; end; @@ -95,8 +103,8 @@ var adapt : IThriftStream; begin adapt := TThriftStreamAdapterDelphi.Create( stm, FALSE); if aForInput - then trans := TStreamTransportImpl.Create( adapt, nil) - else trans := TStreamTransportImpl.Create( nil, adapt); + then trans := TStreamTransportImpl.Create( adapt, nil, TThriftConfigurationImpl.Create) + else trans := TStreamTransportImpl.Create( nil, adapt, TThriftConfigurationImpl.Create); result := protfact.GetProtocol( trans); end; @@ -141,7 +149,7 @@ begin Writeln('- reading response'); stm := TFileStream.Create( fname+RESPONSE_EXT, fmOpenRead); try - // save request data + // load request data proto := CreateProtocol( protfact, stm, TRUE); client := TSkipTestService.TClient.Create( proto, nil); cliRef := client as IUnknown; @@ -189,12 +197,16 @@ end; procedure Test( protfact : IProtocolFactory; fname : string); begin // try to read an existing request + Writeln; + Writeln('Reading data file '+fname); if FileExists( fname + REQUEST_EXT) then begin ProcessFile( protfact, fname); ReadResponse( protfact, fname); end; // create a new request and try to process + Writeln; + Writeln('Writing data file '+fname); CreateRequest( protfact, fname); ProcessFile( protfact, fname); ReadResponse( protfact, fname); @@ -202,8 +214,9 @@ end; const - FILE_BINARY = 'pingpong.bin'; - FILE_JSON = 'pingpong.json'; + FILE_BINARY = 'pingpong.bin'; + FILE_JSON = 'pingpong.json'; + FILE_COMPACT = 'pingpong.compact'; begin try Writeln( 'Delphi SkipTest '+IntToStr(Tskiptest_version_2Constants.SKIPTESTSERVICE_VERSION)+' using '+Thrift.Version); @@ -216,6 +229,10 @@ begin Writeln('JSON protocol'); Test( TJSONProtocolImpl.TFactory.Create, FILE_JSON); + Writeln; + Writeln('Compact protocol'); + Test( TCompactProtocolImpl.TFactory.Create, FILE_COMPACT); + Writeln; Writeln('Test completed without errors.'); Writeln; diff --git a/lib/delphi/test/typeregistry/Test.EnumToString.pas b/lib/delphi/test/typeregistry/Test.EnumToString.pas new file mode 100644 index 00000000000..a3d095d0b67 --- /dev/null +++ b/lib/delphi/test/typeregistry/Test.EnumToString.pas @@ -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. + *) + +unit Test.EnumToString; + +interface + +uses + Classes, SysUtils, + Thrift.Utils, + DebugProtoTest; + + +procedure RunTest; + + +implementation + +{$SCOPEDENUMS ON} + +type + TIrregularEnum = ( // has gaps and/or does not start at zero + FiveHundretOne = 501, + FiveHundretTwo = 502, + FiveHundretFive = 505 + ); + + TRegularEnum = ( // starts at zero, no gaps, no duplicates + One, + Two, + Three + ); + + +procedure IrregularEnumToString; +// TIrregularEnum does not run from 0 to N, so we don't have RTTI for it +// Search for "E2134: Type has no typeinfo" message to get the details +// Unfortunately, this also means that StringUtils.ToString() does not work for enums w/o RTTI +var value : Integer; + sA,sB : string; +begin + for value := Pred(Ord(Low(TIrregularEnum))) to Succ(Ord(High(TIrregularEnum))) do begin + sA := EnumUtils.ToString(Ord(value)); // much more reliable + sB := StringUtils.ToString(TIrregularEnum(value)); // does not really work + WriteLn( '- TIrregularEnum('+IntToStr(value)+'): EnumUtils => ',sA,', StringUtils => ', sB); + end; +end; + + +procedure RegularEnumToString; +// Regular enums have RTTI and work like a charm +var value : Integer; + sA,sB : string; +begin + for value := Pred(Ord(Low(TRegularEnum))) to Succ(Ord(High(TRegularEnum))) do begin + sA := EnumUtils.ToString(Ord(value)); + sB := StringUtils.ToString(TRegularEnum(value)); + if sA = sB // both are expected to work with regular enums + then WriteLn( '- TRegularEnum('+IntToStr(value)+'): ',sA,' = ', sB) + else raise Exception.Create( 'Test failed: '+sA+' <> '+sB); + end; +end; + + +procedure RunTest; +begin + Writeln('Testing enum utils ...'); + + RegularEnumToString; + IrregularEnumToString; + + Writeln; +end; + + +end. + diff --git a/lib/delphi/test/typeregistry/Test.TypeRegistry.pas b/lib/delphi/test/typeregistry/Test.TypeRegistry.pas new file mode 100644 index 00000000000..96e30d81c2c --- /dev/null +++ b/lib/delphi/test/typeregistry/Test.TypeRegistry.pas @@ -0,0 +1,94 @@ +(* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + *) + +unit Test.TypeRegistry; + +interface + +uses + Classes, SysUtils, TypInfo, + Thrift, + Thrift.TypeRegistry, + DebugProtoTest; + + +procedure RunTest; + + +implementation + + +type + Tester = class + public + class procedure Test; + end; + + + +class procedure Tester.Test; +var instance : T; + name : string; +begin + instance := TypeRegistry.Construct; + name := GetTypeName(TypeInfo(T)); + if instance <> nil + then Writeln( name, ' = ok') + else begin + Writeln( name, ' = failed'); + raise Exception.Create( 'Test with '+name+' failed!'); + end; +end; + + +procedure RunTest; +begin + Writeln('Testing type registry ...'); + + Tester.Test; + Tester.Test; + Tester.Test; + Tester.Test; + Tester.Test; + Tester.Test; + Tester.Test; + Tester.Test; + Tester.Test; + Tester.Test; + Tester.Test; + Tester.Test; + Tester.Test; + Tester.Test; + Tester.Test; + Tester.Test; + Tester.Test; + Tester.Test; + Tester.Test; + Tester.Test; + Tester.Test; + Tester.Test; + Tester.Test; + Tester.Test; + + Writeln; +end; + + +end. + diff --git a/lib/delphi/test/typeregistry/TestTypeRegistry.dpr b/lib/delphi/test/typeregistry/TestTypeRegistry.dpr index 18a7c7df3e6..2896bbf9b03 100644 --- a/lib/delphi/test/typeregistry/TestTypeRegistry.dpr +++ b/lib/delphi/test/typeregistry/TestTypeRegistry.dpr @@ -30,61 +30,26 @@ uses Thrift.Protocol in '..\..\src\Thrift.Protocol.pas', Thrift.Protocol.JSON in '..\..\src\Thrift.Protocol.JSON.pas', Thrift.Collections in '..\..\src\Thrift.Collections.pas', + Thrift.Configuration in '..\..\src\Thrift.Configuration.pas', Thrift.Server in '..\..\src\Thrift.Server.pas', Thrift.Utils in '..\..\src\Thrift.Utils.pas', Thrift.Serializer in '..\..\src\Thrift.Serializer.pas', Thrift.Stream in '..\..\src\Thrift.Stream.pas', + Thrift.WinHTTP in '..\..\src\Thrift.WinHTTP.pas', Thrift.TypeRegistry in '..\..\src\Thrift.TypeRegistry.pas', - DebugProtoTest; + Thrift.Test, // in 'gen-delphi\Thrift.Test.pas', + Test.TypeRegistry, + Test.EnumToString; -type - Tester = class - public - class procedure Test; - end; - -class procedure Tester.Test; -var instance : T; - name : string; -begin - instance := TypeRegistry.Construct; - name := GetTypeName(TypeInfo(T)); - if instance <> nil - then Writeln( name, ' = ok') - else begin - Writeln( name, ' = failed'); - raise Exception.Create( 'Test with '+name+' failed!'); - end; -end; begin - Writeln('Testing ...'); - Tester.Test; - Tester.Test; - Tester.Test; - Tester.Test; - Tester.Test; - Tester.Test; - Tester.Test; - Tester.Test; - Tester.Test; - Tester.Test; - Tester.Test; - Tester.Test; - Tester.Test; - Tester.Test; - Tester.Test; - Tester.Test; - Tester.Test; - Tester.Test; - Tester.Test; - Tester.Test; - Tester.Test; - Tester.Test; - Tester.Test; - Tester.Test; - Writeln('Completed.'); - + try + Test.TypeRegistry.RunTest; + Test.EnumToString.RunTest; + Writeln('Completed.'); + except + on e:Exception do Writeln(e.ClassName,': ',e.Message); + end; end. diff --git a/lib/erl/Makefile.am b/lib/erl/Makefile.am index 06323b4a7d2..d4544a3392a 100644 --- a/lib/erl/Makefile.am +++ b/lib/erl/Makefile.am @@ -49,13 +49,10 @@ $(THRIFT_OMIT_FILE): test/Thrift_omit_with.thrift touch .generated all: .generated - $(REBAR) get-deps $(REBAR) compile check: .generated - $(REBAR) -C rebar.test.config get-deps - $(REBAR) -C rebar.test.config compile - $(REBAR) -C rebar.test.config skip_deps=true eunit + $(REBAR) eunit install: all mkdir -p $(DESTDIR)$(ERLANG_INSTALL_LIB_DIR_thrift) ; \ @@ -67,16 +64,26 @@ install: all done uninstall: - rm -rf $(DESTDIR)$(ERLANG_INSTALL_LIB_DIR_thrift) + $(RM) -rf $(DESTDIR)$(ERLANG_INSTALL_LIB_DIR_thrift) -clean: - rm -f .generated - rm -rf test/gen-erl/ - rm -f $(THRIFT_OMIT_FILE) +clean-local: $(REBAR) clean + $(RM) .generated + $(RM) -r .rebar/ + $(RM) -r _build/ + $(RM) -r test/gen-erl/ + $(RM) $(THRIFT_OMIT_FILE) maintainer-clean-local: - rm -rf ebin + $(RM) -r ebin/ + +dist-hook: + $(RM) $(distdir)/.generated + $(RM) -r $(distdir)/.rebar/ + $(RM) -r $(distdir)/_build/ + $(RM) -r $(distdir)/ebin/ + $(RM) -r $(distdir)/test/gen-erl/ + $(RM) $(distdir)/$(THRIFT_OMIT_FILE) EXTRA_DIST = \ include \ @@ -84,7 +91,6 @@ EXTRA_DIST = \ coding_standards.md \ rebar.config \ rebar.config.script \ - rebar.test.config \ test \ README.md diff --git a/lib/erl/rebar.config b/lib/erl/rebar.config index 1ea18a4312f..ab2c255c6b4 100644 --- a/lib/erl/rebar.config +++ b/lib/erl/rebar.config @@ -1 +1,11 @@ {erl_opts, [{platform_define, "^R.*", otp16_or_less}, debug_info]}. + +{profiles, [ + {test, [ + {deps, [meck]}, + {eunit_tests, [ + {dir, "test"}, + {dir, "test/gen-erl"} + ]} + ]} +]}. diff --git a/lib/erl/rebar.test.config b/lib/erl/rebar.test.config deleted file mode 100644 index 2ff2afbb119..00000000000 --- a/lib/erl/rebar.test.config +++ /dev/null @@ -1,5 +0,0 @@ -{erl_opts, [{platform_define, "^R.*", otp16_or_less}, debug_info]}. - -{deps, [ - {meck, "", {git, "https://github.com/eproxus/meck.git", {tag, "0.8.9"}}} -]}. diff --git a/lib/erl/src/thrift.app.src b/lib/erl/src/thrift.app.src index b02ab210fa6..5e8d670f2b6 100644 --- a/lib/erl/src/thrift.app.src +++ b/lib/erl/src/thrift.app.src @@ -22,7 +22,7 @@ {description, "Thrift bindings"}, % The version of the applicaton - {vsn, "0.12.0"}, + {vsn, "0.14.0"}, % All modules used by the application. {modules, [ diff --git a/lib/erl/src/thrift_socket_server.erl b/lib/erl/src/thrift_socket_server.erl index 4e3c052bffb..432e65b563e 100644 --- a/lib/erl/src/thrift_socket_server.erl +++ b/lib/erl/src/thrift_socket_server.erl @@ -278,9 +278,13 @@ handle_cast(stop, State) -> terminate(Reason, #thrift_socket_server{listen=Listen, port=Port}) -> gen_tcp:close(Listen), - {backtrace, Bt} = erlang:process_info(self(), backtrace), - error_logger:error_report({?MODULE, ?LINE, - {child_error, Reason, Bt}}), + case Reason of + normal -> ok; + shutdown -> ok; + _ -> {backtrace, Bt} = erlang:process_info(self(), backtrace), + error_logger:error_report({?MODULE, ?LINE, + {child_error, Reason, Bt}}) + end, case Port < 1024 of true -> catch fdsrv:stop(), diff --git a/lib/go/README.md b/lib/go/README.md index ce6d5edc130..74e142b0f04 100644 --- a/lib/go/README.md +++ b/lib/go/README.md @@ -81,3 +81,35 @@ which will generate: type Foo struct { Bar string `thrift:"bar,1,required" some_tag:"some_tag_value"` } + +A note about server handler implementations +=========================================== + +The context object passed into the server handler function will be canceled when +the client closes the connection (this is a best effort check, not a guarantee +-- there's no guarantee that the context object is always canceled when client +closes the connection, but when it's canceled you can always assume the client +closed the connection). When implementing Go Thrift server, you can take +advantage of that to abandon requests that's no longer needed: + + func MyEndpoint(ctx context.Context, req *thriftRequestType) (*thriftResponseType, error) { + ... + if ctx.Err() == context.Canceled { + return nil, thrift.ErrAbandonRequest + } + ... + } + +This feature would add roughly 1 millisecond of latency overhead to the server +handlers (along with roughly 2 goroutines per request). +If that is unacceptable, it can be disabled by having this line early in your +main function: + + thrift.ServerConnectivityCheckInterval = 0 + +Please be advised that due to a +[Go runtime bug](https://github.com/golang/go/issues/27707), currently +if this interval is set to a value too low (for example, 1ms), it might cause +excessive cpu overhead. + +This feature is also only enabled on non-oneway endpoints. diff --git a/lib/cpp/thrift-qt.pc.in b/lib/go/test/ConflictNamespaceServiceTest.thrift old mode 100755 new mode 100644 similarity index 77% rename from lib/cpp/thrift-qt.pc.in rename to lib/go/test/ConflictNamespaceServiceTest.thrift index 5e60d840d39..aade3d7d6ed --- a/lib/cpp/thrift-qt.pc.in +++ b/lib/go/test/ConflictNamespaceServiceTest.thrift @@ -16,15 +16,10 @@ # specific language governing permissions and limitations # under the License. # +namespace go conflict.context -prefix=@prefix@ -exec_prefix=@exec_prefix@ -libdir=@libdir@ -includedir=@includedir@ +include "ConflictNamespaceTestD.thrift" -Name: Thrift -Description: Thrift Qt API -Version: @VERSION@ -Requires: thrift = @VERSION@ -Libs: -L${libdir} -lthriftqt -Cflags: -I${includedir} +service ConflictService { + ConflictNamespaceTestD.ThingD thingFunc() +} diff --git a/compiler/cpp/test/cpp_plugin_test.sh b/lib/go/test/ConflictNamespaceTestA.thrift old mode 100755 new mode 100644 similarity index 73% rename from compiler/cpp/test/cpp_plugin_test.sh rename to lib/go/test/ConflictNamespaceTestA.thrift index ddb2e0a0923..2749da90b7e --- a/compiler/cpp/test/cpp_plugin_test.sh +++ b/lib/go/test/ConflictNamespaceTestA.thrift @@ -1,5 +1,3 @@ -#!/bin/sh - # # Licensed to the Apache Software Foundation (ASF) under one # or more contributor license agreements. See the NOTICE file @@ -18,10 +16,8 @@ # specific language governing permissions and limitations # under the License. # +namespace go conflicta.common -# this file is intended to be invoked by make. -set -e -mkdir -p gen-cpp gen-mycpp -PATH=.:"$PATH" ../thrift -r -out gen-cpp -gen cpp ../../../test/Include.thrift -PATH=.:"$PATH" ../thrift -r -out gen-mycpp -gen mycpp ../../../test/Include.thrift -diff -urN gen-cpp gen-mycpp +struct ThingA { + 1: bool value +} diff --git a/lib/go/test/ConflictNamespaceTestB.thrift b/lib/go/test/ConflictNamespaceTestB.thrift new file mode 100644 index 00000000000..b1940ff9f60 --- /dev/null +++ b/lib/go/test/ConflictNamespaceTestB.thrift @@ -0,0 +1,23 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# +namespace go conflictb.common + +struct ThingB { + 1: bool value +} diff --git a/lib/go/test/ConflictNamespaceTestC.thrift b/lib/go/test/ConflictNamespaceTestC.thrift new file mode 100644 index 00000000000..7d5ee258260 --- /dev/null +++ b/lib/go/test/ConflictNamespaceTestC.thrift @@ -0,0 +1,23 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# +namespace go common + +struct ThingC { + 1: bool value +} diff --git a/lib/go/test/ConflictNamespaceTestD.thrift b/lib/go/test/ConflictNamespaceTestD.thrift new file mode 100644 index 00000000000..8fe7f5e013d --- /dev/null +++ b/lib/go/test/ConflictNamespaceTestD.thrift @@ -0,0 +1,23 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# +namespace go conflictd.context + +struct ThingD { + 1: bool value +} diff --git a/lib/go/test/ConflictNamespaceTestSuperThing.thrift b/lib/go/test/ConflictNamespaceTestSuperThing.thrift new file mode 100644 index 00000000000..2348a621f80 --- /dev/null +++ b/lib/go/test/ConflictNamespaceTestSuperThing.thrift @@ -0,0 +1,29 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# +include "ConflictNamespaceTestA.thrift" +include "ConflictNamespaceTestB.thrift" +include "ConflictNamespaceTestC.thrift" +include "ConflictNamespaceTestD.thrift" + +struct SuperThing { + 1: ConflictNamespaceTestA.ThingA thing_a + 2: ConflictNamespaceTestB.ThingB thing_b + 3: ConflictNamespaceTestC.ThingC thing_c + 4: ConflictNamespaceTestD.ThingD thing_d +} diff --git a/lib/go/test/DuplicateImportsTest.thrift b/lib/go/test/DuplicateImportsTest.thrift new file mode 100644 index 00000000000..ffe1caff2c3 --- /dev/null +++ b/lib/go/test/DuplicateImportsTest.thrift @@ -0,0 +1,5 @@ +include "common/a.thrift" +include "common/b.thrift" + +typedef a.A A +typedef b.B B diff --git a/lib/go/test/EqualsTest.thrift b/lib/go/test/EqualsTest.thrift new file mode 100644 index 00000000000..c699232b4d2 --- /dev/null +++ b/lib/go/test/EqualsTest.thrift @@ -0,0 +1,109 @@ +typedef i8 mybyte +typedef string mystr +typedef binary mybin + +enum EnumFoo { + e1 + e2 +} + +struct BasicEqualsFoo { + 1: bool BoolFoo, + 2: optional bool OptBoolFoo, + 3: i8 I8Foo, + 4: optional i8 OptI8Foo, + 5: i16 I16Foo, + 6: optional i16 OptI16Foo, + 7: i32 I32Foo, + 8: optional i32 OptI32Foo, + 9: i64 I64Foo, + 10: optional i64 OptI64Foo, + 11: double DoubleFoo, + 12: optional double OptDoubleFoo, + 13: string StrFoo, + 14: optional string OptStrFoo, + 15: binary BinFoo, + 16: optional binary OptBinFoo, + 17: EnumFoo EnumFoo, + 18: optional EnumFoo OptEnumFoo, + 19: mybyte MyByteFoo, + 20: optional mybyte OptMyByteFoo, + 21: mystr MyStrFoo, + 22: optional mystr OptMyStrFoo, + 23: mybin MyBinFoo, + 24: optional mybin OptMyBinFoo, +} + +struct StructEqualsFoo { + 1: BasicEqualsFoo StructFoo, + 2: optional BasicEqualsFoo OptStructFoo, +} + +struct ListEqualsFoo { + 1: list I64ListFoo, + 2: optional list OptI64ListFoo, + 3: list StrListFoo, + 4: optional list OptStrListFoo, + 5: list BinListFoo, + 6: optional list OptBinListFoo, + 7: list StructListFoo, + 8: optional list OptStructListFoo, + 9: list> I64ListListFoo, + 10: optional list> OptI64ListListFoo, + 11: list> I64SetListFoo, + 12: optional list> OptI64SetListFoo, + 13: list> I64StringMapListFoo, + 14: optional list> OptI64StringMapListFoo, + 15: list MyByteListFoo, + 16: optional list OptMyByteListFoo, + 17: list MyStrListFoo, + 18: optional list OptMyStrListFoo, + 19: list MyBinListFoo, + 20: optional list OptMyBinListFoo, +} + +struct SetEqualsFoo { + 1: set I64SetFoo, + 2: optional set OptI64SetFoo, + 3: set StrSetFoo, + 4: optional set OptStrSetFoo, + 5: set BinSetFoo, + 6: optional set OptBinSetFoo, + 7: set StructSetFoo, + 8: optional set OptStructSetFoo, + 9: set> I64ListSetFoo, + 10: optional set> OptI64ListSetFoo, + 11: set> I64SetSetFoo, + 12: optional set> OptI64SetSetFoo, + 13: set> I64StringMapSetFoo, + 14: optional set> OptI64StringMapSetFoo, + 15: set MyByteSetFoo, + 16: optional set OptMyByteSetFoo, + 17: set MyStrSetFoo, + 18: optional set OptMyStrSetFoo, + 19: set MyBinSetFoo, + 20: optional set OptMyBinSetFoo, +} + +struct MapEqualsFoo { + 1: map I64StrMapFoo, + 2: optional map OptI64StrMapFoo, + 3: map StrI64MapFoo, + 4: optional map OptStrI64MapFoo, + 5: map StructBinMapFoo, + 6: optional map OptStructBinMapFoo, + 7: map BinStructMapFoo, + 8: optional map OptBinStructMapFoo, + 9: map> I64I64ListMapFoo, + 10: optional map> OptI64I64ListMapFoo, + 11: map> I64I64SetMapFoo, + 12: optional map> OptI64I64SetMapFoo, + 13: map> I64I64StringMapMapFoo, + 14: optional map> OptI64I64StringMapMapFoo, + 15: map MyStrMyBinMapFoo, + 16: optional map OptMyStrMyBinMapFoo, + 17: map Int64MyByteMapFoo, + 18: optional map OptInt64MyByteMapFoo, + 19: map MyByteInt64MapFoo, + 20: optional map OptMyByteInt64MapFoo, +} \ No newline at end of file diff --git a/lib/go/test/GoTagTest.thrift b/lib/go/test/GoTagTest.thrift index 508b3b6c3ec..4b6ac3113fa 100644 --- a/lib/go/test/GoTagTest.thrift +++ b/lib/go/test/GoTagTest.thrift @@ -19,6 +19,7 @@ struct tagged { 1: string string_thing, - 2: i64 int_thing (go.tag = "json:\"int_thing,string\""), + 2: i64 int_thing (go.tag = "json:\"custom_thing\" mykey:\"myvalue\""), 3: optional i64 optional_int_thing + 4: optional bool optional_bool_thing = false } diff --git a/lib/go/test/Makefile.am b/lib/go/test/Makefile.am index 78d468129c2..2f748c6fa59 100644 --- a/lib/go/test/Makefile.am +++ b/lib/go/test/Makefile.am @@ -39,7 +39,14 @@ gopath: $(THRIFT) $(THRIFTTEST) \ InitialismsTest.thrift \ DontExportRWTest.thrift \ dontexportrwtest/compile_test.go \ - IgnoreInitialismsTest.thrift + IgnoreInitialismsTest.thrift \ + ConflictNamespaceTestA.thrift \ + ConflictNamespaceTestB.thrift \ + ConflictNamespaceTestC.thrift \ + ConflictNamespaceTestD.thrift \ + ConflictNamespaceTestSuperThing.thrift \ + ConflictNamespaceServiceTest.thrift \ + DuplicateImportsTest.thrift mkdir -p gopath/src grep -v list.*map.*list.*map $(THRIFTTEST) | grep -v 'set' > ThriftTest.thrift $(THRIFT) $(THRIFTARGS) -r IncludesTest.thrift @@ -59,6 +66,14 @@ gopath: $(THRIFT) $(THRIFTTEST) \ $(THRIFT) $(THRIFTARGS) InitialismsTest.thrift $(THRIFT) $(THRIFTARGS),read_write_private DontExportRWTest.thrift $(THRIFT) $(THRIFTARGS),ignore_initialisms IgnoreInitialismsTest.thrift + $(THRIFT) $(THRIFTARGS) ConflictNamespaceTestA.thrift + $(THRIFT) $(THRIFTARGS) ConflictNamespaceTestB.thrift + $(THRIFT) $(THRIFTARGS) ConflictNamespaceTestC.thrift + $(THRIFT) $(THRIFTARGS) ConflictNamespaceTestD.thrift + $(THRIFT) $(THRIFTARGS) ConflictNamespaceTestSuperThing.thrift + $(THRIFT) $(THRIFTARGS) ConflictNamespaceServiceTest.thrift + $(THRIFT) $(THRIFTARGS) -r DuplicateImportsTest.thrift + $(THRIFT) $(THRIFTARGS) EqualsTest.thrift GOPATH=`pwd`/gopath $(GO) get github.com/golang/mock/gomock || true sed -i 's/\"context\"/\"golang.org\/x\/net\/context\"/g' gopath/src/github.com/golang/mock/gomock/controller.go || true GOPATH=`pwd`/gopath $(GO) get github.com/golang/mock/gomock @@ -79,7 +94,12 @@ check: gopath initialismstest \ dontexportrwtest \ ignoreinitialismstest \ - unionbinarytest + unionbinarytest \ + conflictnamespacetestsuperthing \ + conflict/context/conflict_service-remote \ + servicestest/container_test-remote \ + duplicateimportstest \ + equalstest GOPATH=`pwd`/gopath $(GO) test thrift tests dontexportrwtest clean-local: @@ -91,21 +111,30 @@ client: stubs EXTRA_DIST = \ dontexportrwtest \ tests \ + common \ BinaryKeyTest.thrift \ + ConflictNamespaceServiceTest.thrift \ + ConflictNamespaceTestA.thrift \ + ConflictNamespaceTestB.thrift \ + ConflictNamespaceTestC.thrift \ + ConflictNamespaceTestD.thrift \ + ConflictNamespaceTestSuperThing.thrift \ + DontExportRWTest.thrift \ + DuplicateImportsTest.thrift \ + ErrorTest.thrift \ + EqualsTest.thrift \ GoTagTest.thrift \ + IgnoreInitialismsTest.thrift \ IncludesTest.thrift \ + InitialismsTest.thrift \ MultiplexedProtocolTest.thrift \ NamespacedTest.thrift \ + NamesTest.thrift \ OnewayTest.thrift \ OptionalFieldsTest.thrift \ - RequiredFieldTest.thrift \ RefAnnotationFieldsTest.thrift \ - UnionDefaultValueTest.thrift \ - UnionBinaryTest.thrift \ + RequiredFieldTest.thrift \ ServicesTest.thrift \ TypedefFieldTest.thrift \ - ErrorTest.thrift \ - NamesTest.thrift \ - InitialismsTest.thrift \ - DontExportRWTest.thrift \ - IgnoreInitialismsTest.thrift + UnionBinaryTest.thrift \ + UnionDefaultValueTest.thrift diff --git a/lib/go/test/ServicesTest.thrift b/lib/go/test/ServicesTest.thrift index 882b03acbcb..666197f15b7 100644 --- a/lib/go/test/ServicesTest.thrift +++ b/lib/go/test/ServicesTest.thrift @@ -107,5 +107,12 @@ service a_serv { struct_a struct_a_func_2ex_1int_1s(1: i64 i, 2: string s) throws(1: moderate_disaster err1, 2:total_disaster err2) struct_a struct_a_func_1struct_a(1: struct_a st) +} + +service container_test_parent { + void parent_only_func(1: set s) +} +service container_test extends container_test_parent { + void child_only_func(1: set s) } diff --git a/lib/go/test/common/a.thrift b/lib/go/test/common/a.thrift new file mode 100644 index 00000000000..37e0e1cf421 --- /dev/null +++ b/lib/go/test/common/a.thrift @@ -0,0 +1,5 @@ +namespace go common + +struct A { + 1: optional string a +} diff --git a/lib/go/test/common/b.thrift b/lib/go/test/common/b.thrift new file mode 100644 index 00000000000..19930e7d325 --- /dev/null +++ b/lib/go/test/common/b.thrift @@ -0,0 +1,5 @@ +namespace go common + +struct B { + 1: optional string b +} diff --git a/lib/go/test/tests/client_error_test.go b/lib/go/test/tests/client_error_test.go index fdec4ea57f2..8d720fff272 100644 --- a/lib/go/test/tests/client_error_test.go +++ b/lib/go/test/tests/client_error_test.go @@ -32,89 +32,106 @@ import ( // TestCase: Comprehensive call and reply workflow in the client. // Setup mock to fail at a certain position. Return true if position exists otherwise false. func prepareClientCallReply(protocol *MockTProtocol, failAt int, failWith error) bool { + // NOTE: here the number 50 is the same as the last number at the end of + // this function. If more function calls are added in the future, this + // number needs to be increased accordingly. + // + // It's needed for go 1.14+. Before go 1.14 we only call gomock + // controller's Finish function when this function returns true. + // Starting from go 1.14 the gomock will take advantage of + // testing.T.Cleanup interface, which means the Finish function will + // always be called by t.Cleanup, even if we return false here. + // As a result, in the case we need to return false by this function, + // we must return before calling any of the + // protocol.EXPECT().* functions. + const lastFailAt = 50 + if failAt > lastFailAt { + return false + } + var err error = nil if failAt == 0 { err = failWith } - last := protocol.EXPECT().WriteMessageBegin("testStruct", thrift.CALL, int32(1)).Return(err) + last := protocol.EXPECT().WriteMessageBegin(context.Background(), "testStruct", thrift.CALL, int32(1)).Return(err) if failAt == 0 { return true } if failAt == 1 { err = failWith } - last = protocol.EXPECT().WriteStructBegin("testStruct_args").Return(err).After(last) + last = protocol.EXPECT().WriteStructBegin(context.Background(), "testStruct_args").Return(err).After(last) if failAt == 1 { return true } if failAt == 2 { err = failWith } - last = protocol.EXPECT().WriteFieldBegin("thing", thrift.TType(thrift.STRUCT), int16(1)).Return(err).After(last) + last = protocol.EXPECT().WriteFieldBegin(context.Background(), "thing", thrift.TType(thrift.STRUCT), int16(1)).Return(err).After(last) if failAt == 2 { return true } if failAt == 3 { err = failWith } - last = protocol.EXPECT().WriteStructBegin("TestStruct").Return(err).After(last) + last = protocol.EXPECT().WriteStructBegin(context.Background(), "TestStruct").Return(err).After(last) if failAt == 3 { return true } if failAt == 4 { err = failWith } - last = protocol.EXPECT().WriteFieldBegin("m", thrift.TType(thrift.MAP), int16(1)).Return(err).After(last) + last = protocol.EXPECT().WriteFieldBegin(context.Background(), "m", thrift.TType(thrift.MAP), int16(1)).Return(err).After(last) if failAt == 4 { return true } if failAt == 5 { err = failWith } - last = protocol.EXPECT().WriteMapBegin(thrift.TType(thrift.STRING), thrift.TType(thrift.STRING), 0).Return(err).After(last) + last = protocol.EXPECT().WriteMapBegin(context.Background(), thrift.TType(thrift.STRING), thrift.TType(thrift.STRING), 0).Return(err).After(last) if failAt == 5 { return true } if failAt == 6 { err = failWith } - last = protocol.EXPECT().WriteMapEnd().Return(err).After(last) + last = protocol.EXPECT().WriteMapEnd(context.Background()).Return(err).After(last) if failAt == 6 { return true } if failAt == 7 { err = failWith } - last = protocol.EXPECT().WriteFieldEnd().Return(err).After(last) + last = protocol.EXPECT().WriteFieldEnd(context.Background()).Return(err).After(last) if failAt == 7 { return true } if failAt == 8 { err = failWith } - last = protocol.EXPECT().WriteFieldBegin("l", thrift.TType(thrift.LIST), int16(2)).Return(err).After(last) + last = protocol.EXPECT().WriteFieldBegin(context.Background(), "l", thrift.TType(thrift.LIST), int16(2)).Return(err).After(last) if failAt == 8 { return true } if failAt == 9 { err = failWith } - last = protocol.EXPECT().WriteListBegin(thrift.TType(thrift.STRING), 0).Return(err).After(last) + last = protocol.EXPECT().WriteListBegin(context.Background(), thrift.TType(thrift.STRING), 0).Return(err).After(last) if failAt == 9 { return true } if failAt == 10 { err = failWith } - last = protocol.EXPECT().WriteListEnd().Return(err).After(last) + last = protocol.EXPECT().WriteListEnd(context.Background()).Return(err).After(last) if failAt == 10 { return true } if failAt == 11 { err = failWith } - last = protocol.EXPECT().WriteFieldEnd().Return(err).After(last) + last = protocol.EXPECT().WriteFieldEnd(context.Background()).Return(err).After(last) if failAt == 11 { return true } @@ -122,91 +139,91 @@ func prepareClientCallReply(protocol *MockTProtocol, failAt int, failWith error) err = failWith } - last = protocol.EXPECT().WriteFieldBegin("s", thrift.TType(thrift.SET), int16(3)).Return(err).After(last) + last = protocol.EXPECT().WriteFieldBegin(context.Background(), "s", thrift.TType(thrift.SET), int16(3)).Return(err).After(last) if failAt == 12 { return true } if failAt == 13 { err = failWith } - last = protocol.EXPECT().WriteSetBegin(thrift.TType(thrift.STRING), 0).Return(err).After(last) + last = protocol.EXPECT().WriteSetBegin(context.Background(), thrift.TType(thrift.STRING), 0).Return(err).After(last) if failAt == 13 { return true } if failAt == 14 { err = failWith } - last = protocol.EXPECT().WriteSetEnd().Return(err).After(last) + last = protocol.EXPECT().WriteSetEnd(context.Background()).Return(err).After(last) if failAt == 14 { return true } if failAt == 15 { err = failWith } - last = protocol.EXPECT().WriteFieldEnd().Return(err).After(last) + last = protocol.EXPECT().WriteFieldEnd(context.Background()).Return(err).After(last) if failAt == 15 { return true } if failAt == 16 { err = failWith } - last = protocol.EXPECT().WriteFieldBegin("i", thrift.TType(thrift.I32), int16(4)).Return(err).After(last) + last = protocol.EXPECT().WriteFieldBegin(context.Background(), "i", thrift.TType(thrift.I32), int16(4)).Return(err).After(last) if failAt == 16 { return true } if failAt == 17 { err = failWith } - last = protocol.EXPECT().WriteI32(int32(3)).Return(err).After(last) + last = protocol.EXPECT().WriteI32(context.Background(), int32(3)).Return(err).After(last) if failAt == 17 { return true } if failAt == 18 { err = failWith } - last = protocol.EXPECT().WriteFieldEnd().Return(err).After(last) + last = protocol.EXPECT().WriteFieldEnd(context.Background()).Return(err).After(last) if failAt == 18 { return true } if failAt == 19 { err = failWith } - last = protocol.EXPECT().WriteFieldStop().Return(err).After(last) + last = protocol.EXPECT().WriteFieldStop(context.Background()).Return(err).After(last) if failAt == 19 { return true } if failAt == 20 { err = failWith } - last = protocol.EXPECT().WriteStructEnd().Return(err).After(last) + last = protocol.EXPECT().WriteStructEnd(context.Background()).Return(err).After(last) if failAt == 20 { return true } if failAt == 21 { err = failWith } - last = protocol.EXPECT().WriteFieldEnd().Return(err).After(last) + last = protocol.EXPECT().WriteFieldEnd(context.Background()).Return(err).After(last) if failAt == 21 { return true } if failAt == 22 { err = failWith } - last = protocol.EXPECT().WriteFieldStop().Return(err).After(last) + last = protocol.EXPECT().WriteFieldStop(context.Background()).Return(err).After(last) if failAt == 22 { return true } if failAt == 23 { err = failWith } - last = protocol.EXPECT().WriteStructEnd().Return(err).After(last) + last = protocol.EXPECT().WriteStructEnd(context.Background()).Return(err).After(last) if failAt == 23 { return true } if failAt == 24 { err = failWith } - last = protocol.EXPECT().WriteMessageEnd().Return(err).After(last) + last = protocol.EXPECT().WriteMessageEnd(context.Background()).Return(err).After(last) if failAt == 24 { return true } @@ -220,175 +237,175 @@ func prepareClientCallReply(protocol *MockTProtocol, failAt int, failWith error) if failAt == 26 { err = failWith } - last = protocol.EXPECT().ReadMessageBegin().Return("testStruct", thrift.REPLY, int32(1), err).After(last) + last = protocol.EXPECT().ReadMessageBegin(context.Background()).Return("testStruct", thrift.REPLY, int32(1), err).After(last) if failAt == 26 { return true } if failAt == 27 { err = failWith } - last = protocol.EXPECT().ReadStructBegin().Return("testStruct_args", err).After(last) + last = protocol.EXPECT().ReadStructBegin(context.Background()).Return("testStruct_args", err).After(last) if failAt == 27 { return true } if failAt == 28 { err = failWith } - last = protocol.EXPECT().ReadFieldBegin().Return("_", thrift.TType(thrift.STRUCT), int16(0), err).After(last) + last = protocol.EXPECT().ReadFieldBegin(context.Background()).Return("_", thrift.TType(thrift.STRUCT), int16(0), err).After(last) if failAt == 28 { return true } if failAt == 29 { err = failWith } - last = protocol.EXPECT().ReadStructBegin().Return("TestStruct", err).After(last) + last = protocol.EXPECT().ReadStructBegin(context.Background()).Return("TestStruct", err).After(last) if failAt == 29 { return true } if failAt == 30 { err = failWith } - last = protocol.EXPECT().ReadFieldBegin().Return("m", thrift.TType(thrift.MAP), int16(1), err).After(last) + last = protocol.EXPECT().ReadFieldBegin(context.Background()).Return("m", thrift.TType(thrift.MAP), int16(1), err).After(last) if failAt == 30 { return true } if failAt == 31 { err = failWith } - last = protocol.EXPECT().ReadMapBegin().Return(thrift.TType(thrift.STRING), thrift.TType(thrift.STRING), 0, err).After(last) + last = protocol.EXPECT().ReadMapBegin(context.Background()).Return(thrift.TType(thrift.STRING), thrift.TType(thrift.STRING), 0, err).After(last) if failAt == 31 { return true } if failAt == 32 { err = failWith } - last = protocol.EXPECT().ReadMapEnd().Return(err).After(last) + last = protocol.EXPECT().ReadMapEnd(context.Background()).Return(err).After(last) if failAt == 32 { return true } if failAt == 33 { err = failWith } - last = protocol.EXPECT().ReadFieldEnd().Return(err).After(last) + last = protocol.EXPECT().ReadFieldEnd(context.Background()).Return(err).After(last) if failAt == 33 { return true } if failAt == 34 { err = failWith } - last = protocol.EXPECT().ReadFieldBegin().Return("l", thrift.TType(thrift.LIST), int16(2), err).After(last) + last = protocol.EXPECT().ReadFieldBegin(context.Background()).Return("l", thrift.TType(thrift.LIST), int16(2), err).After(last) if failAt == 34 { return true } if failAt == 35 { err = failWith } - last = protocol.EXPECT().ReadListBegin().Return(thrift.TType(thrift.STRING), 0, err).After(last) + last = protocol.EXPECT().ReadListBegin(context.Background()).Return(thrift.TType(thrift.STRING), 0, err).After(last) if failAt == 35 { return true } if failAt == 36 { err = failWith } - last = protocol.EXPECT().ReadListEnd().Return(err).After(last) + last = protocol.EXPECT().ReadListEnd(context.Background()).Return(err).After(last) if failAt == 36 { return true } if failAt == 37 { err = failWith } - last = protocol.EXPECT().ReadFieldEnd().Return(err).After(last) + last = protocol.EXPECT().ReadFieldEnd(context.Background()).Return(err).After(last) if failAt == 37 { return true } if failAt == 38 { err = failWith } - last = protocol.EXPECT().ReadFieldBegin().Return("s", thrift.TType(thrift.SET), int16(3), err).After(last) + last = protocol.EXPECT().ReadFieldBegin(context.Background()).Return("s", thrift.TType(thrift.SET), int16(3), err).After(last) if failAt == 38 { return true } if failAt == 39 { err = failWith } - last = protocol.EXPECT().ReadSetBegin().Return(thrift.TType(thrift.STRING), 0, err).After(last) + last = protocol.EXPECT().ReadSetBegin(context.Background()).Return(thrift.TType(thrift.STRING), 0, err).After(last) if failAt == 39 { return true } if failAt == 40 { err = failWith } - last = protocol.EXPECT().ReadSetEnd().Return(err).After(last) + last = protocol.EXPECT().ReadSetEnd(context.Background()).Return(err).After(last) if failAt == 40 { return true } if failAt == 41 { err = failWith } - last = protocol.EXPECT().ReadFieldEnd().Return(err).After(last) + last = protocol.EXPECT().ReadFieldEnd(context.Background()).Return(err).After(last) if failAt == 41 { return true } if failAt == 42 { err = failWith } - last = protocol.EXPECT().ReadFieldBegin().Return("i", thrift.TType(thrift.I32), int16(4), err).After(last) + last = protocol.EXPECT().ReadFieldBegin(context.Background()).Return("i", thrift.TType(thrift.I32), int16(4), err).After(last) if failAt == 42 { return true } if failAt == 43 { err = failWith } - last = protocol.EXPECT().ReadI32().Return(int32(3), err).After(last) + last = protocol.EXPECT().ReadI32(context.Background()).Return(int32(3), err).After(last) if failAt == 43 { return true } if failAt == 44 { err = failWith } - last = protocol.EXPECT().ReadFieldEnd().Return(err).After(last) + last = protocol.EXPECT().ReadFieldEnd(context.Background()).Return(err).After(last) if failAt == 44 { return true } if failAt == 45 { err = failWith } - last = protocol.EXPECT().ReadFieldBegin().Return("_", thrift.TType(thrift.STOP), int16(5), err).After(last) + last = protocol.EXPECT().ReadFieldBegin(context.Background()).Return("_", thrift.TType(thrift.STOP), int16(5), err).After(last) if failAt == 45 { return true } if failAt == 46 { err = failWith } - last = protocol.EXPECT().ReadStructEnd().Return(err).After(last) + last = protocol.EXPECT().ReadStructEnd(context.Background()).Return(err).After(last) if failAt == 46 { return true } if failAt == 47 { err = failWith } - last = protocol.EXPECT().ReadFieldEnd().Return(err).After(last) + last = protocol.EXPECT().ReadFieldEnd(context.Background()).Return(err).After(last) if failAt == 47 { return true } if failAt == 48 { err = failWith } - last = protocol.EXPECT().ReadFieldBegin().Return("_", thrift.TType(thrift.STOP), int16(1), err).After(last) + last = protocol.EXPECT().ReadFieldBegin(context.Background()).Return("_", thrift.TType(thrift.STOP), int16(1), err).After(last) if failAt == 48 { return true } if failAt == 49 { err = failWith } - last = protocol.EXPECT().ReadStructEnd().Return(err).After(last) + last = protocol.EXPECT().ReadStructEnd(context.Background()).Return(err).After(last) if failAt == 49 { return true } if failAt == 50 { err = failWith } - last = protocol.EXPECT().ReadMessageEnd().Return(err).After(last) + last = protocol.EXPECT().ReadMessageEnd(context.Background()).Return(err).After(last) if failAt == 50 { return true } @@ -529,91 +546,91 @@ func prepareClientCallException(protocol *MockTProtocol, failAt int, failWith er var err error = nil // No need to test failure in this block, because it is covered in other test cases - last := protocol.EXPECT().WriteMessageBegin("testString", thrift.CALL, int32(1)) - last = protocol.EXPECT().WriteStructBegin("testString_args").After(last) - last = protocol.EXPECT().WriteFieldBegin("s", thrift.TType(thrift.STRING), int16(1)).After(last) - last = protocol.EXPECT().WriteString("test").After(last) - last = protocol.EXPECT().WriteFieldEnd().After(last) - last = protocol.EXPECT().WriteFieldStop().After(last) - last = protocol.EXPECT().WriteStructEnd().After(last) - last = protocol.EXPECT().WriteMessageEnd().After(last) + last := protocol.EXPECT().WriteMessageBegin(context.Background(), "testString", thrift.CALL, int32(1)) + last = protocol.EXPECT().WriteStructBegin(context.Background(), "testString_args").After(last) + last = protocol.EXPECT().WriteFieldBegin(context.Background(), "s", thrift.TType(thrift.STRING), int16(1)).After(last) + last = protocol.EXPECT().WriteString(context.Background(), "test").After(last) + last = protocol.EXPECT().WriteFieldEnd(context.Background()).After(last) + last = protocol.EXPECT().WriteFieldStop(context.Background()).After(last) + last = protocol.EXPECT().WriteStructEnd(context.Background()).After(last) + last = protocol.EXPECT().WriteMessageEnd(context.Background()).After(last) last = protocol.EXPECT().Flush(context.Background()).After(last) // Reading the exception, might fail. if failAt == 0 { err = failWith } - last = protocol.EXPECT().ReadMessageBegin().Return("testString", thrift.EXCEPTION, int32(1), err).After(last) + last = protocol.EXPECT().ReadMessageBegin(context.Background()).Return("testString", thrift.EXCEPTION, int32(1), err).After(last) if failAt == 0 { return true } if failAt == 1 { err = failWith } - last = protocol.EXPECT().ReadStructBegin().Return("TApplicationException", err).After(last) + last = protocol.EXPECT().ReadStructBegin(context.Background()).Return("TApplicationException", err).After(last) if failAt == 1 { return true } if failAt == 2 { err = failWith } - last = protocol.EXPECT().ReadFieldBegin().Return("message", thrift.TType(thrift.STRING), int16(1), err).After(last) + last = protocol.EXPECT().ReadFieldBegin(context.Background()).Return("message", thrift.TType(thrift.STRING), int16(1), err).After(last) if failAt == 2 { return true } if failAt == 3 { err = failWith } - last = protocol.EXPECT().ReadString().Return("test", err).After(last) + last = protocol.EXPECT().ReadString(context.Background()).Return("test", err).After(last) if failAt == 3 { return true } if failAt == 4 { err = failWith } - last = protocol.EXPECT().ReadFieldEnd().Return(err).After(last) + last = protocol.EXPECT().ReadFieldEnd(context.Background()).Return(err).After(last) if failAt == 4 { return true } if failAt == 5 { err = failWith } - last = protocol.EXPECT().ReadFieldBegin().Return("type", thrift.TType(thrift.I32), int16(2), err).After(last) + last = protocol.EXPECT().ReadFieldBegin(context.Background()).Return("type", thrift.TType(thrift.I32), int16(2), err).After(last) if failAt == 5 { return true } if failAt == 6 { err = failWith } - last = protocol.EXPECT().ReadI32().Return(int32(thrift.PROTOCOL_ERROR), err).After(last) + last = protocol.EXPECT().ReadI32(context.Background()).Return(int32(thrift.PROTOCOL_ERROR), err).After(last) if failAt == 6 { return true } if failAt == 7 { err = failWith } - last = protocol.EXPECT().ReadFieldEnd().Return(err).After(last) + last = protocol.EXPECT().ReadFieldEnd(context.Background()).Return(err).After(last) if failAt == 7 { return true } if failAt == 8 { err = failWith } - last = protocol.EXPECT().ReadFieldBegin().Return("_", thrift.TType(thrift.STOP), int16(2), err).After(last) + last = protocol.EXPECT().ReadFieldBegin(context.Background()).Return("_", thrift.TType(thrift.STOP), int16(2), err).After(last) if failAt == 8 { return true } if failAt == 9 { err = failWith } - last = protocol.EXPECT().ReadStructEnd().Return(err).After(last) + last = protocol.EXPECT().ReadStructEnd(context.Background()).Return(err).After(last) if failAt == 9 { return true } if failAt == 10 { err = failWith } - last = protocol.EXPECT().ReadMessageEnd().Return(err).After(last) + last = protocol.EXPECT().ReadMessageEnd(context.Background()).Return(err).After(last) if failAt == 10 { return true } @@ -697,16 +714,16 @@ func TestClientSeqIdMismatch(t *testing.T) { mockCtrl := gomock.NewController(t) protocol := NewMockTProtocol(mockCtrl) gomock.InOrder( - protocol.EXPECT().WriteMessageBegin("testString", thrift.CALL, int32(1)), - protocol.EXPECT().WriteStructBegin("testString_args"), - protocol.EXPECT().WriteFieldBegin("s", thrift.TType(thrift.STRING), int16(1)), - protocol.EXPECT().WriteString("test"), - protocol.EXPECT().WriteFieldEnd(), - protocol.EXPECT().WriteFieldStop(), - protocol.EXPECT().WriteStructEnd(), - protocol.EXPECT().WriteMessageEnd(), + protocol.EXPECT().WriteMessageBegin(context.Background(), "testString", thrift.CALL, int32(1)), + protocol.EXPECT().WriteStructBegin(context.Background(), "testString_args"), + protocol.EXPECT().WriteFieldBegin(context.Background(), "s", thrift.TType(thrift.STRING), int16(1)), + protocol.EXPECT().WriteString(context.Background(), "test"), + protocol.EXPECT().WriteFieldEnd(context.Background()), + protocol.EXPECT().WriteFieldStop(context.Background()), + protocol.EXPECT().WriteStructEnd(context.Background()), + protocol.EXPECT().WriteMessageEnd(context.Background()), protocol.EXPECT().Flush(context.Background()), - protocol.EXPECT().ReadMessageBegin().Return("testString", thrift.REPLY, int32(2), nil), + protocol.EXPECT().ReadMessageBegin(context.Background()).Return("testString", thrift.REPLY, int32(2), nil), ) client := errortest.NewErrorTestClient(thrift.NewTStandardClient(protocol, protocol)) @@ -728,16 +745,16 @@ func TestClientSeqIdMismatchLegeacy(t *testing.T) { transport := thrift.NewTMemoryBuffer() protocol := NewMockTProtocol(mockCtrl) gomock.InOrder( - protocol.EXPECT().WriteMessageBegin("testString", thrift.CALL, int32(1)), - protocol.EXPECT().WriteStructBegin("testString_args"), - protocol.EXPECT().WriteFieldBegin("s", thrift.TType(thrift.STRING), int16(1)), - protocol.EXPECT().WriteString("test"), - protocol.EXPECT().WriteFieldEnd(), - protocol.EXPECT().WriteFieldStop(), - protocol.EXPECT().WriteStructEnd(), - protocol.EXPECT().WriteMessageEnd(), + protocol.EXPECT().WriteMessageBegin(context.Background(), "testString", thrift.CALL, int32(1)), + protocol.EXPECT().WriteStructBegin(context.Background(), "testString_args"), + protocol.EXPECT().WriteFieldBegin(context.Background(), "s", thrift.TType(thrift.STRING), int16(1)), + protocol.EXPECT().WriteString(context.Background(), "test"), + protocol.EXPECT().WriteFieldEnd(context.Background()), + protocol.EXPECT().WriteFieldStop(context.Background()), + protocol.EXPECT().WriteStructEnd(context.Background()), + protocol.EXPECT().WriteMessageEnd(context.Background()), protocol.EXPECT().Flush(context.Background()), - protocol.EXPECT().ReadMessageBegin().Return("testString", thrift.REPLY, int32(2), nil), + protocol.EXPECT().ReadMessageBegin(context.Background()).Return("testString", thrift.REPLY, int32(2), nil), ) client := errortest.NewErrorTestClientProtocol(transport, protocol, protocol) @@ -757,16 +774,16 @@ func TestClientWrongMethodName(t *testing.T) { mockCtrl := gomock.NewController(t) protocol := NewMockTProtocol(mockCtrl) gomock.InOrder( - protocol.EXPECT().WriteMessageBegin("testString", thrift.CALL, int32(1)), - protocol.EXPECT().WriteStructBegin("testString_args"), - protocol.EXPECT().WriteFieldBegin("s", thrift.TType(thrift.STRING), int16(1)), - protocol.EXPECT().WriteString("test"), - protocol.EXPECT().WriteFieldEnd(), - protocol.EXPECT().WriteFieldStop(), - protocol.EXPECT().WriteStructEnd(), - protocol.EXPECT().WriteMessageEnd(), + protocol.EXPECT().WriteMessageBegin(context.Background(), "testString", thrift.CALL, int32(1)), + protocol.EXPECT().WriteStructBegin(context.Background(), "testString_args"), + protocol.EXPECT().WriteFieldBegin(context.Background(), "s", thrift.TType(thrift.STRING), int16(1)), + protocol.EXPECT().WriteString(context.Background(), "test"), + protocol.EXPECT().WriteFieldEnd(context.Background()), + protocol.EXPECT().WriteFieldStop(context.Background()), + protocol.EXPECT().WriteStructEnd(context.Background()), + protocol.EXPECT().WriteMessageEnd(context.Background()), protocol.EXPECT().Flush(context.Background()), - protocol.EXPECT().ReadMessageBegin().Return("unknown", thrift.REPLY, int32(1), nil), + protocol.EXPECT().ReadMessageBegin(context.Background()).Return("unknown", thrift.REPLY, int32(1), nil), ) client := errortest.NewErrorTestClient(thrift.NewTStandardClient(protocol, protocol)) @@ -788,16 +805,16 @@ func TestClientWrongMethodNameLegacy(t *testing.T) { transport := thrift.NewTMemoryBuffer() protocol := NewMockTProtocol(mockCtrl) gomock.InOrder( - protocol.EXPECT().WriteMessageBegin("testString", thrift.CALL, int32(1)), - protocol.EXPECT().WriteStructBegin("testString_args"), - protocol.EXPECT().WriteFieldBegin("s", thrift.TType(thrift.STRING), int16(1)), - protocol.EXPECT().WriteString("test"), - protocol.EXPECT().WriteFieldEnd(), - protocol.EXPECT().WriteFieldStop(), - protocol.EXPECT().WriteStructEnd(), - protocol.EXPECT().WriteMessageEnd(), + protocol.EXPECT().WriteMessageBegin(context.Background(), "testString", thrift.CALL, int32(1)), + protocol.EXPECT().WriteStructBegin(context.Background(), "testString_args"), + protocol.EXPECT().WriteFieldBegin(context.Background(), "s", thrift.TType(thrift.STRING), int16(1)), + protocol.EXPECT().WriteString(context.Background(), "test"), + protocol.EXPECT().WriteFieldEnd(context.Background()), + protocol.EXPECT().WriteFieldStop(context.Background()), + protocol.EXPECT().WriteStructEnd(context.Background()), + protocol.EXPECT().WriteMessageEnd(context.Background()), protocol.EXPECT().Flush(context.Background()), - protocol.EXPECT().ReadMessageBegin().Return("unknown", thrift.REPLY, int32(1), nil), + protocol.EXPECT().ReadMessageBegin(context.Background()).Return("unknown", thrift.REPLY, int32(1), nil), ) client := errortest.NewErrorTestClientProtocol(transport, protocol, protocol) @@ -817,16 +834,16 @@ func TestClientWrongMessageType(t *testing.T) { mockCtrl := gomock.NewController(t) protocol := NewMockTProtocol(mockCtrl) gomock.InOrder( - protocol.EXPECT().WriteMessageBegin("testString", thrift.CALL, int32(1)), - protocol.EXPECT().WriteStructBegin("testString_args"), - protocol.EXPECT().WriteFieldBegin("s", thrift.TType(thrift.STRING), int16(1)), - protocol.EXPECT().WriteString("test"), - protocol.EXPECT().WriteFieldEnd(), - protocol.EXPECT().WriteFieldStop(), - protocol.EXPECT().WriteStructEnd(), - protocol.EXPECT().WriteMessageEnd(), + protocol.EXPECT().WriteMessageBegin(context.Background(), "testString", thrift.CALL, int32(1)), + protocol.EXPECT().WriteStructBegin(context.Background(), "testString_args"), + protocol.EXPECT().WriteFieldBegin(context.Background(), "s", thrift.TType(thrift.STRING), int16(1)), + protocol.EXPECT().WriteString(context.Background(), "test"), + protocol.EXPECT().WriteFieldEnd(context.Background()), + protocol.EXPECT().WriteFieldStop(context.Background()), + protocol.EXPECT().WriteStructEnd(context.Background()), + protocol.EXPECT().WriteMessageEnd(context.Background()), protocol.EXPECT().Flush(context.Background()), - protocol.EXPECT().ReadMessageBegin().Return("testString", thrift.INVALID_TMESSAGE_TYPE, int32(1), nil), + protocol.EXPECT().ReadMessageBegin(context.Background()).Return("testString", thrift.INVALID_TMESSAGE_TYPE, int32(1), nil), ) client := errortest.NewErrorTestClient(thrift.NewTStandardClient(protocol, protocol)) @@ -848,16 +865,16 @@ func TestClientWrongMessageTypeLegacy(t *testing.T) { transport := thrift.NewTMemoryBuffer() protocol := NewMockTProtocol(mockCtrl) gomock.InOrder( - protocol.EXPECT().WriteMessageBegin("testString", thrift.CALL, int32(1)), - protocol.EXPECT().WriteStructBegin("testString_args"), - protocol.EXPECT().WriteFieldBegin("s", thrift.TType(thrift.STRING), int16(1)), - protocol.EXPECT().WriteString("test"), - protocol.EXPECT().WriteFieldEnd(), - protocol.EXPECT().WriteFieldStop(), - protocol.EXPECT().WriteStructEnd(), - protocol.EXPECT().WriteMessageEnd(), + protocol.EXPECT().WriteMessageBegin(context.Background(), "testString", thrift.CALL, int32(1)), + protocol.EXPECT().WriteStructBegin(context.Background(), "testString_args"), + protocol.EXPECT().WriteFieldBegin(context.Background(), "s", thrift.TType(thrift.STRING), int16(1)), + protocol.EXPECT().WriteString(context.Background(), "test"), + protocol.EXPECT().WriteFieldEnd(context.Background()), + protocol.EXPECT().WriteFieldStop(context.Background()), + protocol.EXPECT().WriteStructEnd(context.Background()), + protocol.EXPECT().WriteMessageEnd(context.Background()), protocol.EXPECT().Flush(context.Background()), - protocol.EXPECT().ReadMessageBegin().Return("testString", thrift.INVALID_TMESSAGE_TYPE, int32(1), nil), + protocol.EXPECT().ReadMessageBegin(context.Background()).Return("testString", thrift.INVALID_TMESSAGE_TYPE, int32(1), nil), ) client := errortest.NewErrorTestClientProtocol(transport, protocol, protocol) diff --git a/lib/go/test/tests/equals_test.go b/lib/go/test/tests/equals_test.go new file mode 100644 index 00000000000..3489efa0d50 --- /dev/null +++ b/lib/go/test/tests/equals_test.go @@ -0,0 +1,243 @@ +package tests + +import ( + "equalstest" + "strconv" + "testing" +) + +func TestEquals(t *testing.T) { + // test basic field + basicTgt, basicSrc := genBasicFoo(), genBasicFoo() + if !basicTgt.Equals(basicSrc) { + t.Error("BasicEqualsFoo.Equals() test failed") + } + basicSrc.EnumFoo = equalstest.EnumFoo_e2 + if basicTgt.Equals(basicSrc) { + t.Error("BasicEqualsFoo.Equals() test failed") + } + basicSrc = genBasicFoo() + basicSrc.OptBoolFoo = nil + if basicTgt.Equals(basicSrc) || basicSrc.Equals(basicTgt) { + t.Error("BasicEqualsFoo.Equals() test failed") + } + if !(&equalstest.BasicEqualsFoo{}).Equals(&equalstest.BasicEqualsFoo{}) { + t.Error("BasicEqualsFoo.Equals() test failed") + } + // test struct field + structTgt, structSrc := genStructFoo(), genStructFoo() + if !structTgt.Equals(structSrc) { + t.Error("StructEqualsFoo.Equals() test failed") + } + structSrc.OptStructFoo.EnumFoo = equalstest.EnumFoo_e2 + if structTgt.Equals(structSrc) { + t.Error("StructEqualsFoo.Equals() test failed") + } + structSrc = genStructFoo() + structSrc.OptStructFoo = nil + if structTgt.Equals(structSrc) || structSrc.Equals(structTgt) { + t.Error("StructEqualsFoo.Equals() test failed") + } + if !(&equalstest.StructEqualsFoo{}).Equals(&equalstest.StructEqualsFoo{}) { + t.Error("StructEqualsFoo.Equals() test failed") + } + // test list field + listTgt, listSrc := genListFoo(), genListFoo() + if !listTgt.Equals(listSrc) { + t.Error("ListEqualsFoo.Equals() test failed") + } + listSrc.OptI64StringMapListFoo[0][1] = "0" + if listTgt.Equals(listSrc) { + t.Error("ListEqualsFoo.Equals() test failed") + } + listSrc = genListFoo() + listSrc.OptI64StringMapListFoo = nil + if listTgt.Equals(listSrc) || listSrc.Equals(listTgt) { + t.Error("ListEqualsFoo.Equals() test failed") + } + if !(&equalstest.ListEqualsFoo{}).Equals(&equalstest.ListEqualsFoo{}) { + t.Error("ListEqualsFoo.Equals() test failed") + } + // test set field + setTgt, setSrc := genSetFoo(), genSetFoo() + if !setTgt.Equals(setSrc) { + t.Error("SetEqualsFoo.Equals() test failed") + } + setSrc.OptI64StringMapSetFoo[0][1] = "0" + if setTgt.Equals(setSrc) { + t.Error("SetEqualsFoo.Equals() test failed") + } + setSrc = genSetFoo() + setSrc.OptI64StringMapSetFoo = nil + if setTgt.Equals(setSrc) || setSrc.Equals(setTgt) { + t.Error("SetEqualsFoo.Equals() test failed") + } + if !(&equalstest.SetEqualsFoo{}).Equals(&equalstest.SetEqualsFoo{}) { + t.Error("SetEqualsFoo.Equals() test failed") + } + // test map field + mapTgt, mapSrc := genMapFoo(), genMapFoo() + if !mapTgt.Equals(mapSrc) { + t.Error("MapEqualsFoo.Equals() test failed") + } + mapSrc.OptI64I64StringMapMapFoo[1][1] = "0" + if mapTgt.Equals(mapSrc) { + t.Error("MapEqualsFoo.Equals() test failed") + } + mapSrc = genMapFoo() + mapSrc.OptI64I64StringMapMapFoo = nil + if mapTgt.Equals(mapSrc) || mapSrc.Equals(mapTgt) { + t.Error("MapEqualsFoo.Equals() test failed") + } + if !(&equalstest.MapEqualsFoo{}).Equals(&equalstest.MapEqualsFoo{}) { + t.Error("MapEqualsFoo.Equals() test failed") + } +} + +func genBasicFoo() *equalstest.BasicEqualsFoo { + return &equalstest.BasicEqualsFoo{ + BoolFoo: true, + OptBoolFoo: &(&struct{ x bool }{true}).x, + I8Foo: 1, + OptI8Foo: &(&struct{ x int8 }{1}).x, + I16Foo: 2, + OptI16Foo: &(&struct{ x int16 }{2}).x, + I32Foo: 3, + OptI32Foo: &(&struct{ x int32 }{3}).x, + I64Foo: 4, + OptI64Foo: &(&struct{ x int64 }{4}).x, + DoubleFoo: 5, + OptDoubleFoo: &(&struct{ x float64 }{5}).x, + StrFoo: "6", + OptStrFoo: &(&struct{ x string }{"6"}).x, + BinFoo: []byte("7"), + OptBinFoo: []byte("7"), + EnumFoo: equalstest.EnumFoo_e1, + OptEnumFoo: equalstest.EnumFooPtr(equalstest.EnumFoo_e1), + MyByteFoo: equalstest.Mybyte(8), + OptMyByteFoo: equalstest.MybytePtr(8), + MyStrFoo: equalstest.Mystr("9"), + OptMyStrFoo: equalstest.MystrPtr(equalstest.Mystr("9")), + MyBinFoo: equalstest.Mybin("10"), + OptMyBinFoo: equalstest.Mybin("10"), + } +} + +func genStructFoo() *equalstest.StructEqualsFoo { + return &equalstest.StructEqualsFoo{ + StructFoo: genBasicFoo(), + OptStructFoo: genBasicFoo(), + } +} + +func genListFoo() *equalstest.ListEqualsFoo { + return &equalstest.ListEqualsFoo{ + I64ListFoo: genInt64Slice(6), + OptI64ListFoo: genInt64Slice(6), + StrListFoo: genStringSlice(6), + OptStrListFoo: genStringSlice(6), + BinListFoo: genBytesSlice(6), + OptBinListFoo: genBytesSlice(6), + StructListFoo: []*equalstest.BasicEqualsFoo{genBasicFoo(), {}}, + OptStructListFoo: []*equalstest.BasicEqualsFoo{genBasicFoo(), {}}, + I64ListListFoo: [][]int64{genInt64Slice(6), genInt64Slice(5), genInt64Slice(4), genInt64Slice(3), genInt64Slice(2), genInt64Slice(1)}, + OptI64ListListFoo: [][]int64{genInt64Slice(6), genInt64Slice(5), genInt64Slice(4), genInt64Slice(3), genInt64Slice(2), genInt64Slice(1)}, + I64SetListFoo: [][]int64{genInt64Slice(6), genInt64Slice(5), genInt64Slice(4), genInt64Slice(3), genInt64Slice(2), genInt64Slice(1)}, + OptI64SetListFoo: [][]int64{genInt64Slice(6), genInt64Slice(5), genInt64Slice(4), genInt64Slice(3), genInt64Slice(2), genInt64Slice(1)}, + I64StringMapListFoo: []map[int64]string{{6: "6"}, {5: "5"}, {4: "4"}, {3: "3"}, {2: "2"}, {1: "1"}}, + OptI64StringMapListFoo: []map[int64]string{{6: "6"}, {5: "5"}, {4: "4"}, {3: "3"}, {2: "2"}, {1: "1"}}, + MyByteListFoo: []equalstest.Mybyte{6, 5, 4, 3, 2, 1}, + OptMyByteListFoo: []equalstest.Mybyte{6, 5, 4, 3, 2, 1}, + MyStrListFoo: []equalstest.Mystr{equalstest.Mystr("6"), equalstest.Mystr("5"), equalstest.Mystr("4"), equalstest.Mystr("3"), equalstest.Mystr("2"), equalstest.Mystr("1")}, + OptMyStrListFoo: []equalstest.Mystr{equalstest.Mystr("6"), equalstest.Mystr("5"), equalstest.Mystr("4"), equalstest.Mystr("3"), equalstest.Mystr("2"), equalstest.Mystr("1")}, + MyBinListFoo: []equalstest.Mybin{equalstest.Mybin("6"), equalstest.Mybin("5"), equalstest.Mybin("4"), equalstest.Mybin("3"), equalstest.Mybin("2"), equalstest.Mybin("1")}, + OptMyBinListFoo: []equalstest.Mybin{equalstest.Mybin("6"), equalstest.Mybin("5"), equalstest.Mybin("4"), equalstest.Mybin("3"), equalstest.Mybin("2"), equalstest.Mybin("1")}, + } +} + +func genSetFoo() *equalstest.SetEqualsFoo { + return &equalstest.SetEqualsFoo{ + I64SetFoo: genInt64Slice(6), + OptI64SetFoo: genInt64Slice(6), + StrSetFoo: genStringSlice(6), + OptStrSetFoo: genStringSlice(6), + BinSetFoo: genBytesSlice(6), + OptBinSetFoo: genBytesSlice(6), + StructSetFoo: []*equalstest.BasicEqualsFoo{genBasicFoo(), {}}, + OptStructSetFoo: []*equalstest.BasicEqualsFoo{genBasicFoo(), {}}, + I64ListSetFoo: [][]int64{genInt64Slice(6), genInt64Slice(5), genInt64Slice(4), genInt64Slice(3), genInt64Slice(2), genInt64Slice(1)}, + OptI64ListSetFoo: [][]int64{genInt64Slice(6), genInt64Slice(5), genInt64Slice(4), genInt64Slice(3), genInt64Slice(2), genInt64Slice(1)}, + I64SetSetFoo: [][]int64{genInt64Slice(6), genInt64Slice(5), genInt64Slice(4), genInt64Slice(3), genInt64Slice(2), genInt64Slice(1)}, + OptI64SetSetFoo: [][]int64{genInt64Slice(6), genInt64Slice(5), genInt64Slice(4), genInt64Slice(3), genInt64Slice(2), genInt64Slice(1)}, + I64StringMapSetFoo: []map[int64]string{{6: "6"}, {5: "5"}, {4: "4"}, {3: "3"}, {2: "2"}, {1: "1"}}, + OptI64StringMapSetFoo: []map[int64]string{{6: "6"}, {5: "5"}, {4: "4"}, {3: "3"}, {2: "2"}, {1: "1"}}, + MyByteSetFoo: []equalstest.Mybyte{6, 5, 4, 3, 2, 1}, + OptMyByteSetFoo: []equalstest.Mybyte{6, 5, 4, 3, 2, 1}, + MyStrSetFoo: []equalstest.Mystr{equalstest.Mystr("6"), equalstest.Mystr("5"), equalstest.Mystr("4"), equalstest.Mystr("3"), equalstest.Mystr("2"), equalstest.Mystr("1")}, + OptMyStrSetFoo: []equalstest.Mystr{equalstest.Mystr("6"), equalstest.Mystr("5"), equalstest.Mystr("4"), equalstest.Mystr("3"), equalstest.Mystr("2"), equalstest.Mystr("1")}, + MyBinSetFoo: []equalstest.Mybin{equalstest.Mybin("6"), equalstest.Mybin("5"), equalstest.Mybin("4"), equalstest.Mybin("3"), equalstest.Mybin("2"), equalstest.Mybin("1")}, + OptMyBinSetFoo: []equalstest.Mybin{equalstest.Mybin("6"), equalstest.Mybin("5"), equalstest.Mybin("4"), equalstest.Mybin("3"), equalstest.Mybin("2"), equalstest.Mybin("1")}, + } +} + +var ( + structMapKey0 = genBasicFoo() + structMapKey1 = &equalstest.BasicEqualsFoo{} +) + +func genMapFoo() *equalstest.MapEqualsFoo { + return &equalstest.MapEqualsFoo{ + I64StrMapFoo: genInt64StringMap(6), + OptI64StrMapFoo: genInt64StringMap(6), + StrI64MapFoo: map[string]int64{"6": 6, "5": 5, "4": 4, "3": 3, "2": 2, "1": 1}, + OptStrI64MapFoo: map[string]int64{"6": 6, "5": 5, "4": 4, "3": 3, "2": 2, "1": 1}, + StructBinMapFoo: map[*equalstest.BasicEqualsFoo][]byte{structMapKey0: []byte("0"), structMapKey1: []byte("1")}, + OptStructBinMapFoo: map[*equalstest.BasicEqualsFoo][]byte{structMapKey0: []byte("0"), structMapKey1: []byte("1")}, + BinStructMapFoo: map[string]*equalstest.BasicEqualsFoo{"1": genBasicFoo(), "0": {}}, + OptBinStructMapFoo: map[string]*equalstest.BasicEqualsFoo{"1": genBasicFoo(), "0": {}}, + I64I64ListMapFoo: map[int64][]int64{6: genInt64Slice(6), 5: genInt64Slice(5), 4: genInt64Slice(4), 3: genInt64Slice(3), 2: genInt64Slice(2), 1: genInt64Slice(1)}, + OptI64I64ListMapFoo: map[int64][]int64{6: genInt64Slice(6), 5: genInt64Slice(5), 4: genInt64Slice(4), 3: genInt64Slice(3), 2: genInt64Slice(2), 1: genInt64Slice(1)}, + I64I64SetMapFoo: map[int64][]int64{6: genInt64Slice(6), 5: genInt64Slice(5), 4: genInt64Slice(4), 3: genInt64Slice(3), 2: genInt64Slice(2), 1: genInt64Slice(1)}, + OptI64I64SetMapFoo: map[int64][]int64{6: genInt64Slice(6), 5: genInt64Slice(5), 4: genInt64Slice(4), 3: genInt64Slice(3), 2: genInt64Slice(2), 1: genInt64Slice(1)}, + I64I64StringMapMapFoo: map[int64]map[int64]string{6: genInt64StringMap(6), 5: genInt64StringMap(5), 4: genInt64StringMap(4), 3: genInt64StringMap(3), 2: genInt64StringMap(2), 1: genInt64StringMap(1)}, + OptI64I64StringMapMapFoo: map[int64]map[int64]string{6: genInt64StringMap(6), 5: genInt64StringMap(5), 4: genInt64StringMap(4), 3: genInt64StringMap(3), 2: genInt64StringMap(2), 1: genInt64StringMap(1)}, + MyStrMyBinMapFoo: map[equalstest.Mystr]equalstest.Mybin{equalstest.Mystr("1"): equalstest.Mybin("1"), equalstest.Mystr("0"): equalstest.Mybin("0")}, + OptMyStrMyBinMapFoo: map[equalstest.Mystr]equalstest.Mybin{equalstest.Mystr("1"): equalstest.Mybin("1"), equalstest.Mystr("0"): equalstest.Mybin("0")}, + Int64MyByteMapFoo: map[int64]equalstest.Mybyte{6: equalstest.Mybyte(6), 5: equalstest.Mybyte(5), 4: equalstest.Mybyte(4), 3: equalstest.Mybyte(3), 2: equalstest.Mybyte(2), 1: equalstest.Mybyte(1)}, + OptInt64MyByteMapFoo: map[int64]equalstest.Mybyte{6: equalstest.Mybyte(6), 5: equalstest.Mybyte(5), 4: equalstest.Mybyte(4), 3: equalstest.Mybyte(3), 2: equalstest.Mybyte(2), 1: equalstest.Mybyte(1)}, + MyByteInt64MapFoo: map[equalstest.Mybyte]int64{equalstest.Mybyte(6): 6, equalstest.Mybyte(5): 5, equalstest.Mybyte(4): 4, equalstest.Mybyte(3): 3, equalstest.Mybyte(2): 2, equalstest.Mybyte(1): 1}, + OptMyByteInt64MapFoo: map[equalstest.Mybyte]int64{equalstest.Mybyte(6): 6, equalstest.Mybyte(5): 5, equalstest.Mybyte(4): 4, equalstest.Mybyte(3): 3, equalstest.Mybyte(2): 2, equalstest.Mybyte(1): 1}, + } +} + +func genInt64Slice(length int) []int64 { + ret := make([]int64, length) + for i := 0; i < length; i++ { + ret[i] = int64(length - i) + } + return ret +} + +func genStringSlice(length int) []string { + ret := make([]string, length) + for i := 0; i < length; i++ { + ret[i] = strconv.Itoa(length - i) + } + return ret +} + +func genBytesSlice(length int) [][]byte { + ret := make([][]byte, length) + for i := 0; i < length; i++ { + ret[i] = []byte(strconv.Itoa(length - i)) + } + return ret +} + +func genInt64StringMap(length int) map[int64]string { + ret := make(map[int64]string, length) + for i := 0; i < length; i++ { + ret[int64(i)] = strconv.Itoa(i) + } + return ret +} diff --git a/lib/go/test/tests/gotag_test.go b/lib/go/test/tests/gotag_test.go index 32f056ff6e6..4cbea560ea8 100644 --- a/lib/go/test/tests/gotag_test.go +++ b/lib/go/test/tests/gotag_test.go @@ -38,8 +38,16 @@ func TestCustomTag(t *testing.T) { s := gotagtest.Tagged{} st := reflect.TypeOf(s) field, ok := st.FieldByName("IntThing") - if !ok || field.Tag.Get("json") != "int_thing,string" { - t.Error("Unexpected custom tag value") + if !ok { + t.Error("Missing field IntThing") + return + } + + if v := field.Tag.Get("json"); v != "custom_thing" { + t.Errorf("Expected custom_thing for tag json, got %s", v) + } + if v := field.Tag.Get("mykey"); v != "myvalue" { + t.Errorf("Expected myvalue for tag mykey, got %s", v) } } @@ -51,3 +59,12 @@ func TestOptionalTag(t *testing.T) { t.Error("Unexpected default tag value for optional field") } } + +func TestOptionalTagWithDefaultValue(t *testing.T) { + s := gotagtest.Tagged{} + st := reflect.TypeOf(s) + field, ok := st.FieldByName("OptionalBoolThing") + if !ok || field.Tag.Get("json") != "optional_bool_thing" { + t.Error("Unexpected default tag value for optional field that has a default value") + } +} diff --git a/lib/go/test/tests/multiplexed_protocol_test.go b/lib/go/test/tests/multiplexed_protocol_test.go index 61ac62828d4..4fb6f4f4d17 100644 --- a/lib/go/test/tests/multiplexed_protocol_test.go +++ b/lib/go/test/tests/multiplexed_protocol_test.go @@ -50,7 +50,7 @@ func (s *SecondImpl) ReturnTwo(ctx context.Context) (r int64, err error) { } func createTransport(addr net.Addr) (thrift.TTransport, error) { - socket := thrift.NewTSocketFromAddrTimeout(addr, TIMEOUT) + socket := thrift.NewTSocketFromAddrTimeout(addr, TIMEOUT, TIMEOUT) transport := thrift.NewTFramedTransport(socket) err := transport.Open() if err != nil { diff --git a/lib/go/test/tests/one_way_test.go b/lib/go/test/tests/one_way_test.go index 48d0bbe3890..010e3bb6ae9 100644 --- a/lib/go/test/tests/one_way_test.go +++ b/lib/go/test/tests/one_way_test.go @@ -65,7 +65,7 @@ func TestInitOneway(t *testing.T) { } func TestInitOnewayClient(t *testing.T) { - transport := thrift.NewTSocketFromAddrTimeout(addr, TIMEOUT) + transport := thrift.NewTSocketFromAddrTimeout(addr, TIMEOUT, TIMEOUT) protocol := thrift.NewTBinaryProtocolTransport(transport) client = onewaytest.NewOneWayClient(thrift.NewTStandardClient(protocol, protocol)) err := transport.Open() diff --git a/lib/go/test/tests/optional_fields_test.go b/lib/go/test/tests/optional_fields_test.go index 34ad6605a47..7e240e6e297 100644 --- a/lib/go/test/tests/optional_fields_test.go +++ b/lib/go/test/tests/optional_fields_test.go @@ -21,6 +21,7 @@ package tests import ( "bytes" + "context" gomock "github.com/golang/mock/gomock" "optionalfieldstest" "testing" @@ -185,12 +186,12 @@ func TestNoOptionalUnsetFieldsOnWire(t *testing.T) { defer mockCtrl.Finish() proto := NewMockTProtocol(mockCtrl) gomock.InOrder( - proto.EXPECT().WriteStructBegin("all_optional").Return(nil), - proto.EXPECT().WriteFieldStop().Return(nil), - proto.EXPECT().WriteStructEnd().Return(nil), + proto.EXPECT().WriteStructBegin(context.Background(), "all_optional").Return(nil), + proto.EXPECT().WriteFieldStop(context.Background()).Return(nil), + proto.EXPECT().WriteStructEnd(context.Background()).Return(nil), ) ao := optionalfieldstest.NewAllOptional() - ao.Write(proto) + ao.Write(context.Background(), proto) } func TestNoSetToDefaultFieldsOnWire(t *testing.T) { @@ -198,13 +199,13 @@ func TestNoSetToDefaultFieldsOnWire(t *testing.T) { defer mockCtrl.Finish() proto := NewMockTProtocol(mockCtrl) gomock.InOrder( - proto.EXPECT().WriteStructBegin("all_optional").Return(nil), - proto.EXPECT().WriteFieldStop().Return(nil), - proto.EXPECT().WriteStructEnd().Return(nil), + proto.EXPECT().WriteStructBegin(context.Background(), "all_optional").Return(nil), + proto.EXPECT().WriteFieldStop(context.Background()).Return(nil), + proto.EXPECT().WriteStructEnd(context.Background()).Return(nil), ) ao := optionalfieldstest.NewAllOptional() ao.I = 42 - ao.Write(proto) + ao.Write(context.Background(), proto) } //Make sure that only one field is being serialized when set to non-default @@ -213,16 +214,16 @@ func TestOneISetFieldOnWire(t *testing.T) { defer mockCtrl.Finish() proto := NewMockTProtocol(mockCtrl) gomock.InOrder( - proto.EXPECT().WriteStructBegin("all_optional").Return(nil), - proto.EXPECT().WriteFieldBegin("i", thrift.TType(thrift.I64), int16(2)).Return(nil), - proto.EXPECT().WriteI64(int64(123)).Return(nil), - proto.EXPECT().WriteFieldEnd().Return(nil), - proto.EXPECT().WriteFieldStop().Return(nil), - proto.EXPECT().WriteStructEnd().Return(nil), + proto.EXPECT().WriteStructBegin(context.Background(), "all_optional").Return(nil), + proto.EXPECT().WriteFieldBegin(context.Background(), "i", thrift.TType(thrift.I64), int16(2)).Return(nil), + proto.EXPECT().WriteI64(context.Background(), int64(123)).Return(nil), + proto.EXPECT().WriteFieldEnd(context.Background()).Return(nil), + proto.EXPECT().WriteFieldStop(context.Background()).Return(nil), + proto.EXPECT().WriteStructEnd(context.Background()).Return(nil), ) ao := optionalfieldstest.NewAllOptional() ao.I = 123 - ao.Write(proto) + ao.Write(context.Background(), proto) } func TestOneLSetFieldOnWire(t *testing.T) { @@ -230,19 +231,19 @@ func TestOneLSetFieldOnWire(t *testing.T) { defer mockCtrl.Finish() proto := NewMockTProtocol(mockCtrl) gomock.InOrder( - proto.EXPECT().WriteStructBegin("all_optional").Return(nil), - proto.EXPECT().WriteFieldBegin("l", thrift.TType(thrift.LIST), int16(9)).Return(nil), - proto.EXPECT().WriteListBegin(thrift.TType(thrift.I64), 2).Return(nil), - proto.EXPECT().WriteI64(int64(1)).Return(nil), - proto.EXPECT().WriteI64(int64(2)).Return(nil), - proto.EXPECT().WriteListEnd().Return(nil), - proto.EXPECT().WriteFieldEnd().Return(nil), - proto.EXPECT().WriteFieldStop().Return(nil), - proto.EXPECT().WriteStructEnd().Return(nil), + proto.EXPECT().WriteStructBegin(context.Background(), "all_optional").Return(nil), + proto.EXPECT().WriteFieldBegin(context.Background(), "l", thrift.TType(thrift.LIST), int16(9)).Return(nil), + proto.EXPECT().WriteListBegin(context.Background(), thrift.TType(thrift.I64), 2).Return(nil), + proto.EXPECT().WriteI64(context.Background(), int64(1)).Return(nil), + proto.EXPECT().WriteI64(context.Background(), int64(2)).Return(nil), + proto.EXPECT().WriteListEnd(context.Background()).Return(nil), + proto.EXPECT().WriteFieldEnd(context.Background()).Return(nil), + proto.EXPECT().WriteFieldStop(context.Background()).Return(nil), + proto.EXPECT().WriteStructEnd(context.Background()).Return(nil), ) ao := optionalfieldstest.NewAllOptional() ao.L = []int64{1, 2} - ao.Write(proto) + ao.Write(context.Background(), proto) } func TestOneBinSetFieldOnWire(t *testing.T) { @@ -250,16 +251,16 @@ func TestOneBinSetFieldOnWire(t *testing.T) { defer mockCtrl.Finish() proto := NewMockTProtocol(mockCtrl) gomock.InOrder( - proto.EXPECT().WriteStructBegin("all_optional").Return(nil), - proto.EXPECT().WriteFieldBegin("bin", thrift.TType(thrift.STRING), int16(13)).Return(nil), - proto.EXPECT().WriteBinary([]byte("somebytestring")).Return(nil), - proto.EXPECT().WriteFieldEnd().Return(nil), - proto.EXPECT().WriteFieldStop().Return(nil), - proto.EXPECT().WriteStructEnd().Return(nil), + proto.EXPECT().WriteStructBegin(context.Background(), "all_optional").Return(nil), + proto.EXPECT().WriteFieldBegin(context.Background(), "bin", thrift.TType(thrift.STRING), int16(13)).Return(nil), + proto.EXPECT().WriteBinary(context.Background(), []byte("somebytestring")).Return(nil), + proto.EXPECT().WriteFieldEnd(context.Background()).Return(nil), + proto.EXPECT().WriteFieldStop(context.Background()).Return(nil), + proto.EXPECT().WriteStructEnd(context.Background()).Return(nil), ) ao := optionalfieldstest.NewAllOptional() ao.Bin = []byte("somebytestring") - ao.Write(proto) + ao.Write(context.Background(), proto) } func TestOneEmptyBinSetFieldOnWire(t *testing.T) { @@ -267,14 +268,14 @@ func TestOneEmptyBinSetFieldOnWire(t *testing.T) { defer mockCtrl.Finish() proto := NewMockTProtocol(mockCtrl) gomock.InOrder( - proto.EXPECT().WriteStructBegin("all_optional").Return(nil), - proto.EXPECT().WriteFieldBegin("bin", thrift.TType(thrift.STRING), int16(13)).Return(nil), - proto.EXPECT().WriteBinary([]byte{}).Return(nil), - proto.EXPECT().WriteFieldEnd().Return(nil), - proto.EXPECT().WriteFieldStop().Return(nil), - proto.EXPECT().WriteStructEnd().Return(nil), + proto.EXPECT().WriteStructBegin(context.Background(), "all_optional").Return(nil), + proto.EXPECT().WriteFieldBegin(context.Background(), "bin", thrift.TType(thrift.STRING), int16(13)).Return(nil), + proto.EXPECT().WriteBinary(context.Background(), []byte{}).Return(nil), + proto.EXPECT().WriteFieldEnd(context.Background()).Return(nil), + proto.EXPECT().WriteFieldStop(context.Background()).Return(nil), + proto.EXPECT().WriteStructEnd(context.Background()).Return(nil), ) ao := optionalfieldstest.NewAllOptional() ao.Bin = []byte{} - ao.Write(proto) + ao.Write(context.Background(), proto) } diff --git a/lib/go/test/tests/protocol_mock.go b/lib/go/test/tests/protocol_mock.go index 51d7a02ff7b..793e4e1c0fd 100644 --- a/lib/go/test/tests/protocol_mock.go +++ b/lib/go/test/tests/protocol_mock.go @@ -60,52 +60,52 @@ func (_mr *_MockTProtocolRecorder) Flush(ctx context.Context) *gomock.Call { return _mr.mock.ctrl.RecordCall(_mr.mock, "Flush") } -func (_m *MockTProtocol) ReadBinary() ([]byte, error) { - ret := _m.ctrl.Call(_m, "ReadBinary") +func (_m *MockTProtocol) ReadBinary(ctx context.Context) ([]byte, error) { + ret := _m.ctrl.Call(_m, "ReadBinary", ctx) ret0, _ := ret[0].([]byte) ret1, _ := ret[1].(error) return ret0, ret1 } -func (_mr *_MockTProtocolRecorder) ReadBinary() *gomock.Call { - return _mr.mock.ctrl.RecordCall(_mr.mock, "ReadBinary") +func (_mr *_MockTProtocolRecorder) ReadBinary(ctx context.Context) *gomock.Call { + return _mr.mock.ctrl.RecordCall(_mr.mock, "ReadBinary", ctx) } -func (_m *MockTProtocol) ReadBool() (bool, error) { - ret := _m.ctrl.Call(_m, "ReadBool") +func (_m *MockTProtocol) ReadBool(ctx context.Context) (bool, error) { + ret := _m.ctrl.Call(_m, "ReadBool", ctx) ret0, _ := ret[0].(bool) ret1, _ := ret[1].(error) return ret0, ret1 } -func (_mr *_MockTProtocolRecorder) ReadBool() *gomock.Call { - return _mr.mock.ctrl.RecordCall(_mr.mock, "ReadBool") +func (_mr *_MockTProtocolRecorder) ReadBool(ctx context.Context) *gomock.Call { + return _mr.mock.ctrl.RecordCall(_mr.mock, "ReadBool", ctx) } -func (_m *MockTProtocol) ReadByte() (int8, error) { - ret := _m.ctrl.Call(_m, "ReadByte") +func (_m *MockTProtocol) ReadByte(ctx context.Context) (int8, error) { + ret := _m.ctrl.Call(_m, "ReadByte", ctx) ret0, _ := ret[0].(int8) ret1, _ := ret[1].(error) return ret0, ret1 } -func (_mr *_MockTProtocolRecorder) ReadByte() *gomock.Call { - return _mr.mock.ctrl.RecordCall(_mr.mock, "ReadByte") +func (_mr *_MockTProtocolRecorder) ReadByte(ctx context.Context) *gomock.Call { + return _mr.mock.ctrl.RecordCall(_mr.mock, "ReadByte", ctx) } -func (_m *MockTProtocol) ReadDouble() (float64, error) { - ret := _m.ctrl.Call(_m, "ReadDouble") +func (_m *MockTProtocol) ReadDouble(ctx context.Context) (float64, error) { + ret := _m.ctrl.Call(_m, "ReadDouble", ctx) ret0, _ := ret[0].(float64) ret1, _ := ret[1].(error) return ret0, ret1 } -func (_mr *_MockTProtocolRecorder) ReadDouble() *gomock.Call { - return _mr.mock.ctrl.RecordCall(_mr.mock, "ReadDouble") +func (_mr *_MockTProtocolRecorder) ReadDouble(ctx context.Context) *gomock.Call { + return _mr.mock.ctrl.RecordCall(_mr.mock, "ReadDouble", ctx) } -func (_m *MockTProtocol) ReadFieldBegin() (string, thrift.TType, int16, error) { - ret := _m.ctrl.Call(_m, "ReadFieldBegin") +func (_m *MockTProtocol) ReadFieldBegin(ctx context.Context) (string, thrift.TType, int16, error) { + ret := _m.ctrl.Call(_m, "ReadFieldBegin", ctx) ret0, _ := ret[0].(string) ret1, _ := ret[1].(thrift.TType) ret2, _ := ret[2].(int16) @@ -113,77 +113,77 @@ func (_m *MockTProtocol) ReadFieldBegin() (string, thrift.TType, int16, error) { return ret0, ret1, ret2, ret3 } -func (_mr *_MockTProtocolRecorder) ReadFieldBegin() *gomock.Call { - return _mr.mock.ctrl.RecordCall(_mr.mock, "ReadFieldBegin") +func (_mr *_MockTProtocolRecorder) ReadFieldBegin(ctx context.Context) *gomock.Call { + return _mr.mock.ctrl.RecordCall(_mr.mock, "ReadFieldBegin", ctx) } -func (_m *MockTProtocol) ReadFieldEnd() error { - ret := _m.ctrl.Call(_m, "ReadFieldEnd") +func (_m *MockTProtocol) ReadFieldEnd(ctx context.Context) error { + ret := _m.ctrl.Call(_m, "ReadFieldEnd", ctx) ret0, _ := ret[0].(error) return ret0 } -func (_mr *_MockTProtocolRecorder) ReadFieldEnd() *gomock.Call { - return _mr.mock.ctrl.RecordCall(_mr.mock, "ReadFieldEnd") +func (_mr *_MockTProtocolRecorder) ReadFieldEnd(ctx context.Context) *gomock.Call { + return _mr.mock.ctrl.RecordCall(_mr.mock, "ReadFieldEnd", ctx) } -func (_m *MockTProtocol) ReadI16() (int16, error) { - ret := _m.ctrl.Call(_m, "ReadI16") +func (_m *MockTProtocol) ReadI16(ctx context.Context) (int16, error) { + ret := _m.ctrl.Call(_m, "ReadI16", ctx) ret0, _ := ret[0].(int16) ret1, _ := ret[1].(error) return ret0, ret1 } -func (_mr *_MockTProtocolRecorder) ReadI16() *gomock.Call { - return _mr.mock.ctrl.RecordCall(_mr.mock, "ReadI16") +func (_mr *_MockTProtocolRecorder) ReadI16(ctx context.Context) *gomock.Call { + return _mr.mock.ctrl.RecordCall(_mr.mock, "ReadI16", ctx) } -func (_m *MockTProtocol) ReadI32() (int32, error) { - ret := _m.ctrl.Call(_m, "ReadI32") +func (_m *MockTProtocol) ReadI32(ctx context.Context) (int32, error) { + ret := _m.ctrl.Call(_m, "ReadI32", ctx) ret0, _ := ret[0].(int32) ret1, _ := ret[1].(error) return ret0, ret1 } -func (_mr *_MockTProtocolRecorder) ReadI32() *gomock.Call { - return _mr.mock.ctrl.RecordCall(_mr.mock, "ReadI32") +func (_mr *_MockTProtocolRecorder) ReadI32(ctx context.Context) *gomock.Call { + return _mr.mock.ctrl.RecordCall(_mr.mock, "ReadI32", ctx) } -func (_m *MockTProtocol) ReadI64() (int64, error) { - ret := _m.ctrl.Call(_m, "ReadI64") +func (_m *MockTProtocol) ReadI64(ctx context.Context) (int64, error) { + ret := _m.ctrl.Call(_m, "ReadI64", ctx) ret0, _ := ret[0].(int64) ret1, _ := ret[1].(error) return ret0, ret1 } -func (_mr *_MockTProtocolRecorder) ReadI64() *gomock.Call { - return _mr.mock.ctrl.RecordCall(_mr.mock, "ReadI64") +func (_mr *_MockTProtocolRecorder) ReadI64(ctx context.Context) *gomock.Call { + return _mr.mock.ctrl.RecordCall(_mr.mock, "ReadI64", ctx) } -func (_m *MockTProtocol) ReadListBegin() (thrift.TType, int, error) { - ret := _m.ctrl.Call(_m, "ReadListBegin") +func (_m *MockTProtocol) ReadListBegin(ctx context.Context) (thrift.TType, int, error) { + ret := _m.ctrl.Call(_m, "ReadListBegin", ctx) ret0, _ := ret[0].(thrift.TType) ret1, _ := ret[1].(int) ret2, _ := ret[2].(error) return ret0, ret1, ret2 } -func (_mr *_MockTProtocolRecorder) ReadListBegin() *gomock.Call { - return _mr.mock.ctrl.RecordCall(_mr.mock, "ReadListBegin") +func (_mr *_MockTProtocolRecorder) ReadListBegin(ctx context.Context) *gomock.Call { + return _mr.mock.ctrl.RecordCall(_mr.mock, "ReadListBegin", ctx) } -func (_m *MockTProtocol) ReadListEnd() error { - ret := _m.ctrl.Call(_m, "ReadListEnd") +func (_m *MockTProtocol) ReadListEnd(ctx context.Context) error { + ret := _m.ctrl.Call(_m, "ReadListEnd", ctx) ret0, _ := ret[0].(error) return ret0 } -func (_mr *_MockTProtocolRecorder) ReadListEnd() *gomock.Call { - return _mr.mock.ctrl.RecordCall(_mr.mock, "ReadListEnd") +func (_mr *_MockTProtocolRecorder) ReadListEnd(ctx context.Context) *gomock.Call { + return _mr.mock.ctrl.RecordCall(_mr.mock, "ReadListEnd", ctx) } -func (_m *MockTProtocol) ReadMapBegin() (thrift.TType, thrift.TType, int, error) { - ret := _m.ctrl.Call(_m, "ReadMapBegin") +func (_m *MockTProtocol) ReadMapBegin(ctx context.Context) (thrift.TType, thrift.TType, int, error) { + ret := _m.ctrl.Call(_m, "ReadMapBegin", ctx) ret0, _ := ret[0].(thrift.TType) ret1, _ := ret[1].(thrift.TType) ret2, _ := ret[2].(int) @@ -191,22 +191,22 @@ func (_m *MockTProtocol) ReadMapBegin() (thrift.TType, thrift.TType, int, error) return ret0, ret1, ret2, ret3 } -func (_mr *_MockTProtocolRecorder) ReadMapBegin() *gomock.Call { - return _mr.mock.ctrl.RecordCall(_mr.mock, "ReadMapBegin") +func (_mr *_MockTProtocolRecorder) ReadMapBegin(ctx context.Context) *gomock.Call { + return _mr.mock.ctrl.RecordCall(_mr.mock, "ReadMapBegin", ctx) } -func (_m *MockTProtocol) ReadMapEnd() error { - ret := _m.ctrl.Call(_m, "ReadMapEnd") +func (_m *MockTProtocol) ReadMapEnd(ctx context.Context) error { + ret := _m.ctrl.Call(_m, "ReadMapEnd", ctx) ret0, _ := ret[0].(error) return ret0 } -func (_mr *_MockTProtocolRecorder) ReadMapEnd() *gomock.Call { - return _mr.mock.ctrl.RecordCall(_mr.mock, "ReadMapEnd") +func (_mr *_MockTProtocolRecorder) ReadMapEnd(ctx context.Context) *gomock.Call { + return _mr.mock.ctrl.RecordCall(_mr.mock, "ReadMapEnd", ctx) } -func (_m *MockTProtocol) ReadMessageBegin() (string, thrift.TMessageType, int32, error) { - ret := _m.ctrl.Call(_m, "ReadMessageBegin") +func (_m *MockTProtocol) ReadMessageBegin(ctx context.Context) (string, thrift.TMessageType, int32, error) { + ret := _m.ctrl.Call(_m, "ReadMessageBegin", ctx) ret0, _ := ret[0].(string) ret1, _ := ret[1].(thrift.TMessageType) ret2, _ := ret[2].(int32) @@ -214,82 +214,82 @@ func (_m *MockTProtocol) ReadMessageBegin() (string, thrift.TMessageType, int32, return ret0, ret1, ret2, ret3 } -func (_mr *_MockTProtocolRecorder) ReadMessageBegin() *gomock.Call { - return _mr.mock.ctrl.RecordCall(_mr.mock, "ReadMessageBegin") +func (_mr *_MockTProtocolRecorder) ReadMessageBegin(ctx context.Context) *gomock.Call { + return _mr.mock.ctrl.RecordCall(_mr.mock, "ReadMessageBegin", ctx) } -func (_m *MockTProtocol) ReadMessageEnd() error { - ret := _m.ctrl.Call(_m, "ReadMessageEnd") +func (_m *MockTProtocol) ReadMessageEnd(ctx context.Context) error { + ret := _m.ctrl.Call(_m, "ReadMessageEnd", ctx) ret0, _ := ret[0].(error) return ret0 } -func (_mr *_MockTProtocolRecorder) ReadMessageEnd() *gomock.Call { - return _mr.mock.ctrl.RecordCall(_mr.mock, "ReadMessageEnd") +func (_mr *_MockTProtocolRecorder) ReadMessageEnd(ctx context.Context) *gomock.Call { + return _mr.mock.ctrl.RecordCall(_mr.mock, "ReadMessageEnd", ctx) } -func (_m *MockTProtocol) ReadSetBegin() (thrift.TType, int, error) { - ret := _m.ctrl.Call(_m, "ReadSetBegin") +func (_m *MockTProtocol) ReadSetBegin(ctx context.Context) (thrift.TType, int, error) { + ret := _m.ctrl.Call(_m, "ReadSetBegin", ctx) ret0, _ := ret[0].(thrift.TType) ret1, _ := ret[1].(int) ret2, _ := ret[2].(error) return ret0, ret1, ret2 } -func (_mr *_MockTProtocolRecorder) ReadSetBegin() *gomock.Call { - return _mr.mock.ctrl.RecordCall(_mr.mock, "ReadSetBegin") +func (_mr *_MockTProtocolRecorder) ReadSetBegin(ctx context.Context) *gomock.Call { + return _mr.mock.ctrl.RecordCall(_mr.mock, "ReadSetBegin", ctx) } -func (_m *MockTProtocol) ReadSetEnd() error { - ret := _m.ctrl.Call(_m, "ReadSetEnd") +func (_m *MockTProtocol) ReadSetEnd(ctx context.Context) error { + ret := _m.ctrl.Call(_m, "ReadSetEnd", ctx) ret0, _ := ret[0].(error) return ret0 } -func (_mr *_MockTProtocolRecorder) ReadSetEnd() *gomock.Call { - return _mr.mock.ctrl.RecordCall(_mr.mock, "ReadSetEnd") +func (_mr *_MockTProtocolRecorder) ReadSetEnd(ctx context.Context) *gomock.Call { + return _mr.mock.ctrl.RecordCall(_mr.mock, "ReadSetEnd", ctx) } -func (_m *MockTProtocol) ReadString() (string, error) { - ret := _m.ctrl.Call(_m, "ReadString") +func (_m *MockTProtocol) ReadString(ctx context.Context) (string, error) { + ret := _m.ctrl.Call(_m, "ReadString", ctx) ret0, _ := ret[0].(string) ret1, _ := ret[1].(error) return ret0, ret1 } -func (_mr *_MockTProtocolRecorder) ReadString() *gomock.Call { - return _mr.mock.ctrl.RecordCall(_mr.mock, "ReadString") +func (_mr *_MockTProtocolRecorder) ReadString(ctx context.Context) *gomock.Call { + return _mr.mock.ctrl.RecordCall(_mr.mock, "ReadString", ctx) } -func (_m *MockTProtocol) ReadStructBegin() (string, error) { - ret := _m.ctrl.Call(_m, "ReadStructBegin") +func (_m *MockTProtocol) ReadStructBegin(ctx context.Context) (string, error) { + ret := _m.ctrl.Call(_m, "ReadStructBegin", ctx) ret0, _ := ret[0].(string) ret1, _ := ret[1].(error) return ret0, ret1 } -func (_mr *_MockTProtocolRecorder) ReadStructBegin() *gomock.Call { - return _mr.mock.ctrl.RecordCall(_mr.mock, "ReadStructBegin") +func (_mr *_MockTProtocolRecorder) ReadStructBegin(ctx context.Context) *gomock.Call { + return _mr.mock.ctrl.RecordCall(_mr.mock, "ReadStructBegin", ctx) } -func (_m *MockTProtocol) ReadStructEnd() error { - ret := _m.ctrl.Call(_m, "ReadStructEnd") +func (_m *MockTProtocol) ReadStructEnd(ctx context.Context) error { + ret := _m.ctrl.Call(_m, "ReadStructEnd", ctx) ret0, _ := ret[0].(error) return ret0 } -func (_mr *_MockTProtocolRecorder) ReadStructEnd() *gomock.Call { - return _mr.mock.ctrl.RecordCall(_mr.mock, "ReadStructEnd") +func (_mr *_MockTProtocolRecorder) ReadStructEnd(ctx context.Context) *gomock.Call { + return _mr.mock.ctrl.RecordCall(_mr.mock, "ReadStructEnd", ctx) } -func (_m *MockTProtocol) Skip(_param0 thrift.TType) error { - ret := _m.ctrl.Call(_m, "Skip", _param0) +func (_m *MockTProtocol) Skip(ctx context.Context, _param0 thrift.TType) error { + ret := _m.ctrl.Call(_m, "Skip", ctx, _param0) ret0, _ := ret[0].(error) return ret0 } -func (_mr *_MockTProtocolRecorder) Skip(arg0 interface{}) *gomock.Call { - return _mr.mock.ctrl.RecordCall(_mr.mock, "Skip", arg0) +func (_mr *_MockTProtocolRecorder) Skip(ctx context.Context, arg0 interface{}) *gomock.Call { + return _mr.mock.ctrl.RecordCall(_mr.mock, "Skip", ctx, arg0) } func (_m *MockTProtocol) Transport() thrift.TTransport { @@ -302,212 +302,212 @@ func (_mr *_MockTProtocolRecorder) Transport() *gomock.Call { return _mr.mock.ctrl.RecordCall(_mr.mock, "Transport") } -func (_m *MockTProtocol) WriteBinary(_param0 []byte) error { - ret := _m.ctrl.Call(_m, "WriteBinary", _param0) +func (_m *MockTProtocol) WriteBinary(ctx context.Context, _param0 []byte) error { + ret := _m.ctrl.Call(_m, "WriteBinary", ctx, _param0) ret0, _ := ret[0].(error) return ret0 } -func (_mr *_MockTProtocolRecorder) WriteBinary(arg0 interface{}) *gomock.Call { - return _mr.mock.ctrl.RecordCall(_mr.mock, "WriteBinary", arg0) +func (_mr *_MockTProtocolRecorder) WriteBinary(ctx context.Context, arg0 interface{}) *gomock.Call { + return _mr.mock.ctrl.RecordCall(_mr.mock, "WriteBinary", ctx, arg0) } -func (_m *MockTProtocol) WriteBool(_param0 bool) error { - ret := _m.ctrl.Call(_m, "WriteBool", _param0) +func (_m *MockTProtocol) WriteBool(ctx context.Context, _param0 bool) error { + ret := _m.ctrl.Call(_m, "WriteBool", ctx, _param0) ret0, _ := ret[0].(error) return ret0 } -func (_mr *_MockTProtocolRecorder) WriteBool(arg0 interface{}) *gomock.Call { - return _mr.mock.ctrl.RecordCall(_mr.mock, "WriteBool", arg0) +func (_mr *_MockTProtocolRecorder) WriteBool(ctx context.Context, arg0 interface{}) *gomock.Call { + return _mr.mock.ctrl.RecordCall(_mr.mock, "WriteBool", ctx, arg0) } -func (_m *MockTProtocol) WriteByte(_param0 int8) error { - ret := _m.ctrl.Call(_m, "WriteByte", _param0) +func (_m *MockTProtocol) WriteByte(ctx context.Context, _param0 int8) error { + ret := _m.ctrl.Call(_m, "WriteByte", ctx, _param0) ret0, _ := ret[0].(error) return ret0 } -func (_mr *_MockTProtocolRecorder) WriteByte(arg0 interface{}) *gomock.Call { - return _mr.mock.ctrl.RecordCall(_mr.mock, "WriteByte", arg0) +func (_mr *_MockTProtocolRecorder) WriteByte(ctx context.Context, arg0 interface{}) *gomock.Call { + return _mr.mock.ctrl.RecordCall(_mr.mock, "WriteByte", ctx, arg0) } -func (_m *MockTProtocol) WriteDouble(_param0 float64) error { - ret := _m.ctrl.Call(_m, "WriteDouble", _param0) +func (_m *MockTProtocol) WriteDouble(ctx context.Context, _param0 float64) error { + ret := _m.ctrl.Call(_m, "WriteDouble", ctx, _param0) ret0, _ := ret[0].(error) return ret0 } -func (_mr *_MockTProtocolRecorder) WriteDouble(arg0 interface{}) *gomock.Call { - return _mr.mock.ctrl.RecordCall(_mr.mock, "WriteDouble", arg0) +func (_mr *_MockTProtocolRecorder) WriteDouble(ctx context.Context, arg0 interface{}) *gomock.Call { + return _mr.mock.ctrl.RecordCall(_mr.mock, "WriteDouble", ctx, arg0) } -func (_m *MockTProtocol) WriteFieldBegin(_param0 string, _param1 thrift.TType, _param2 int16) error { - ret := _m.ctrl.Call(_m, "WriteFieldBegin", _param0, _param1, _param2) +func (_m *MockTProtocol) WriteFieldBegin(ctx context.Context, _param0 string, _param1 thrift.TType, _param2 int16) error { + ret := _m.ctrl.Call(_m, "WriteFieldBegin", ctx, _param0, _param1, _param2) ret0, _ := ret[0].(error) return ret0 } -func (_mr *_MockTProtocolRecorder) WriteFieldBegin(arg0, arg1, arg2 interface{}) *gomock.Call { - return _mr.mock.ctrl.RecordCall(_mr.mock, "WriteFieldBegin", arg0, arg1, arg2) +func (_mr *_MockTProtocolRecorder) WriteFieldBegin(ctx context.Context, arg0, arg1, arg2 interface{}) *gomock.Call { + return _mr.mock.ctrl.RecordCall(_mr.mock, "WriteFieldBegin", ctx, arg0, arg1, arg2) } -func (_m *MockTProtocol) WriteFieldEnd() error { - ret := _m.ctrl.Call(_m, "WriteFieldEnd") +func (_m *MockTProtocol) WriteFieldEnd(ctx context.Context) error { + ret := _m.ctrl.Call(_m, "WriteFieldEnd", ctx) ret0, _ := ret[0].(error) return ret0 } -func (_mr *_MockTProtocolRecorder) WriteFieldEnd() *gomock.Call { - return _mr.mock.ctrl.RecordCall(_mr.mock, "WriteFieldEnd") +func (_mr *_MockTProtocolRecorder) WriteFieldEnd(ctx context.Context) *gomock.Call { + return _mr.mock.ctrl.RecordCall(_mr.mock, "WriteFieldEnd", ctx) } -func (_m *MockTProtocol) WriteFieldStop() error { - ret := _m.ctrl.Call(_m, "WriteFieldStop") +func (_m *MockTProtocol) WriteFieldStop(ctx context.Context) error { + ret := _m.ctrl.Call(_m, "WriteFieldStop", ctx) ret0, _ := ret[0].(error) return ret0 } -func (_mr *_MockTProtocolRecorder) WriteFieldStop() *gomock.Call { - return _mr.mock.ctrl.RecordCall(_mr.mock, "WriteFieldStop") +func (_mr *_MockTProtocolRecorder) WriteFieldStop(ctx context.Context) *gomock.Call { + return _mr.mock.ctrl.RecordCall(_mr.mock, "WriteFieldStop", ctx) } -func (_m *MockTProtocol) WriteI16(_param0 int16) error { - ret := _m.ctrl.Call(_m, "WriteI16", _param0) +func (_m *MockTProtocol) WriteI16(ctx context.Context, _param0 int16) error { + ret := _m.ctrl.Call(_m, "WriteI16", ctx, _param0) ret0, _ := ret[0].(error) return ret0 } -func (_mr *_MockTProtocolRecorder) WriteI16(arg0 interface{}) *gomock.Call { - return _mr.mock.ctrl.RecordCall(_mr.mock, "WriteI16", arg0) +func (_mr *_MockTProtocolRecorder) WriteI16(ctx context.Context, arg0 interface{}) *gomock.Call { + return _mr.mock.ctrl.RecordCall(_mr.mock, "WriteI16", ctx, arg0) } -func (_m *MockTProtocol) WriteI32(_param0 int32) error { - ret := _m.ctrl.Call(_m, "WriteI32", _param0) +func (_m *MockTProtocol) WriteI32(ctx context.Context, _param0 int32) error { + ret := _m.ctrl.Call(_m, "WriteI32", ctx, _param0) ret0, _ := ret[0].(error) return ret0 } -func (_mr *_MockTProtocolRecorder) WriteI32(arg0 interface{}) *gomock.Call { - return _mr.mock.ctrl.RecordCall(_mr.mock, "WriteI32", arg0) +func (_mr *_MockTProtocolRecorder) WriteI32(ctx context.Context, arg0 interface{}) *gomock.Call { + return _mr.mock.ctrl.RecordCall(_mr.mock, "WriteI32", ctx, arg0) } -func (_m *MockTProtocol) WriteI64(_param0 int64) error { - ret := _m.ctrl.Call(_m, "WriteI64", _param0) +func (_m *MockTProtocol) WriteI64(ctx context.Context, _param0 int64) error { + ret := _m.ctrl.Call(_m, "WriteI64", ctx, _param0) ret0, _ := ret[0].(error) return ret0 } -func (_mr *_MockTProtocolRecorder) WriteI64(arg0 interface{}) *gomock.Call { - return _mr.mock.ctrl.RecordCall(_mr.mock, "WriteI64", arg0) +func (_mr *_MockTProtocolRecorder) WriteI64(ctx context.Context, arg0 interface{}) *gomock.Call { + return _mr.mock.ctrl.RecordCall(_mr.mock, "WriteI64", ctx, arg0) } -func (_m *MockTProtocol) WriteListBegin(_param0 thrift.TType, _param1 int) error { - ret := _m.ctrl.Call(_m, "WriteListBegin", _param0, _param1) +func (_m *MockTProtocol) WriteListBegin(ctx context.Context, _param0 thrift.TType, _param1 int) error { + ret := _m.ctrl.Call(_m, "WriteListBegin", ctx, _param0, _param1) ret0, _ := ret[0].(error) return ret0 } -func (_mr *_MockTProtocolRecorder) WriteListBegin(arg0, arg1 interface{}) *gomock.Call { - return _mr.mock.ctrl.RecordCall(_mr.mock, "WriteListBegin", arg0, arg1) +func (_mr *_MockTProtocolRecorder) WriteListBegin(ctx context.Context, arg0, arg1 interface{}) *gomock.Call { + return _mr.mock.ctrl.RecordCall(_mr.mock, "WriteListBegin", ctx, arg0, arg1) } -func (_m *MockTProtocol) WriteListEnd() error { - ret := _m.ctrl.Call(_m, "WriteListEnd") +func (_m *MockTProtocol) WriteListEnd(ctx context.Context) error { + ret := _m.ctrl.Call(_m, "WriteListEnd", ctx) ret0, _ := ret[0].(error) return ret0 } -func (_mr *_MockTProtocolRecorder) WriteListEnd() *gomock.Call { - return _mr.mock.ctrl.RecordCall(_mr.mock, "WriteListEnd") +func (_mr *_MockTProtocolRecorder) WriteListEnd(ctx context.Context) *gomock.Call { + return _mr.mock.ctrl.RecordCall(_mr.mock, "WriteListEnd", ctx) } -func (_m *MockTProtocol) WriteMapBegin(_param0 thrift.TType, _param1 thrift.TType, _param2 int) error { - ret := _m.ctrl.Call(_m, "WriteMapBegin", _param0, _param1, _param2) +func (_m *MockTProtocol) WriteMapBegin(ctx context.Context, _param0 thrift.TType, _param1 thrift.TType, _param2 int) error { + ret := _m.ctrl.Call(_m, "WriteMapBegin", ctx, _param0, _param1, _param2) ret0, _ := ret[0].(error) return ret0 } -func (_mr *_MockTProtocolRecorder) WriteMapBegin(arg0, arg1, arg2 interface{}) *gomock.Call { - return _mr.mock.ctrl.RecordCall(_mr.mock, "WriteMapBegin", arg0, arg1, arg2) +func (_mr *_MockTProtocolRecorder) WriteMapBegin(ctx context.Context, arg0, arg1, arg2 interface{}) *gomock.Call { + return _mr.mock.ctrl.RecordCall(_mr.mock, "WriteMapBegin", ctx, arg0, arg1, arg2) } -func (_m *MockTProtocol) WriteMapEnd() error { - ret := _m.ctrl.Call(_m, "WriteMapEnd") +func (_m *MockTProtocol) WriteMapEnd(ctx context.Context) error { + ret := _m.ctrl.Call(_m, "WriteMapEnd", ctx) ret0, _ := ret[0].(error) return ret0 } -func (_mr *_MockTProtocolRecorder) WriteMapEnd() *gomock.Call { - return _mr.mock.ctrl.RecordCall(_mr.mock, "WriteMapEnd") +func (_mr *_MockTProtocolRecorder) WriteMapEnd(ctx context.Context) *gomock.Call { + return _mr.mock.ctrl.RecordCall(_mr.mock, "WriteMapEnd", ctx) } -func (_m *MockTProtocol) WriteMessageBegin(_param0 string, _param1 thrift.TMessageType, _param2 int32) error { - ret := _m.ctrl.Call(_m, "WriteMessageBegin", _param0, _param1, _param2) +func (_m *MockTProtocol) WriteMessageBegin(ctx context.Context, _param0 string, _param1 thrift.TMessageType, _param2 int32) error { + ret := _m.ctrl.Call(_m, "WriteMessageBegin", ctx, _param0, _param1, _param2) ret0, _ := ret[0].(error) return ret0 } -func (_mr *_MockTProtocolRecorder) WriteMessageBegin(arg0, arg1, arg2 interface{}) *gomock.Call { - return _mr.mock.ctrl.RecordCall(_mr.mock, "WriteMessageBegin", arg0, arg1, arg2) +func (_mr *_MockTProtocolRecorder) WriteMessageBegin(ctx context.Context, arg0, arg1, arg2 interface{}) *gomock.Call { + return _mr.mock.ctrl.RecordCall(_mr.mock, "WriteMessageBegin", ctx, arg0, arg1, arg2) } -func (_m *MockTProtocol) WriteMessageEnd() error { - ret := _m.ctrl.Call(_m, "WriteMessageEnd") +func (_m *MockTProtocol) WriteMessageEnd(ctx context.Context) error { + ret := _m.ctrl.Call(_m, "WriteMessageEnd", ctx) ret0, _ := ret[0].(error) return ret0 } -func (_mr *_MockTProtocolRecorder) WriteMessageEnd() *gomock.Call { - return _mr.mock.ctrl.RecordCall(_mr.mock, "WriteMessageEnd") +func (_mr *_MockTProtocolRecorder) WriteMessageEnd(ctx context.Context) *gomock.Call { + return _mr.mock.ctrl.RecordCall(_mr.mock, "WriteMessageEnd", ctx) } -func (_m *MockTProtocol) WriteSetBegin(_param0 thrift.TType, _param1 int) error { - ret := _m.ctrl.Call(_m, "WriteSetBegin", _param0, _param1) +func (_m *MockTProtocol) WriteSetBegin(ctx context.Context, _param0 thrift.TType, _param1 int) error { + ret := _m.ctrl.Call(_m, "WriteSetBegin", ctx, _param0, _param1) ret0, _ := ret[0].(error) return ret0 } -func (_mr *_MockTProtocolRecorder) WriteSetBegin(arg0, arg1 interface{}) *gomock.Call { - return _mr.mock.ctrl.RecordCall(_mr.mock, "WriteSetBegin", arg0, arg1) +func (_mr *_MockTProtocolRecorder) WriteSetBegin(ctx context.Context, arg0, arg1 interface{}) *gomock.Call { + return _mr.mock.ctrl.RecordCall(_mr.mock, "WriteSetBegin", ctx, arg0, arg1) } -func (_m *MockTProtocol) WriteSetEnd() error { - ret := _m.ctrl.Call(_m, "WriteSetEnd") +func (_m *MockTProtocol) WriteSetEnd(ctx context.Context) error { + ret := _m.ctrl.Call(_m, "WriteSetEnd", ctx) ret0, _ := ret[0].(error) return ret0 } -func (_mr *_MockTProtocolRecorder) WriteSetEnd() *gomock.Call { - return _mr.mock.ctrl.RecordCall(_mr.mock, "WriteSetEnd") +func (_mr *_MockTProtocolRecorder) WriteSetEnd(ctx context.Context) *gomock.Call { + return _mr.mock.ctrl.RecordCall(_mr.mock, "WriteSetEnd", ctx) } -func (_m *MockTProtocol) WriteString(_param0 string) error { - ret := _m.ctrl.Call(_m, "WriteString", _param0) +func (_m *MockTProtocol) WriteString(ctx context.Context, _param0 string) error { + ret := _m.ctrl.Call(_m, "WriteString", ctx, _param0) ret0, _ := ret[0].(error) return ret0 } -func (_mr *_MockTProtocolRecorder) WriteString(arg0 interface{}) *gomock.Call { - return _mr.mock.ctrl.RecordCall(_mr.mock, "WriteString", arg0) +func (_mr *_MockTProtocolRecorder) WriteString(ctx context.Context, arg0 interface{}) *gomock.Call { + return _mr.mock.ctrl.RecordCall(_mr.mock, "WriteString", ctx, arg0) } -func (_m *MockTProtocol) WriteStructBegin(_param0 string) error { - ret := _m.ctrl.Call(_m, "WriteStructBegin", _param0) +func (_m *MockTProtocol) WriteStructBegin(ctx context.Context, _param0 string) error { + ret := _m.ctrl.Call(_m, "WriteStructBegin", ctx, _param0) ret0, _ := ret[0].(error) return ret0 } -func (_mr *_MockTProtocolRecorder) WriteStructBegin(arg0 interface{}) *gomock.Call { - return _mr.mock.ctrl.RecordCall(_mr.mock, "WriteStructBegin", arg0) +func (_mr *_MockTProtocolRecorder) WriteStructBegin(ctx context.Context, arg0 interface{}) *gomock.Call { + return _mr.mock.ctrl.RecordCall(_mr.mock, "WriteStructBegin", ctx, arg0) } -func (_m *MockTProtocol) WriteStructEnd() error { - ret := _m.ctrl.Call(_m, "WriteStructEnd") +func (_m *MockTProtocol) WriteStructEnd(ctx context.Context) error { + ret := _m.ctrl.Call(_m, "WriteStructEnd", ctx) ret0, _ := ret[0].(error) return ret0 } -func (_mr *_MockTProtocolRecorder) WriteStructEnd() *gomock.Call { - return _mr.mock.ctrl.RecordCall(_mr.mock, "WriteStructEnd") +func (_mr *_MockTProtocolRecorder) WriteStructEnd(ctx context.Context) *gomock.Call { + return _mr.mock.ctrl.RecordCall(_mr.mock, "WriteStructEnd", ctx) } diff --git a/lib/go/test/tests/protocols_test.go b/lib/go/test/tests/protocols_test.go index cffd9c3f7e9..9030e9d5a31 100644 --- a/lib/go/test/tests/protocols_test.go +++ b/lib/go/test/tests/protocols_test.go @@ -41,7 +41,7 @@ func RunSocketTestSuite(t *testing.T, protocolFactory thrift.TProtocolFactory, go server.Serve() // client - var transport thrift.TTransport = thrift.NewTSocketFromAddrTimeout(addr, TIMEOUT) + var transport thrift.TTransport = thrift.NewTSocketFromAddrTimeout(addr, TIMEOUT, TIMEOUT) transport, err = transportFactory.GetTransport(transport) if err != nil { t.Fatal(err) diff --git a/lib/go/test/tests/required_fields_test.go b/lib/go/test/tests/required_fields_test.go index 3fa414ad82a..06e8560e524 100644 --- a/lib/go/test/tests/required_fields_test.go +++ b/lib/go/test/tests/required_fields_test.go @@ -37,7 +37,7 @@ func TestRequiredField_SucecssWhenSet(t *testing.T) { } d := thrift.NewTDeserializer() - err = d.Read(&requiredfieldtest.RequiredField{}, sourceData) + err = d.Read(context.Background(), &requiredfieldtest.RequiredField{}, sourceData) if err != nil { t.Fatalf("Did not expect an error when trying to deserialize the requiredfieldtest.RequiredField: %v", err) } @@ -53,7 +53,7 @@ func TestRequiredField_ErrorWhenMissing(t *testing.T) { // attempt to deserialize into a different type (which should fail) d := thrift.NewTDeserializer() - err = d.Read(&requiredfieldtest.RequiredField{}, sourceData) + err = d.Read(context.Background(), &requiredfieldtest.RequiredField{}, sourceData) if err == nil { t.Fatal("Expected an error when trying to deserialize an object which is missing a required field") } @@ -66,12 +66,12 @@ func TestStructReadRequiredFields(t *testing.T) { // None of required fields are set gomock.InOrder( - protocol.EXPECT().ReadStructBegin().Return("StructC", nil), - protocol.EXPECT().ReadFieldBegin().Return("_", thrift.TType(thrift.STOP), int16(1), nil), - protocol.EXPECT().ReadStructEnd().Return(nil), + protocol.EXPECT().ReadStructBegin(context.Background()).Return("StructC", nil), + protocol.EXPECT().ReadFieldBegin(context.Background()).Return("_", thrift.TType(thrift.STOP), int16(1), nil), + protocol.EXPECT().ReadStructEnd(context.Background()).Return(nil), ) - err := testStruct.Read(protocol) + err := testStruct.Read(context.Background(), protocol) mockCtrl.Finish() mockCtrl = gomock.NewController(t) if err == nil { @@ -87,15 +87,15 @@ func TestStructReadRequiredFields(t *testing.T) { // One of the required fields is set gomock.InOrder( - protocol.EXPECT().ReadStructBegin().Return("StructC", nil), - protocol.EXPECT().ReadFieldBegin().Return("I", thrift.TType(thrift.I32), int16(2), nil), - protocol.EXPECT().ReadI32().Return(int32(1), nil), - protocol.EXPECT().ReadFieldEnd().Return(nil), - protocol.EXPECT().ReadFieldBegin().Return("_", thrift.TType(thrift.STOP), int16(1), nil), - protocol.EXPECT().ReadStructEnd().Return(nil), + protocol.EXPECT().ReadStructBegin(context.Background()).Return("StructC", nil), + protocol.EXPECT().ReadFieldBegin(context.Background()).Return("I", thrift.TType(thrift.I32), int16(2), nil), + protocol.EXPECT().ReadI32(context.Background()).Return(int32(1), nil), + protocol.EXPECT().ReadFieldEnd(context.Background()).Return(nil), + protocol.EXPECT().ReadFieldBegin(context.Background()).Return("_", thrift.TType(thrift.STOP), int16(1), nil), + protocol.EXPECT().ReadStructEnd(context.Background()).Return(nil), ) - err = testStruct.Read(protocol) + err = testStruct.Read(context.Background(), protocol) mockCtrl.Finish() mockCtrl = gomock.NewController(t) if err == nil { @@ -111,18 +111,18 @@ func TestStructReadRequiredFields(t *testing.T) { // Both of the required fields are set gomock.InOrder( - protocol.EXPECT().ReadStructBegin().Return("StructC", nil), - protocol.EXPECT().ReadFieldBegin().Return("i", thrift.TType(thrift.I32), int16(2), nil), - protocol.EXPECT().ReadI32().Return(int32(1), nil), - protocol.EXPECT().ReadFieldEnd().Return(nil), - protocol.EXPECT().ReadFieldBegin().Return("s2", thrift.TType(thrift.STRING), int16(4), nil), - protocol.EXPECT().ReadString().Return("test", nil), - protocol.EXPECT().ReadFieldEnd().Return(nil), - protocol.EXPECT().ReadFieldBegin().Return("_", thrift.TType(thrift.STOP), int16(1), nil), - protocol.EXPECT().ReadStructEnd().Return(nil), + protocol.EXPECT().ReadStructBegin(context.Background()).Return("StructC", nil), + protocol.EXPECT().ReadFieldBegin(context.Background()).Return("i", thrift.TType(thrift.I32), int16(2), nil), + protocol.EXPECT().ReadI32(context.Background()).Return(int32(1), nil), + protocol.EXPECT().ReadFieldEnd(context.Background()).Return(nil), + protocol.EXPECT().ReadFieldBegin(context.Background()).Return("s2", thrift.TType(thrift.STRING), int16(4), nil), + protocol.EXPECT().ReadString(context.Background()).Return("test", nil), + protocol.EXPECT().ReadFieldEnd(context.Background()).Return(nil), + protocol.EXPECT().ReadFieldBegin(context.Background()).Return("_", thrift.TType(thrift.STOP), int16(1), nil), + protocol.EXPECT().ReadStructEnd(context.Background()).Return(nil), ) - err = testStruct.Read(protocol) + err = testStruct.Read(context.Background(), protocol) mockCtrl.Finish() if err != nil { t.Fatal("Expected read to succeed") diff --git a/lib/go/test/tests/thrifttest_driver.go b/lib/go/test/tests/thrifttest_driver.go index de54cbcaa93..4fc5baab451 100644 --- a/lib/go/test/tests/thrifttest_driver.go +++ b/lib/go/test/tests/thrifttest_driver.go @@ -213,24 +213,25 @@ func (p *ThriftTestDriver) Start() { 2: {thrifttest.Numberz_SIX: crazyEmpty}, } if r, err := client.TestInsanity(defaultCtx, crazy); !reflect.DeepEqual(r, insanity) || err != nil { - t.Fatal("TestInsanity failed") + t.Fatal("TestInsanity failed:", err) } if err := client.TestException(defaultCtx, "TException"); err == nil { - t.Fatal("TestException TException failed") + t.Fatal("TestException TException failed:", err) } - if err, ok := client.TestException(defaultCtx, "Xception").(*thrifttest.Xception); ok == false || err == nil { - t.Fatal("TestException Xception failed") - } else if err.ErrorCode != 1001 || err.Message != "Xception" { - t.Fatal("TestException Xception failed") + err := client.TestException(defaultCtx, "Xception") + if e, ok := err.(*thrifttest.Xception); ok == false || e == nil { + t.Fatal("TestException Xception failed:", err) + } else if e.ErrorCode != 1001 || e.Message != "Xception" { + t.Fatal("TestException Xception failed:", e) } if err := client.TestException(defaultCtx, "no Exception"); err != nil { - t.Fatal("TestException no Exception failed") + t.Fatal("TestException no Exception failed:", err) } if err := client.TestOneway(defaultCtx, 0); err != nil { - t.Fatal("TestOneway failed") + t.Fatal("TestOneway failed:", err) } } diff --git a/lib/go/test/tests/thrifttest_handler.go b/lib/go/test/tests/thrifttest_handler.go index 31b9ee23eca..7b115ec4053 100644 --- a/lib/go/test/tests/thrifttest_handler.go +++ b/lib/go/test/tests/thrifttest_handler.go @@ -179,7 +179,7 @@ func (p *ThriftTestHandler) TestException(ctx context.Context, arg string) (err x.Message = arg return x } else if arg == "TException" { - return thrift.TException(errors.New(arg)) + return thrift.WrapTException(errors.New(arg)) } else { return nil } diff --git a/lib/go/thrift/application_exception.go b/lib/go/thrift/application_exception.go index b9d7eedcdd7..32d5b0147a2 100644 --- a/lib/go/thrift/application_exception.go +++ b/lib/go/thrift/application_exception.go @@ -19,6 +19,10 @@ package thrift +import ( + "context" +) + const ( UNKNOWN_APPLICATION_EXCEPTION = 0 UNKNOWN_METHOD = 1 @@ -28,6 +32,9 @@ const ( MISSING_RESULT = 5 INTERNAL_ERROR = 6 PROTOCOL_ERROR = 7 + INVALID_TRANSFORM = 8 + INVALID_PROTOCOL = 9 + UNSUPPORTED_CLIENT_TYPE = 10 ) var defaultApplicationExceptionMessage = map[int32]string{ @@ -39,14 +46,17 @@ var defaultApplicationExceptionMessage = map[int32]string{ MISSING_RESULT: "missing result", INTERNAL_ERROR: "unknown internal error", PROTOCOL_ERROR: "unknown protocol error", + INVALID_TRANSFORM: "Invalid transform", + INVALID_PROTOCOL: "Invalid protocol", + UNSUPPORTED_CLIENT_TYPE: "Unsupported client type", } // Application level Thrift exception type TApplicationException interface { TException TypeId() int32 - Read(iprot TProtocol) error - Write(oprot TProtocol) error + Read(ctx context.Context, iprot TProtocol) error + Write(ctx context.Context, oprot TProtocol) error } type tApplicationException struct { @@ -54,6 +64,12 @@ type tApplicationException struct { type_ int32 } +var _ TApplicationException = (*tApplicationException)(nil) + +func (tApplicationException) TExceptionType() TExceptionType { + return TExceptionTypeApplication +} + func (e tApplicationException) Error() string { if e.message != "" { return e.message @@ -69,9 +85,9 @@ func (p *tApplicationException) TypeId() int32 { return p.type_ } -func (p *tApplicationException) Read(iprot TProtocol) error { +func (p *tApplicationException) Read(ctx context.Context, iprot TProtocol) error { // TODO: this should really be generated by the compiler - _, err := iprot.ReadStructBegin() + _, err := iprot.ReadStructBegin(ctx) if err != nil { return err } @@ -80,7 +96,7 @@ func (p *tApplicationException) Read(iprot TProtocol) error { type_ := int32(UNKNOWN_APPLICATION_EXCEPTION) for { - _, ttype, id, err := iprot.ReadFieldBegin() + _, ttype, id, err := iprot.ReadFieldBegin(ctx) if err != nil { return err } @@ -90,34 +106,34 @@ func (p *tApplicationException) Read(iprot TProtocol) error { switch id { case 1: if ttype == STRING { - if message, err = iprot.ReadString(); err != nil { + if message, err = iprot.ReadString(ctx); err != nil { return err } } else { - if err = SkipDefaultDepth(iprot, ttype); err != nil { + if err = SkipDefaultDepth(ctx, iprot, ttype); err != nil { return err } } case 2: if ttype == I32 { - if type_, err = iprot.ReadI32(); err != nil { + if type_, err = iprot.ReadI32(ctx); err != nil { return err } } else { - if err = SkipDefaultDepth(iprot, ttype); err != nil { + if err = SkipDefaultDepth(ctx, iprot, ttype); err != nil { return err } } default: - if err = SkipDefaultDepth(iprot, ttype); err != nil { + if err = SkipDefaultDepth(ctx, iprot, ttype); err != nil { return err } } - if err = iprot.ReadFieldEnd(); err != nil { + if err = iprot.ReadFieldEnd(ctx); err != nil { return err } } - if err := iprot.ReadStructEnd(); err != nil { + if err := iprot.ReadStructEnd(ctx); err != nil { return err } @@ -127,38 +143,38 @@ func (p *tApplicationException) Read(iprot TProtocol) error { return nil } -func (p *tApplicationException) Write(oprot TProtocol) (err error) { - err = oprot.WriteStructBegin("TApplicationException") +func (p *tApplicationException) Write(ctx context.Context, oprot TProtocol) (err error) { + err = oprot.WriteStructBegin(ctx, "TApplicationException") if len(p.Error()) > 0 { - err = oprot.WriteFieldBegin("message", STRING, 1) + err = oprot.WriteFieldBegin(ctx, "message", STRING, 1) if err != nil { return } - err = oprot.WriteString(p.Error()) + err = oprot.WriteString(ctx, p.Error()) if err != nil { return } - err = oprot.WriteFieldEnd() + err = oprot.WriteFieldEnd(ctx) if err != nil { return } } - err = oprot.WriteFieldBegin("type", I32, 2) + err = oprot.WriteFieldBegin(ctx, "type", I32, 2) if err != nil { return } - err = oprot.WriteI32(p.type_) + err = oprot.WriteI32(ctx, p.type_) if err != nil { return } - err = oprot.WriteFieldEnd() + err = oprot.WriteFieldEnd(ctx) if err != nil { return } - err = oprot.WriteFieldStop() + err = oprot.WriteFieldStop(ctx) if err != nil { return } - err = oprot.WriteStructEnd() + err = oprot.WriteStructEnd(ctx) return } diff --git a/lib/go/thrift/binary_protocol.go b/lib/go/thrift/binary_protocol.go index 1f90bf4351f..45c880d32f8 100644 --- a/lib/go/thrift/binary_protocol.go +++ b/lib/go/thrift/binary_protocol.go @@ -32,190 +32,220 @@ import ( type TBinaryProtocol struct { trans TRichTransport origTransport TTransport - reader io.Reader - writer io.Writer - strictRead bool - strictWrite bool + cfg *TConfiguration buffer [64]byte } type TBinaryProtocolFactory struct { - strictRead bool - strictWrite bool + cfg *TConfiguration } +// Deprecated: Use NewTBinaryProtocolConf instead. func NewTBinaryProtocolTransport(t TTransport) *TBinaryProtocol { - return NewTBinaryProtocol(t, false, true) + return NewTBinaryProtocolConf(t, &TConfiguration{ + noPropagation: true, + }) } +// Deprecated: Use NewTBinaryProtocolConf instead. func NewTBinaryProtocol(t TTransport, strictRead, strictWrite bool) *TBinaryProtocol { - p := &TBinaryProtocol{origTransport: t, strictRead: strictRead, strictWrite: strictWrite} + return NewTBinaryProtocolConf(t, &TConfiguration{ + TBinaryStrictRead: &strictRead, + TBinaryStrictWrite: &strictWrite, + + noPropagation: true, + }) +} + +func NewTBinaryProtocolConf(t TTransport, conf *TConfiguration) *TBinaryProtocol { + PropagateTConfiguration(t, conf) + p := &TBinaryProtocol{ + origTransport: t, + cfg: conf, + } if et, ok := t.(TRichTransport); ok { p.trans = et } else { p.trans = NewTRichTransport(t) } - p.reader = p.trans - p.writer = p.trans return p } +// Deprecated: Use NewTBinaryProtocolFactoryConf instead. func NewTBinaryProtocolFactoryDefault() *TBinaryProtocolFactory { - return NewTBinaryProtocolFactory(false, true) + return NewTBinaryProtocolFactoryConf(&TConfiguration{ + noPropagation: true, + }) } +// Deprecated: Use NewTBinaryProtocolFactoryConf instead. func NewTBinaryProtocolFactory(strictRead, strictWrite bool) *TBinaryProtocolFactory { - return &TBinaryProtocolFactory{strictRead: strictRead, strictWrite: strictWrite} + return NewTBinaryProtocolFactoryConf(&TConfiguration{ + TBinaryStrictRead: &strictRead, + TBinaryStrictWrite: &strictWrite, + + noPropagation: true, + }) +} + +func NewTBinaryProtocolFactoryConf(conf *TConfiguration) *TBinaryProtocolFactory { + return &TBinaryProtocolFactory{ + cfg: conf, + } } func (p *TBinaryProtocolFactory) GetProtocol(t TTransport) TProtocol { - return NewTBinaryProtocol(t, p.strictRead, p.strictWrite) + return NewTBinaryProtocolConf(t, p.cfg) +} + +func (p *TBinaryProtocolFactory) SetTConfiguration(conf *TConfiguration) { + p.cfg = conf } /** * Writing Methods */ -func (p *TBinaryProtocol) WriteMessageBegin(name string, typeId TMessageType, seqId int32) error { - if p.strictWrite { +func (p *TBinaryProtocol) WriteMessageBegin(ctx context.Context, name string, typeId TMessageType, seqId int32) error { + if p.cfg.GetTBinaryStrictWrite() { version := uint32(VERSION_1) | uint32(typeId) - e := p.WriteI32(int32(version)) + e := p.WriteI32(ctx, int32(version)) if e != nil { return e } - e = p.WriteString(name) + e = p.WriteString(ctx, name) if e != nil { return e } - e = p.WriteI32(seqId) + e = p.WriteI32(ctx, seqId) return e } else { - e := p.WriteString(name) + e := p.WriteString(ctx, name) if e != nil { return e } - e = p.WriteByte(int8(typeId)) + e = p.WriteByte(ctx, int8(typeId)) if e != nil { return e } - e = p.WriteI32(seqId) + e = p.WriteI32(ctx, seqId) return e } return nil } -func (p *TBinaryProtocol) WriteMessageEnd() error { +func (p *TBinaryProtocol) WriteMessageEnd(ctx context.Context) error { return nil } -func (p *TBinaryProtocol) WriteStructBegin(name string) error { +func (p *TBinaryProtocol) WriteStructBegin(ctx context.Context, name string) error { return nil } -func (p *TBinaryProtocol) WriteStructEnd() error { +func (p *TBinaryProtocol) WriteStructEnd(ctx context.Context) error { return nil } -func (p *TBinaryProtocol) WriteFieldBegin(name string, typeId TType, id int16) error { - e := p.WriteByte(int8(typeId)) +func (p *TBinaryProtocol) WriteFieldBegin(ctx context.Context, name string, typeId TType, id int16) error { + e := p.WriteByte(ctx, int8(typeId)) if e != nil { return e } - e = p.WriteI16(id) + e = p.WriteI16(ctx, id) return e } -func (p *TBinaryProtocol) WriteFieldEnd() error { +func (p *TBinaryProtocol) WriteFieldEnd(ctx context.Context) error { return nil } -func (p *TBinaryProtocol) WriteFieldStop() error { - e := p.WriteByte(STOP) +func (p *TBinaryProtocol) WriteFieldStop(ctx context.Context) error { + e := p.WriteByte(ctx, STOP) return e } -func (p *TBinaryProtocol) WriteMapBegin(keyType TType, valueType TType, size int) error { - e := p.WriteByte(int8(keyType)) +func (p *TBinaryProtocol) WriteMapBegin(ctx context.Context, keyType TType, valueType TType, size int) error { + e := p.WriteByte(ctx, int8(keyType)) if e != nil { return e } - e = p.WriteByte(int8(valueType)) + e = p.WriteByte(ctx, int8(valueType)) if e != nil { return e } - e = p.WriteI32(int32(size)) + e = p.WriteI32(ctx, int32(size)) return e } -func (p *TBinaryProtocol) WriteMapEnd() error { +func (p *TBinaryProtocol) WriteMapEnd(ctx context.Context) error { return nil } -func (p *TBinaryProtocol) WriteListBegin(elemType TType, size int) error { - e := p.WriteByte(int8(elemType)) +func (p *TBinaryProtocol) WriteListBegin(ctx context.Context, elemType TType, size int) error { + e := p.WriteByte(ctx, int8(elemType)) if e != nil { return e } - e = p.WriteI32(int32(size)) + e = p.WriteI32(ctx, int32(size)) return e } -func (p *TBinaryProtocol) WriteListEnd() error { +func (p *TBinaryProtocol) WriteListEnd(ctx context.Context) error { return nil } -func (p *TBinaryProtocol) WriteSetBegin(elemType TType, size int) error { - e := p.WriteByte(int8(elemType)) +func (p *TBinaryProtocol) WriteSetBegin(ctx context.Context, elemType TType, size int) error { + e := p.WriteByte(ctx, int8(elemType)) if e != nil { return e } - e = p.WriteI32(int32(size)) + e = p.WriteI32(ctx, int32(size)) return e } -func (p *TBinaryProtocol) WriteSetEnd() error { +func (p *TBinaryProtocol) WriteSetEnd(ctx context.Context) error { return nil } -func (p *TBinaryProtocol) WriteBool(value bool) error { +func (p *TBinaryProtocol) WriteBool(ctx context.Context, value bool) error { if value { - return p.WriteByte(1) + return p.WriteByte(ctx, 1) } - return p.WriteByte(0) + return p.WriteByte(ctx, 0) } -func (p *TBinaryProtocol) WriteByte(value int8) error { +func (p *TBinaryProtocol) WriteByte(ctx context.Context, value int8) error { e := p.trans.WriteByte(byte(value)) return NewTProtocolException(e) } -func (p *TBinaryProtocol) WriteI16(value int16) error { +func (p *TBinaryProtocol) WriteI16(ctx context.Context, value int16) error { v := p.buffer[0:2] binary.BigEndian.PutUint16(v, uint16(value)) - _, e := p.writer.Write(v) + _, e := p.trans.Write(v) return NewTProtocolException(e) } -func (p *TBinaryProtocol) WriteI32(value int32) error { +func (p *TBinaryProtocol) WriteI32(ctx context.Context, value int32) error { v := p.buffer[0:4] binary.BigEndian.PutUint32(v, uint32(value)) - _, e := p.writer.Write(v) + _, e := p.trans.Write(v) return NewTProtocolException(e) } -func (p *TBinaryProtocol) WriteI64(value int64) error { +func (p *TBinaryProtocol) WriteI64(ctx context.Context, value int64) error { v := p.buffer[0:8] binary.BigEndian.PutUint64(v, uint64(value)) - _, err := p.writer.Write(v) + _, err := p.trans.Write(v) return NewTProtocolException(err) } -func (p *TBinaryProtocol) WriteDouble(value float64) error { - return p.WriteI64(int64(math.Float64bits(value))) +func (p *TBinaryProtocol) WriteDouble(ctx context.Context, value float64) error { + return p.WriteI64(ctx, int64(math.Float64bits(value))) } -func (p *TBinaryProtocol) WriteString(value string) error { - e := p.WriteI32(int32(len(value))) +func (p *TBinaryProtocol) WriteString(ctx context.Context, value string) error { + e := p.WriteI32(ctx, int32(len(value))) if e != nil { return e } @@ -223,12 +253,12 @@ func (p *TBinaryProtocol) WriteString(value string) error { return NewTProtocolException(err) } -func (p *TBinaryProtocol) WriteBinary(value []byte) error { - e := p.WriteI32(int32(len(value))) +func (p *TBinaryProtocol) WriteBinary(ctx context.Context, value []byte) error { + e := p.WriteI32(ctx, int32(len(value))) if e != nil { return e } - _, err := p.writer.Write(value) + _, err := p.trans.Write(value) return NewTProtocolException(err) } @@ -236,8 +266,8 @@ func (p *TBinaryProtocol) WriteBinary(value []byte) error { * Reading methods */ -func (p *TBinaryProtocol) ReadMessageBegin() (name string, typeId TMessageType, seqId int32, err error) { - size, e := p.ReadI32() +func (p *TBinaryProtocol) ReadMessageBegin(ctx context.Context) (name string, typeId TMessageType, seqId int32, err error) { + size, e := p.ReadI32(ctx) if e != nil { return "", typeId, 0, NewTProtocolException(e) } @@ -247,79 +277,79 @@ func (p *TBinaryProtocol) ReadMessageBegin() (name string, typeId TMessageType, if version != VERSION_1 { return name, typeId, seqId, NewTProtocolExceptionWithType(BAD_VERSION, fmt.Errorf("Bad version in ReadMessageBegin")) } - name, e = p.ReadString() + name, e = p.ReadString(ctx) if e != nil { return name, typeId, seqId, NewTProtocolException(e) } - seqId, e = p.ReadI32() + seqId, e = p.ReadI32(ctx) if e != nil { return name, typeId, seqId, NewTProtocolException(e) } return name, typeId, seqId, nil } - if p.strictRead { + if p.cfg.GetTBinaryStrictRead() { return name, typeId, seqId, NewTProtocolExceptionWithType(BAD_VERSION, fmt.Errorf("Missing version in ReadMessageBegin")) } name, e2 := p.readStringBody(size) if e2 != nil { return name, typeId, seqId, e2 } - b, e3 := p.ReadByte() + b, e3 := p.ReadByte(ctx) if e3 != nil { return name, typeId, seqId, e3 } typeId = TMessageType(b) - seqId, e4 := p.ReadI32() + seqId, e4 := p.ReadI32(ctx) if e4 != nil { return name, typeId, seqId, e4 } return name, typeId, seqId, nil } -func (p *TBinaryProtocol) ReadMessageEnd() error { +func (p *TBinaryProtocol) ReadMessageEnd(ctx context.Context) error { return nil } -func (p *TBinaryProtocol) ReadStructBegin() (name string, err error) { +func (p *TBinaryProtocol) ReadStructBegin(ctx context.Context) (name string, err error) { return } -func (p *TBinaryProtocol) ReadStructEnd() error { +func (p *TBinaryProtocol) ReadStructEnd(ctx context.Context) error { return nil } -func (p *TBinaryProtocol) ReadFieldBegin() (name string, typeId TType, seqId int16, err error) { - t, err := p.ReadByte() +func (p *TBinaryProtocol) ReadFieldBegin(ctx context.Context) (name string, typeId TType, seqId int16, err error) { + t, err := p.ReadByte(ctx) typeId = TType(t) if err != nil { return name, typeId, seqId, err } if t != STOP { - seqId, err = p.ReadI16() + seqId, err = p.ReadI16(ctx) } return name, typeId, seqId, err } -func (p *TBinaryProtocol) ReadFieldEnd() error { +func (p *TBinaryProtocol) ReadFieldEnd(ctx context.Context) error { return nil } var invalidDataLength = NewTProtocolExceptionWithType(INVALID_DATA, errors.New("Invalid data length")) -func (p *TBinaryProtocol) ReadMapBegin() (kType, vType TType, size int, err error) { - k, e := p.ReadByte() +func (p *TBinaryProtocol) ReadMapBegin(ctx context.Context) (kType, vType TType, size int, err error) { + k, e := p.ReadByte(ctx) if e != nil { err = NewTProtocolException(e) return } kType = TType(k) - v, e := p.ReadByte() + v, e := p.ReadByte(ctx) if e != nil { err = NewTProtocolException(e) return } vType = TType(v) - size32, e := p.ReadI32() + size32, e := p.ReadI32(ctx) if e != nil { err = NewTProtocolException(e) return @@ -332,18 +362,18 @@ func (p *TBinaryProtocol) ReadMapBegin() (kType, vType TType, size int, err erro return kType, vType, size, nil } -func (p *TBinaryProtocol) ReadMapEnd() error { +func (p *TBinaryProtocol) ReadMapEnd(ctx context.Context) error { return nil } -func (p *TBinaryProtocol) ReadListBegin() (elemType TType, size int, err error) { - b, e := p.ReadByte() +func (p *TBinaryProtocol) ReadListBegin(ctx context.Context) (elemType TType, size int, err error) { + b, e := p.ReadByte(ctx) if e != nil { err = NewTProtocolException(e) return } elemType = TType(b) - size32, e := p.ReadI32() + size32, e := p.ReadI32(ctx) if e != nil { err = NewTProtocolException(e) return @@ -357,18 +387,18 @@ func (p *TBinaryProtocol) ReadListBegin() (elemType TType, size int, err error) return } -func (p *TBinaryProtocol) ReadListEnd() error { +func (p *TBinaryProtocol) ReadListEnd(ctx context.Context) error { return nil } -func (p *TBinaryProtocol) ReadSetBegin() (elemType TType, size int, err error) { - b, e := p.ReadByte() +func (p *TBinaryProtocol) ReadSetBegin(ctx context.Context) (elemType TType, size int, err error) { + b, e := p.ReadByte(ctx) if e != nil { err = NewTProtocolException(e) return } elemType = TType(b) - size32, e := p.ReadI32() + size32, e := p.ReadI32(ctx) if e != nil { err = NewTProtocolException(e) return @@ -381,12 +411,12 @@ func (p *TBinaryProtocol) ReadSetBegin() (elemType TType, size int, err error) { return elemType, size, nil } -func (p *TBinaryProtocol) ReadSetEnd() error { +func (p *TBinaryProtocol) ReadSetEnd(ctx context.Context) error { return nil } -func (p *TBinaryProtocol) ReadBool() (bool, error) { - b, e := p.ReadByte() +func (p *TBinaryProtocol) ReadBool(ctx context.Context) (bool, error) { + b, e := p.ReadByte(ctx) v := true if b != 1 { v = false @@ -394,64 +424,75 @@ func (p *TBinaryProtocol) ReadBool() (bool, error) { return v, e } -func (p *TBinaryProtocol) ReadByte() (int8, error) { +func (p *TBinaryProtocol) ReadByte(ctx context.Context) (int8, error) { v, err := p.trans.ReadByte() return int8(v), err } -func (p *TBinaryProtocol) ReadI16() (value int16, err error) { +func (p *TBinaryProtocol) ReadI16(ctx context.Context) (value int16, err error) { buf := p.buffer[0:2] - err = p.readAll(buf) + err = p.readAll(ctx, buf) value = int16(binary.BigEndian.Uint16(buf)) return value, err } -func (p *TBinaryProtocol) ReadI32() (value int32, err error) { +func (p *TBinaryProtocol) ReadI32(ctx context.Context) (value int32, err error) { buf := p.buffer[0:4] - err = p.readAll(buf) + err = p.readAll(ctx, buf) value = int32(binary.BigEndian.Uint32(buf)) return value, err } -func (p *TBinaryProtocol) ReadI64() (value int64, err error) { +func (p *TBinaryProtocol) ReadI64(ctx context.Context) (value int64, err error) { buf := p.buffer[0:8] - err = p.readAll(buf) + err = p.readAll(ctx, buf) value = int64(binary.BigEndian.Uint64(buf)) return value, err } -func (p *TBinaryProtocol) ReadDouble() (value float64, err error) { +func (p *TBinaryProtocol) ReadDouble(ctx context.Context) (value float64, err error) { buf := p.buffer[0:8] - err = p.readAll(buf) + err = p.readAll(ctx, buf) value = math.Float64frombits(binary.BigEndian.Uint64(buf)) return value, err } -func (p *TBinaryProtocol) ReadString() (value string, err error) { - size, e := p.ReadI32() +func (p *TBinaryProtocol) ReadString(ctx context.Context) (value string, err error) { + size, e := p.ReadI32(ctx) if e != nil { return "", e } + err = checkSizeForProtocol(size, p.cfg) + if err != nil { + return + } if size < 0 { err = invalidDataLength return } + if size == 0 { + return "", nil + } + if size < int32(len(p.buffer)) { + // Avoid allocation on small reads + buf := p.buffer[:size] + read, e := io.ReadFull(p.trans, buf) + return string(buf[:read]), NewTProtocolException(e) + } return p.readStringBody(size) } -func (p *TBinaryProtocol) ReadBinary() ([]byte, error) { - size, e := p.ReadI32() +func (p *TBinaryProtocol) ReadBinary(ctx context.Context) ([]byte, error) { + size, e := p.ReadI32(ctx) if e != nil { return nil, e } - if size < 0 { - return nil, invalidDataLength + if err := checkSizeForProtocol(size, p.cfg); err != nil { + return nil, err } - isize := int(size) - buf := make([]byte, isize) - _, err := io.ReadFull(p.trans, buf) + buf, err := safeReadBytes(size, p.trans) return buf, NewTProtocolException(err) } @@ -459,51 +500,56 @@ func (p *TBinaryProtocol) Flush(ctx context.Context) (err error) { return NewTProtocolException(p.trans.Flush(ctx)) } -func (p *TBinaryProtocol) Skip(fieldType TType) (err error) { - return SkipDefaultDepth(p, fieldType) +func (p *TBinaryProtocol) Skip(ctx context.Context, fieldType TType) (err error) { + return SkipDefaultDepth(ctx, p, fieldType) } func (p *TBinaryProtocol) Transport() TTransport { return p.origTransport } -func (p *TBinaryProtocol) readAll(buf []byte) error { - _, err := io.ReadFull(p.reader, buf) +func (p *TBinaryProtocol) readAll(ctx context.Context, buf []byte) (err error) { + var read int + _, deadlineSet := ctx.Deadline() + for { + read, err = io.ReadFull(p.trans, buf) + if deadlineSet && read == 0 && isTimeoutError(err) && ctx.Err() == nil { + // This is I/O timeout without anything read, + // and we still have time left, keep retrying. + continue + } + // For anything else, don't retry + break + } return NewTProtocolException(err) } -const readLimit = 32768 - func (p *TBinaryProtocol) readStringBody(size int32) (value string, err error) { - if size < 0 { - return "", nil - } + buf, err := safeReadBytes(size, p.trans) + return string(buf), NewTProtocolException(err) +} - var ( - buf bytes.Buffer - e error - b []byte - ) +func (p *TBinaryProtocol) SetTConfiguration(conf *TConfiguration) { + PropagateTConfiguration(p.trans, conf) + PropagateTConfiguration(p.origTransport, conf) + p.cfg = conf +} - switch { - case int(size) <= len(p.buffer): - b = p.buffer[:size] // avoids allocation for small reads - case int(size) < readLimit: - b = make([]byte, size) - default: - b = make([]byte, readLimit) - } +var ( + _ TConfigurationSetter = (*TBinaryProtocolFactory)(nil) + _ TConfigurationSetter = (*TBinaryProtocol)(nil) +) - for size > 0 { - _, e = io.ReadFull(p.trans, b) - buf.Write(b) - if e != nil { - break - } - size -= readLimit - if size < readLimit && size > 0 { - b = b[:size] - } +// This function is shared between TBinaryProtocol and TCompactProtocol. +// +// It tries to read size bytes from trans, in a way that prevents large +// allocations when size is insanely large (mostly caused by malformed message). +func safeReadBytes(size int32, trans io.Reader) ([]byte, error) { + if size < 0 { + return nil, nil } - return buf.String(), NewTProtocolException(e) + + buf := new(bytes.Buffer) + _, err := io.CopyN(buf, trans, int64(size)) + return buf.Bytes(), err } diff --git a/lib/go/thrift/binary_protocol_test.go b/lib/go/thrift/binary_protocol_test.go index 0462cc79dee..88bfd26b7ea 100644 --- a/lib/go/thrift/binary_protocol_test.go +++ b/lib/go/thrift/binary_protocol_test.go @@ -20,9 +20,162 @@ package thrift import ( + "bytes" + "math" + "strings" "testing" ) func TestReadWriteBinaryProtocol(t *testing.T) { ReadWriteProtocolTest(t, NewTBinaryProtocolFactoryDefault()) } + +const ( + safeReadBytesSource = ` +Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer sit amet +tincidunt nibh. Phasellus vel convallis libero, sit amet posuere quam. Nullam +blandit velit at nibh fringilla, sed egestas erat dapibus. Sed hendrerit +tincidunt accumsan. Curabitur consectetur bibendum dui nec hendrerit. Fusce quis +turpis nec magna efficitur volutpat a ut nibh. Vestibulum odio risus, tristique +a nisi et, congue mattis mi. Vivamus a nunc justo. Mauris molestie sagittis +magna, hendrerit auctor lectus egestas non. Phasellus pretium, odio sit amet +bibendum feugiat, velit nunc luctus erat, ac bibendum mi dui molestie nulla. +Nullam fermentum magna eu elit vehicula tincidunt. Etiam ornare laoreet +dignissim. Ut sed nunc ac neque vulputate fermentum. Morbi volutpat dapibus +magna, at porttitor quam facilisis a. Donec eget fermentum risus. Aliquam erat +volutpat. + +Phasellus molestie id ante vel iaculis. Fusce eget quam nec quam viverra laoreet +vitae a dui. Mauris blandit blandit dui, iaculis interdum diam mollis at. Morbi +vel sem et. +` + safeReadBytesSourceLen = len(safeReadBytesSource) +) + +func TestSafeReadBytes(t *testing.T) { + srcData := []byte(safeReadBytesSource) + + for _, c := range []struct { + label string + askedSize int32 + dataSize int + }{ + { + label: "normal", + askedSize: 100, + dataSize: 100, + }, + { + label: "max-askedSize", + askedSize: math.MaxInt32, + dataSize: safeReadBytesSourceLen, + }, + } { + t.Run(c.label, func(t *testing.T) { + data := bytes.NewReader(srcData[:c.dataSize]) + buf, err := safeReadBytes(c.askedSize, data) + if len(buf) != c.dataSize { + t.Errorf( + "Expected to read %d bytes, got %d", + c.dataSize, + len(buf), + ) + } + if !strings.HasPrefix(safeReadBytesSource, string(buf)) { + t.Errorf("Unexpected read data: %q", buf) + } + if int32(c.dataSize) < c.askedSize { + // We expect error in this case + if err == nil { + t.Errorf( + "Expected error when dataSize %d < askedSize %d, got nil", + c.dataSize, + c.askedSize, + ) + } + } else { + // We expect no error in this case + if err != nil { + t.Errorf( + "Expected no error when dataSize %d >= askedSize %d, got: %v", + c.dataSize, + c.askedSize, + err, + ) + } + } + }) + } +} + +func generateSafeReadBytesBenchmark(askedSize int32, dataSize int) func(b *testing.B) { + return func(b *testing.B) { + data := make([]byte, dataSize) + b.ResetTimer() + for i := 0; i < b.N; i++ { + safeReadBytes(askedSize, bytes.NewReader(data)) + } + } +} + +func TestSafeReadBytesAlloc(t *testing.T) { + if testing.Short() { + // NOTE: Since this test runs a benchmark test, it takes at + // least 1 second. + // + // In general we try to avoid unit tests taking that long to run, + // but it's to verify a security issue so we made an exception + // here: + // https://issues.apache.org/jira/browse/THRIFT-5322 + t.Skip("skipping test in short mode.") + } + + const ( + askedSize = int32(math.MaxInt32) + dataSize = 4096 + ) + + // The purpose of this test is that in the case a string header says + // that it has a string askedSize bytes long, the implementation should + // not just allocate askedSize bytes upfront. So when there're actually + // not enough data to be read (dataSize), the actual allocated bytes + // should be somewhere between dataSize and askedSize. + // + // Different approachs could have different memory overheads, so this + // target is arbitrary in nature. But when dataSize is small enough + // compare to askedSize, half the askedSize is a good and safe target. + const target = int64(askedSize) / 2 + + bm := testing.Benchmark(generateSafeReadBytesBenchmark(askedSize, dataSize)) + actual := bm.AllocedBytesPerOp() + if actual > target { + t.Errorf( + "Expected allocated bytes per op to be <= %d, got %d", + target, + actual, + ) + } else { + t.Logf("Allocated bytes: %d B/op", actual) + } +} + +func BenchmarkSafeReadBytes(b *testing.B) { + for _, c := range []struct { + label string + askedSize int32 + dataSize int + }{ + { + label: "normal", + askedSize: 100, + dataSize: 100, + }, + { + label: "max-askedSize", + askedSize: math.MaxInt32, + dataSize: 4096, + }, + } { + b.Run(c.label, generateSafeReadBytesBenchmark(c.askedSize, c.dataSize)) + } +} diff --git a/lib/go/thrift/buffered_transport.go b/lib/go/thrift/buffered_transport.go index 96702061b2f..aa551b4ab37 100644 --- a/lib/go/thrift/buffered_transport.go +++ b/lib/go/thrift/buffered_transport.go @@ -90,3 +90,10 @@ func (p *TBufferedTransport) Flush(ctx context.Context) error { func (p *TBufferedTransport) RemainingBytes() (num_bytes uint64) { return p.tp.RemainingBytes() } + +// SetTConfiguration implements TConfigurationSetter for propagation. +func (p *TBufferedTransport) SetTConfiguration(conf *TConfiguration) { + PropagateTConfiguration(p.tp, conf) +} + +var _ TConfigurationSetter = (*TBufferedTransport)(nil) diff --git a/lib/go/thrift/client.go b/lib/go/thrift/client.go index 28791ccd0c0..ea2c01fdadb 100644 --- a/lib/go/thrift/client.go +++ b/lib/go/thrift/client.go @@ -5,8 +5,15 @@ import ( "fmt" ) +// ResponseMeta represents the metadata attached to the response. +type ResponseMeta struct { + // The headers in the response, if any. + // If the underlying transport/protocol is not THeader, this will always be nil. + Headers THeaderMap +} + type TClient interface { - Call(ctx context.Context, method string, args, result TStruct) error + Call(ctx context.Context, method string, args, result TStruct) (ResponseMeta, error) } type TStandardClient struct { @@ -24,20 +31,30 @@ func NewTStandardClient(inputProtocol, outputProtocol TProtocol) *TStandardClien } func (p *TStandardClient) Send(ctx context.Context, oprot TProtocol, seqId int32, method string, args TStruct) error { - if err := oprot.WriteMessageBegin(method, CALL, seqId); err != nil { + // Set headers from context object on THeaderProtocol + if headerProt, ok := oprot.(*THeaderProtocol); ok { + headerProt.ClearWriteHeaders() + for _, key := range GetWriteHeaderList(ctx) { + if value, ok := GetHeader(ctx, key); ok { + headerProt.SetWriteHeader(key, value) + } + } + } + + if err := oprot.WriteMessageBegin(ctx, method, CALL, seqId); err != nil { return err } - if err := args.Write(oprot); err != nil { + if err := args.Write(ctx, oprot); err != nil { return err } - if err := oprot.WriteMessageEnd(); err != nil { + if err := oprot.WriteMessageEnd(ctx); err != nil { return err } return oprot.Flush(ctx) } -func (p *TStandardClient) Recv(iprot TProtocol, seqId int32, method string, result TStruct) error { - rMethod, rTypeId, rSeqId, err := iprot.ReadMessageBegin() +func (p *TStandardClient) Recv(ctx context.Context, iprot TProtocol, seqId int32, method string, result TStruct) error { + rMethod, rTypeId, rSeqId, err := iprot.ReadMessageBegin(ctx) if err != nil { return err } @@ -48,11 +65,11 @@ func (p *TStandardClient) Recv(iprot TProtocol, seqId int32, method string, resu return NewTApplicationException(BAD_SEQUENCE_ID, fmt.Sprintf("%s: out of order sequence response", method)) } else if rTypeId == EXCEPTION { var exception tApplicationException - if err := exception.Read(iprot); err != nil { + if err := exception.Read(ctx, iprot); err != nil { return err } - if err := iprot.ReadMessageEnd(); err != nil { + if err := iprot.ReadMessageEnd(ctx); err != nil { return err } @@ -61,25 +78,32 @@ func (p *TStandardClient) Recv(iprot TProtocol, seqId int32, method string, resu return NewTApplicationException(INVALID_MESSAGE_TYPE_EXCEPTION, fmt.Sprintf("%s: invalid message type", method)) } - if err := result.Read(iprot); err != nil { + if err := result.Read(ctx, iprot); err != nil { return err } - return iprot.ReadMessageEnd() + return iprot.ReadMessageEnd(ctx) } -func (p *TStandardClient) Call(ctx context.Context, method string, args, result TStruct) error { +func (p *TStandardClient) Call(ctx context.Context, method string, args, result TStruct) (ResponseMeta, error) { p.seqId++ seqId := p.seqId if err := p.Send(ctx, p.oprot, seqId, method, args); err != nil { - return err + return ResponseMeta{}, err } // method is oneway if result == nil { - return nil + return ResponseMeta{}, nil } - return p.Recv(p.iprot, seqId, method, result) + err := p.Recv(ctx, p.iprot, seqId, method, result) + var headers THeaderMap + if hp, ok := p.iprot.(*THeaderProtocol); ok { + headers = hp.transport.readHeaders + } + return ResponseMeta{ + Headers: headers, + }, err } diff --git a/lib/go/thrift/common_test.go b/lib/go/thrift/common_test.go index 93597ff8a2a..95d4e21309c 100644 --- a/lib/go/thrift/common_test.go +++ b/lib/go/thrift/common_test.go @@ -19,7 +19,10 @@ package thrift -import "context" +import ( + "context" + "fmt" +) type mockProcessor struct { ProcessFunc func(in, out TProtocol) (bool, TException) @@ -28,3 +31,76 @@ type mockProcessor struct { func (m *mockProcessor) Process(ctx context.Context, in, out TProtocol) (bool, TException) { return m.ProcessFunc(in, out) } + +func (m *mockProcessor) ProcessorMap() map[string]TProcessorFunction { + return map[string]TProcessorFunction{ + "mock": WrappedTProcessorFunction{ + Wrapped: func(ctx context.Context, seqId int32, in, out TProtocol) (bool, TException) { + return m.ProcessFunc(in, out) + }, + }, + } +} + +func (m *mockProcessor) AddToProcessorMap(name string, processorFunc TProcessorFunction) {} + +type mockWrappedProcessorContextKey int + +const ( + processorName mockWrappedProcessorContextKey = iota +) + +// setMockWrappableProcessorName sets the "name" of the TProcessorFunction to +// call on a mockWrappableProcessor when calling Process. +// +// In a normal TProcessor, the request name is read from the request itself +// which happens in TProcessor.Process, so it is not passed into the call to +// Process itself, to get around this in testing, mockWrappableProcessor calls +// getMockWrappableProcessorName to get the name to use from the context +// object. +func setMockWrappableProcessorName(ctx context.Context, name string) context.Context { + return context.WithValue(ctx, processorName, name) +} + +// getMockWrappableProcessorName gets the "name" of the TProcessorFunction to +// call on a mockWrappableProcessor when calling Process. +func getMockWrappableProcessorName(ctx context.Context) (string, bool) { + val, ok := ctx.Value(processorName).(string) + return val, ok +} + +// mockWrappableProcessor can be used to create a mock object that fufills the +// TProcessor interface in testing. +type mockWrappableProcessor struct { + ProcessorFuncs map[string]TProcessorFunction +} + +// Process calls the TProcessorFunction assigned to the "name" set on the +// context object by setMockWrappableProcessorName. +// +// If no name is set on the context or there is no TProcessorFunction mapped to +// that name, the call will panic. +func (p *mockWrappableProcessor) Process(ctx context.Context, in, out TProtocol) (bool, TException) { + name, ok := getMockWrappableProcessorName(ctx) + if !ok { + panic("MockWrappableProcessorName not set on context") + } + processor, ok := p.ProcessorMap()[name] + if !ok { + panic(fmt.Sprintf("No processor set for name %q", name)) + } + return processor.Process(ctx, 0, in, out) +} + +func (p *mockWrappableProcessor) ProcessorMap() map[string]TProcessorFunction { + return p.ProcessorFuncs +} + +func (p *mockWrappableProcessor) AddToProcessorMap(name string, processorFunc TProcessorFunction) { + p.ProcessorFuncs[name] = processorFunc +} + +var ( + _ TProcessor = (*mockProcessor)(nil) + _ TProcessor = (*mockWrappableProcessor)(nil) +) diff --git a/lib/go/thrift/compact_protocol.go b/lib/go/thrift/compact_protocol.go index 1900d50c3b1..a49225dabfb 100644 --- a/lib/go/thrift/compact_protocol.go +++ b/lib/go/thrift/compact_protocol.go @@ -22,6 +22,7 @@ package thrift import ( "context" "encoding/binary" + "errors" "fmt" "io" "math" @@ -74,20 +75,37 @@ func init() { } } -type TCompactProtocolFactory struct{} +type TCompactProtocolFactory struct { + cfg *TConfiguration +} +// Deprecated: Use NewTCompactProtocolFactoryConf instead. func NewTCompactProtocolFactory() *TCompactProtocolFactory { - return &TCompactProtocolFactory{} + return NewTCompactProtocolFactoryConf(&TConfiguration{ + noPropagation: true, + }) +} + +func NewTCompactProtocolFactoryConf(conf *TConfiguration) *TCompactProtocolFactory { + return &TCompactProtocolFactory{ + cfg: conf, + } } func (p *TCompactProtocolFactory) GetProtocol(trans TTransport) TProtocol { - return NewTCompactProtocol(trans) + return NewTCompactProtocolConf(trans, p.cfg) +} + +func (p *TCompactProtocolFactory) SetTConfiguration(conf *TConfiguration) { + p.cfg = conf } type TCompactProtocol struct { trans TRichTransport origTransport TTransport + cfg *TConfiguration + // Used to keep track of the last field for the current and previous structs, // so we can do the delta stuff. lastField []int @@ -106,9 +124,19 @@ type TCompactProtocol struct { buffer [64]byte } -// Create a TCompactProtocol given a TTransport +// Deprecated: Use NewTCompactProtocolConf instead. func NewTCompactProtocol(trans TTransport) *TCompactProtocol { - p := &TCompactProtocol{origTransport: trans, lastField: []int{}} + return NewTCompactProtocolConf(trans, &TConfiguration{ + noPropagation: true, + }) +} + +func NewTCompactProtocolConf(trans TTransport, conf *TConfiguration) *TCompactProtocol { + PropagateTConfiguration(trans, conf) + p := &TCompactProtocol{ + origTransport: trans, + cfg: conf, + } if et, ok := trans.(TRichTransport); ok { p.trans = et } else { @@ -116,7 +144,6 @@ func NewTCompactProtocol(trans TTransport) *TCompactProtocol { } return p - } // @@ -125,7 +152,7 @@ func NewTCompactProtocol(trans TTransport) *TCompactProtocol { // Write a message header to the wire. Compact Protocol messages contain the // protocol version so we can migrate forwards in the future if need be. -func (p *TCompactProtocol) WriteMessageBegin(name string, typeId TMessageType, seqid int32) error { +func (p *TCompactProtocol) WriteMessageBegin(ctx context.Context, name string, typeId TMessageType, seqid int32) error { err := p.writeByteDirect(COMPACT_PROTOCOL_ID) if err != nil { return NewTProtocolException(err) @@ -138,17 +165,17 @@ func (p *TCompactProtocol) WriteMessageBegin(name string, typeId TMessageType, s if err != nil { return NewTProtocolException(err) } - e := p.WriteString(name) + e := p.WriteString(ctx, name) return e } -func (p *TCompactProtocol) WriteMessageEnd() error { return nil } +func (p *TCompactProtocol) WriteMessageEnd(ctx context.Context) error { return nil } // Write a struct begin. This doesn't actually put anything on the wire. We // use it as an opportunity to put special placeholder markers on the field // stack so we can get the field id deltas correct. -func (p *TCompactProtocol) WriteStructBegin(name string) error { +func (p *TCompactProtocol) WriteStructBegin(ctx context.Context, name string) error { p.lastField = append(p.lastField, p.lastFieldId) p.lastFieldId = 0 return nil @@ -157,26 +184,29 @@ func (p *TCompactProtocol) WriteStructBegin(name string) error { // Write a struct end. This doesn't actually put anything on the wire. We use // this as an opportunity to pop the last field from the current struct off // of the field stack. -func (p *TCompactProtocol) WriteStructEnd() error { +func (p *TCompactProtocol) WriteStructEnd(ctx context.Context) error { + if len(p.lastField) <= 0 { + return NewTProtocolExceptionWithType(INVALID_DATA, errors.New("WriteStructEnd called without matching WriteStructBegin call before")) + } p.lastFieldId = p.lastField[len(p.lastField)-1] p.lastField = p.lastField[:len(p.lastField)-1] return nil } -func (p *TCompactProtocol) WriteFieldBegin(name string, typeId TType, id int16) error { +func (p *TCompactProtocol) WriteFieldBegin(ctx context.Context, name string, typeId TType, id int16) error { if typeId == BOOL { // we want to possibly include the value, so we'll wait. p.booleanFieldName, p.booleanFieldId, p.booleanFieldPending = name, id, true return nil } - _, err := p.writeFieldBeginInternal(name, typeId, id, 0xFF) + _, err := p.writeFieldBeginInternal(ctx, name, typeId, id, 0xFF) return NewTProtocolException(err) } // The workhorse of writeFieldBegin. It has the option of doing a // 'type override' of the type header. This is used specifically in the // boolean field case. -func (p *TCompactProtocol) writeFieldBeginInternal(name string, typeId TType, id int16, typeOverride byte) (int, error) { +func (p *TCompactProtocol) writeFieldBeginInternal(ctx context.Context, name string, typeId TType, id int16, typeOverride byte) (int, error) { // short lastField = lastField_.pop(); // if there's a type override, use that. @@ -201,7 +231,7 @@ func (p *TCompactProtocol) writeFieldBeginInternal(name string, typeId TType, id if err != nil { return 0, err } - err = p.WriteI16(id) + err = p.WriteI16(ctx, id) written = 1 + 2 if err != nil { return 0, err @@ -209,18 +239,17 @@ func (p *TCompactProtocol) writeFieldBeginInternal(name string, typeId TType, id } p.lastFieldId = fieldId - // p.lastField.Push(field.id); return written, nil } -func (p *TCompactProtocol) WriteFieldEnd() error { return nil } +func (p *TCompactProtocol) WriteFieldEnd(ctx context.Context) error { return nil } -func (p *TCompactProtocol) WriteFieldStop() error { +func (p *TCompactProtocol) WriteFieldStop(ctx context.Context) error { err := p.writeByteDirect(STOP) return NewTProtocolException(err) } -func (p *TCompactProtocol) WriteMapBegin(keyType TType, valueType TType, size int) error { +func (p *TCompactProtocol) WriteMapBegin(ctx context.Context, keyType TType, valueType TType, size int) error { if size == 0 { err := p.writeByteDirect(0) return NewTProtocolException(err) @@ -233,32 +262,32 @@ func (p *TCompactProtocol) WriteMapBegin(keyType TType, valueType TType, size in return NewTProtocolException(err) } -func (p *TCompactProtocol) WriteMapEnd() error { return nil } +func (p *TCompactProtocol) WriteMapEnd(ctx context.Context) error { return nil } // Write a list header. -func (p *TCompactProtocol) WriteListBegin(elemType TType, size int) error { +func (p *TCompactProtocol) WriteListBegin(ctx context.Context, elemType TType, size int) error { _, err := p.writeCollectionBegin(elemType, size) return NewTProtocolException(err) } -func (p *TCompactProtocol) WriteListEnd() error { return nil } +func (p *TCompactProtocol) WriteListEnd(ctx context.Context) error { return nil } // Write a set header. -func (p *TCompactProtocol) WriteSetBegin(elemType TType, size int) error { +func (p *TCompactProtocol) WriteSetBegin(ctx context.Context, elemType TType, size int) error { _, err := p.writeCollectionBegin(elemType, size) return NewTProtocolException(err) } -func (p *TCompactProtocol) WriteSetEnd() error { return nil } +func (p *TCompactProtocol) WriteSetEnd(ctx context.Context) error { return nil } -func (p *TCompactProtocol) WriteBool(value bool) error { +func (p *TCompactProtocol) WriteBool(ctx context.Context, value bool) error { v := byte(COMPACT_BOOLEAN_FALSE) if value { v = byte(COMPACT_BOOLEAN_TRUE) } if p.booleanFieldPending { // we haven't written the field header yet - _, err := p.writeFieldBeginInternal(p.booleanFieldName, BOOL, p.booleanFieldId, v) + _, err := p.writeFieldBeginInternal(ctx, p.booleanFieldName, BOOL, p.booleanFieldId, v) p.booleanFieldPending = false return NewTProtocolException(err) } @@ -268,31 +297,31 @@ func (p *TCompactProtocol) WriteBool(value bool) error { } // Write a byte. Nothing to see here! -func (p *TCompactProtocol) WriteByte(value int8) error { +func (p *TCompactProtocol) WriteByte(ctx context.Context, value int8) error { err := p.writeByteDirect(byte(value)) return NewTProtocolException(err) } // Write an I16 as a zigzag varint. -func (p *TCompactProtocol) WriteI16(value int16) error { +func (p *TCompactProtocol) WriteI16(ctx context.Context, value int16) error { _, err := p.writeVarint32(p.int32ToZigzag(int32(value))) return NewTProtocolException(err) } // Write an i32 as a zigzag varint. -func (p *TCompactProtocol) WriteI32(value int32) error { +func (p *TCompactProtocol) WriteI32(ctx context.Context, value int32) error { _, err := p.writeVarint32(p.int32ToZigzag(value)) return NewTProtocolException(err) } // Write an i64 as a zigzag varint. -func (p *TCompactProtocol) WriteI64(value int64) error { +func (p *TCompactProtocol) WriteI64(ctx context.Context, value int64) error { _, err := p.writeVarint64(p.int64ToZigzag(value)) return NewTProtocolException(err) } // Write a double to the wire as 8 bytes. -func (p *TCompactProtocol) WriteDouble(value float64) error { +func (p *TCompactProtocol) WriteDouble(ctx context.Context, value float64) error { buf := p.buffer[0:8] binary.LittleEndian.PutUint64(buf, math.Float64bits(value)) _, err := p.trans.Write(buf) @@ -300,7 +329,7 @@ func (p *TCompactProtocol) WriteDouble(value float64) error { } // Write a string to the wire with a varint size preceding. -func (p *TCompactProtocol) WriteString(value string) error { +func (p *TCompactProtocol) WriteString(ctx context.Context, value string) error { _, e := p.writeVarint32(int32(len(value))) if e != nil { return NewTProtocolException(e) @@ -312,7 +341,7 @@ func (p *TCompactProtocol) WriteString(value string) error { } // Write a byte array, using a varint for the size. -func (p *TCompactProtocol) WriteBinary(bin []byte) error { +func (p *TCompactProtocol) WriteBinary(ctx context.Context, bin []byte) error { _, e := p.writeVarint32(int32(len(bin))) if e != nil { return NewTProtocolException(e) @@ -329,9 +358,20 @@ func (p *TCompactProtocol) WriteBinary(bin []byte) error { // // Read a message header. -func (p *TCompactProtocol) ReadMessageBegin() (name string, typeId TMessageType, seqId int32, err error) { +func (p *TCompactProtocol) ReadMessageBegin(ctx context.Context) (name string, typeId TMessageType, seqId int32, err error) { + var protocolId byte - protocolId, err := p.readByteDirect() + _, deadlineSet := ctx.Deadline() + for { + protocolId, err = p.readByteDirect() + if deadlineSet && isTimeoutError(err) && ctx.Err() == nil { + // keep retrying I/O timeout errors since we still have + // time left + continue + } + // For anything else, don't retry + break + } if err != nil { return } @@ -358,15 +398,15 @@ func (p *TCompactProtocol) ReadMessageBegin() (name string, typeId TMessageType, err = NewTProtocolException(e) return } - name, err = p.ReadString() + name, err = p.ReadString(ctx) return } -func (p *TCompactProtocol) ReadMessageEnd() error { return nil } +func (p *TCompactProtocol) ReadMessageEnd(ctx context.Context) error { return nil } // Read a struct begin. There's nothing on the wire for this, but it is our // opportunity to push a new struct begin marker onto the field stack. -func (p *TCompactProtocol) ReadStructBegin() (name string, err error) { +func (p *TCompactProtocol) ReadStructBegin(ctx context.Context) (name string, err error) { p.lastField = append(p.lastField, p.lastFieldId) p.lastFieldId = 0 return @@ -374,15 +414,18 @@ func (p *TCompactProtocol) ReadStructBegin() (name string, err error) { // Doesn't actually consume any wire data, just removes the last field for // this struct from the field stack. -func (p *TCompactProtocol) ReadStructEnd() error { +func (p *TCompactProtocol) ReadStructEnd(ctx context.Context) error { // consume the last field we read off the wire. + if len(p.lastField) <= 0 { + return NewTProtocolExceptionWithType(INVALID_DATA, errors.New("ReadStructEnd called without matching ReadStructBegin call before")) + } p.lastFieldId = p.lastField[len(p.lastField)-1] p.lastField = p.lastField[:len(p.lastField)-1] return nil } // Read a field header off the wire. -func (p *TCompactProtocol) ReadFieldBegin() (name string, typeId TType, id int16, err error) { +func (p *TCompactProtocol) ReadFieldBegin(ctx context.Context) (name string, typeId TType, id int16, err error) { t, err := p.readByteDirect() if err != nil { return @@ -397,7 +440,7 @@ func (p *TCompactProtocol) ReadFieldBegin() (name string, typeId TType, id int16 modifier := int16((t & 0xf0) >> 4) if modifier == 0 { // not a delta. look ahead for the zigzag varint field id. - id, err = p.ReadI16() + id, err = p.ReadI16(ctx) if err != nil { return } @@ -423,12 +466,12 @@ func (p *TCompactProtocol) ReadFieldBegin() (name string, typeId TType, id int16 return } -func (p *TCompactProtocol) ReadFieldEnd() error { return nil } +func (p *TCompactProtocol) ReadFieldEnd(ctx context.Context) error { return nil } // Read a map header off the wire. If the size is zero, skip reading the key // and value type. This means that 0-length maps will yield TMaps without the // "correct" types. -func (p *TCompactProtocol) ReadMapBegin() (keyType TType, valueType TType, size int, err error) { +func (p *TCompactProtocol) ReadMapBegin(ctx context.Context) (keyType TType, valueType TType, size int, err error) { size32, e := p.readVarint32() if e != nil { err = NewTProtocolException(e) @@ -452,13 +495,13 @@ func (p *TCompactProtocol) ReadMapBegin() (keyType TType, valueType TType, size return } -func (p *TCompactProtocol) ReadMapEnd() error { return nil } +func (p *TCompactProtocol) ReadMapEnd(ctx context.Context) error { return nil } // Read a list header off the wire. If the list size is 0-14, the size will // be packed into the element type header. If it's a longer list, the 4 MSB // of the element type header will be 0xF, and a varint will follow with the // true size. -func (p *TCompactProtocol) ReadListBegin() (elemType TType, size int, err error) { +func (p *TCompactProtocol) ReadListBegin(ctx context.Context) (elemType TType, size int, err error) { size_and_type, err := p.readByteDirect() if err != nil { return @@ -484,22 +527,22 @@ func (p *TCompactProtocol) ReadListBegin() (elemType TType, size int, err error) return } -func (p *TCompactProtocol) ReadListEnd() error { return nil } +func (p *TCompactProtocol) ReadListEnd(ctx context.Context) error { return nil } // Read a set header off the wire. If the set size is 0-14, the size will // be packed into the element type header. If it's a longer set, the 4 MSB // of the element type header will be 0xF, and a varint will follow with the // true size. -func (p *TCompactProtocol) ReadSetBegin() (elemType TType, size int, err error) { - return p.ReadListBegin() +func (p *TCompactProtocol) ReadSetBegin(ctx context.Context) (elemType TType, size int, err error) { + return p.ReadListBegin(ctx) } -func (p *TCompactProtocol) ReadSetEnd() error { return nil } +func (p *TCompactProtocol) ReadSetEnd(ctx context.Context) error { return nil } // Read a boolean off the wire. If this is a boolean field, the value should // already have been read during readFieldBegin, so we'll just consume the // pre-stored value. Otherwise, read a byte. -func (p *TCompactProtocol) ReadBool() (value bool, err error) { +func (p *TCompactProtocol) ReadBool(ctx context.Context) (value bool, err error) { if p.boolValueIsNotNull { p.boolValueIsNotNull = false return p.boolValue, nil @@ -509,7 +552,7 @@ func (p *TCompactProtocol) ReadBool() (value bool, err error) { } // Read a single byte off the wire. Nothing interesting here. -func (p *TCompactProtocol) ReadByte() (int8, error) { +func (p *TCompactProtocol) ReadByte(ctx context.Context) (int8, error) { v, err := p.readByteDirect() if err != nil { return 0, NewTProtocolException(err) @@ -518,13 +561,13 @@ func (p *TCompactProtocol) ReadByte() (int8, error) { } // Read an i16 from the wire as a zigzag varint. -func (p *TCompactProtocol) ReadI16() (value int16, err error) { - v, err := p.ReadI32() +func (p *TCompactProtocol) ReadI16(ctx context.Context) (value int16, err error) { + v, err := p.ReadI32(ctx) return int16(v), err } // Read an i32 from the wire as a zigzag varint. -func (p *TCompactProtocol) ReadI32() (value int32, err error) { +func (p *TCompactProtocol) ReadI32(ctx context.Context) (value int32, err error) { v, e := p.readVarint32() if e != nil { return 0, NewTProtocolException(e) @@ -534,7 +577,7 @@ func (p *TCompactProtocol) ReadI32() (value int32, err error) { } // Read an i64 from the wire as a zigzag varint. -func (p *TCompactProtocol) ReadI64() (value int64, err error) { +func (p *TCompactProtocol) ReadI64(ctx context.Context) (value int64, err error) { v, e := p.readVarint64() if e != nil { return 0, NewTProtocolException(e) @@ -544,7 +587,7 @@ func (p *TCompactProtocol) ReadI64() (value int64, err error) { } // No magic here - just read a double off the wire. -func (p *TCompactProtocol) ReadDouble() (value float64, err error) { +func (p *TCompactProtocol) ReadDouble(ctx context.Context) (value float64, err error) { longBits := p.buffer[0:8] _, e := io.ReadFull(p.trans, longBits) if e != nil { @@ -554,43 +597,44 @@ func (p *TCompactProtocol) ReadDouble() (value float64, err error) { } // Reads a []byte (via readBinary), and then UTF-8 decodes it. -func (p *TCompactProtocol) ReadString() (value string, err error) { +func (p *TCompactProtocol) ReadString(ctx context.Context) (value string, err error) { length, e := p.readVarint32() if e != nil { return "", NewTProtocolException(e) } - if length < 0 { - return "", invalidDataLength + err = checkSizeForProtocol(length, p.cfg) + if err != nil { + return } - if length == 0 { return "", nil } - var buf []byte - if length <= int32(len(p.buffer)) { - buf = p.buffer[0:length] - } else { - buf = make([]byte, length) + if length < int32(len(p.buffer)) { + // Avoid allocation on small reads + buf := p.buffer[:length] + read, e := io.ReadFull(p.trans, buf) + return string(buf[:read]), NewTProtocolException(e) } - _, e = io.ReadFull(p.trans, buf) + + buf, e := safeReadBytes(length, p.trans) return string(buf), NewTProtocolException(e) } // Read a []byte from the wire. -func (p *TCompactProtocol) ReadBinary() (value []byte, err error) { +func (p *TCompactProtocol) ReadBinary(ctx context.Context) (value []byte, err error) { length, e := p.readVarint32() if e != nil { return nil, NewTProtocolException(e) } + err = checkSizeForProtocol(length, p.cfg) + if err != nil { + return + } if length == 0 { return []byte{}, nil } - if length < 0 { - return nil, invalidDataLength - } - buf := make([]byte, length) - _, e = io.ReadFull(p.trans, buf) + buf, e := safeReadBytes(length, p.trans) return buf, NewTProtocolException(e) } @@ -598,8 +642,8 @@ func (p *TCompactProtocol) Flush(ctx context.Context) (err error) { return NewTProtocolException(p.trans.Flush(ctx)) } -func (p *TCompactProtocol) Skip(fieldType TType) (err error) { - return SkipDefaultDepth(p, fieldType) +func (p *TCompactProtocol) Skip(ctx context.Context, fieldType TType) (err error) { + return SkipDefaultDepth(ctx, p, fieldType) } func (p *TCompactProtocol) Transport() TTransport { @@ -801,10 +845,21 @@ func (p *TCompactProtocol) getTType(t tCompactType) (TType, error) { case COMPACT_STRUCT: return STRUCT, nil } - return STOP, TException(fmt.Errorf("don't know what type: %v", t&0x0f)) + return STOP, NewTProtocolException(fmt.Errorf("don't know what type: %v", t&0x0f)) } // Given a TType value, find the appropriate TCompactProtocol.Types constant. func (p *TCompactProtocol) getCompactType(t TType) tCompactType { return ttypeToCompactType[t] } + +func (p *TCompactProtocol) SetTConfiguration(conf *TConfiguration) { + PropagateTConfiguration(p.trans, conf) + PropagateTConfiguration(p.origTransport, conf) + p.cfg = conf +} + +var ( + _ TConfigurationSetter = (*TCompactProtocolFactory)(nil) + _ TConfigurationSetter = (*TCompactProtocol)(nil) +) diff --git a/lib/go/thrift/configuration.go b/lib/go/thrift/configuration.go new file mode 100644 index 00000000000..454d9f37748 --- /dev/null +++ b/lib/go/thrift/configuration.go @@ -0,0 +1,378 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package thrift + +import ( + "crypto/tls" + "fmt" + "time" +) + +// Default TConfiguration values. +const ( + DEFAULT_MAX_MESSAGE_SIZE = 100 * 1024 * 1024 + DEFAULT_MAX_FRAME_SIZE = 16384000 + + DEFAULT_TBINARY_STRICT_READ = false + DEFAULT_TBINARY_STRICT_WRITE = true + + DEFAULT_CONNECT_TIMEOUT = 0 + DEFAULT_SOCKET_TIMEOUT = 0 +) + +// TConfiguration defines some configurations shared between TTransport, +// TProtocol, TTransportFactory, TProtocolFactory, and other implementations. +// +// When constructing TConfiguration, you only need to specify the non-default +// fields. All zero values have sane default values. +// +// Not all configurations defined are applicable to all implementations. +// Implementations are free to ignore the configurations not applicable to them. +// +// All functions attached to this type are nil-safe. +// +// See [1] for spec. +// +// NOTE: When using TConfiguration, fill in all the configurations you want to +// set across the stack, not only the ones you want to set in the immediate +// TTransport/TProtocol. +// +// For example, say you want to migrate this old code into using TConfiguration: +// +// sccket := thrift.NewTSocketTimeout("host:port", time.Second) +// transFactory := thrift.NewTFramedTransportFactoryMaxLength( +// thrift.NewTTransportFactory(), +// 1024 * 1024 * 256, +// ) +// protoFactory := thrift.NewTBinaryProtocolFactory(true, true) +// +// This is the wrong way to do it because in the end the TConfiguration used by +// socket and transFactory will be overwritten by the one used by protoFactory +// because of TConfiguration propagation: +// +// // bad example, DO NOT USE +// sccket := thrift.NewTSocketConf("host:port", &thrift.TConfiguration{ +// ConnectTimeout: time.Second, +// SocketTimeout: time.Second, +// }) +// transFactory := thrift.NewTFramedTransportFactoryConf( +// thrift.NewTTransportFactory(), +// &thrift.TConfiguration{ +// MaxFrameSize: 1024 * 1024 * 256, +// }, +// ) +// protoFactory := thrift.NewTBinaryProtocolFactoryConf(&thrift.TConfiguration{ +// TBinaryStrictRead: thrift.BoolPtr(true), +// TBinaryStrictWrite: thrift.BoolPtr(true), +// }) +// +// This is the correct way to do it: +// +// conf := &thrift.TConfiguration{ +// ConnectTimeout: time.Second, +// SocketTimeout: time.Second, +// +// MaxFrameSize: 1024 * 1024 * 256, +// +// TBinaryStrictRead: thrift.BoolPtr(true), +// TBinaryStrictWrite: thrift.BoolPtr(true), +// } +// sccket := thrift.NewTSocketConf("host:port", conf) +// transFactory := thrift.NewTFramedTransportFactoryConf(thrift.NewTTransportFactory(), conf) +// protoFactory := thrift.NewTBinaryProtocolFactoryConf(conf) +// +// [1]: https://github.com/apache/thrift/blob/master/doc/specs/thrift-tconfiguration.md +type TConfiguration struct { + // If <= 0, DEFAULT_MAX_MESSAGE_SIZE will be used instead. + MaxMessageSize int32 + + // If <= 0, DEFAULT_MAX_FRAME_SIZE will be used instead. + // + // Also if MaxMessageSize < MaxFrameSize, + // MaxMessageSize will be used instead. + MaxFrameSize int32 + + // Connect and socket timeouts to be used by TSocket and TSSLSocket. + // + // 0 means no timeout. + // + // If <0, DEFAULT_CONNECT_TIMEOUT and DEFAULT_SOCKET_TIMEOUT will be + // used. + ConnectTimeout time.Duration + SocketTimeout time.Duration + + // TLS config to be used by TSSLSocket. + TLSConfig *tls.Config + + // Strict read/write configurations for TBinaryProtocol. + // + // BoolPtr helper function is available to use literal values. + TBinaryStrictRead *bool + TBinaryStrictWrite *bool + + // The wrapped protocol id to be used in THeader transport/protocol. + // + // THeaderProtocolIDPtr and THeaderProtocolIDPtrMust helper functions + // are provided to help filling this value. + THeaderProtocolID *THeaderProtocolID + + // Used internally by deprecated constructors, to avoid overriding + // underlying TTransport/TProtocol's cfg by accidental propagations. + // + // For external users this is always false. + noPropagation bool +} + +// GetMaxMessageSize returns the max message size an implementation should +// follow. +// +// It's nil-safe. DEFAULT_MAX_MESSAGE_SIZE will be returned if tc is nil. +func (tc *TConfiguration) GetMaxMessageSize() int32 { + if tc == nil || tc.MaxMessageSize <= 0 { + return DEFAULT_MAX_MESSAGE_SIZE + } + return tc.MaxMessageSize +} + +// GetMaxFrameSize returns the max frame size an implementation should follow. +// +// It's nil-safe. DEFAULT_MAX_FRAME_SIZE will be returned if tc is nil. +// +// If the configured max message size is smaller than the configured max frame +// size, the smaller one will be returned instead. +func (tc *TConfiguration) GetMaxFrameSize() int32 { + if tc == nil { + return DEFAULT_MAX_FRAME_SIZE + } + maxFrameSize := tc.MaxFrameSize + if maxFrameSize <= 0 { + maxFrameSize = DEFAULT_MAX_FRAME_SIZE + } + if maxMessageSize := tc.GetMaxMessageSize(); maxMessageSize < maxFrameSize { + return maxMessageSize + } + return maxFrameSize +} + +// GetConnectTimeout returns the connect timeout should be used by TSocket and +// TSSLSocket. +// +// It's nil-safe. If tc is nil, DEFAULT_CONNECT_TIMEOUT will be returned instead. +func (tc *TConfiguration) GetConnectTimeout() time.Duration { + if tc == nil || tc.ConnectTimeout < 0 { + return DEFAULT_CONNECT_TIMEOUT + } + return tc.ConnectTimeout +} + +// GetSocketTimeout returns the socket timeout should be used by TSocket and +// TSSLSocket. +// +// It's nil-safe. If tc is nil, DEFAULT_SOCKET_TIMEOUT will be returned instead. +func (tc *TConfiguration) GetSocketTimeout() time.Duration { + if tc == nil || tc.SocketTimeout < 0 { + return DEFAULT_SOCKET_TIMEOUT + } + return tc.SocketTimeout +} + +// GetTLSConfig returns the tls config should be used by TSSLSocket. +// +// It's nil-safe. If tc is nil, nil will be returned instead. +func (tc *TConfiguration) GetTLSConfig() *tls.Config { + if tc == nil { + return nil + } + return tc.TLSConfig +} + +// GetTBinaryStrictRead returns the strict read configuration TBinaryProtocol +// should follow. +// +// It's nil-safe. DEFAULT_TBINARY_STRICT_READ will be returned if either tc or +// tc.TBinaryStrictRead is nil. +func (tc *TConfiguration) GetTBinaryStrictRead() bool { + if tc == nil || tc.TBinaryStrictRead == nil { + return DEFAULT_TBINARY_STRICT_READ + } + return *tc.TBinaryStrictRead +} + +// GetTBinaryStrictWrite returns the strict read configuration TBinaryProtocol +// should follow. +// +// It's nil-safe. DEFAULT_TBINARY_STRICT_WRITE will be returned if either tc or +// tc.TBinaryStrictWrite is nil. +func (tc *TConfiguration) GetTBinaryStrictWrite() bool { + if tc == nil || tc.TBinaryStrictWrite == nil { + return DEFAULT_TBINARY_STRICT_WRITE + } + return *tc.TBinaryStrictWrite +} + +// GetTHeaderProtocolID returns the THeaderProtocolID should be used by +// THeaderProtocol clients (for servers, they always use the same one as the +// client instead). +// +// It's nil-safe. If either tc or tc.THeaderProtocolID is nil, +// THeaderProtocolDefault will be returned instead. +// THeaderProtocolDefault will also be returned if configured value is invalid. +func (tc *TConfiguration) GetTHeaderProtocolID() THeaderProtocolID { + if tc == nil || tc.THeaderProtocolID == nil { + return THeaderProtocolDefault + } + protoID := *tc.THeaderProtocolID + if err := protoID.Validate(); err != nil { + return THeaderProtocolDefault + } + return protoID +} + +// THeaderProtocolIDPtr validates and returns the pointer to id. +// +// If id is not a valid THeaderProtocolID, a pointer to THeaderProtocolDefault +// and the validation error will be returned. +func THeaderProtocolIDPtr(id THeaderProtocolID) (*THeaderProtocolID, error) { + err := id.Validate() + if err != nil { + id = THeaderProtocolDefault + } + return &id, err +} + +// THeaderProtocolIDPtrMust validates and returns the pointer to id. +// +// It's similar to THeaderProtocolIDPtr, but it panics on validation errors +// instead of returning them. +func THeaderProtocolIDPtrMust(id THeaderProtocolID) *THeaderProtocolID { + ptr, err := THeaderProtocolIDPtr(id) + if err != nil { + panic(err) + } + return ptr +} + +// TConfigurationSetter is an optional interface TProtocol, TTransport, +// TProtocolFactory, TTransportFactory, and other implementations can implement. +// +// It's intended to be called during intializations. +// The behavior of calling SetTConfiguration on a TTransport/TProtocol in the +// middle of a message is undefined: +// It may or may not change the behavior of the current processing message, +// and it may even cause the current message to fail. +// +// Note for implementations: SetTConfiguration might be called multiple times +// with the same value in quick successions due to the implementation of the +// propagation. Implementations should make SetTConfiguration as simple as +// possible (usually just overwrite the stored configuration and propagate it to +// the wrapped TTransports/TProtocols). +type TConfigurationSetter interface { + SetTConfiguration(*TConfiguration) +} + +// PropagateTConfiguration propagates cfg to impl if impl implements +// TConfigurationSetter and cfg is non-nil, otherwise it does nothing. +// +// NOTE: nil cfg is not propagated. If you want to propagate a TConfiguration +// with everything being default value, use &TConfiguration{} explicitly instead. +func PropagateTConfiguration(impl interface{}, cfg *TConfiguration) { + if cfg == nil || cfg.noPropagation { + return + } + + if setter, ok := impl.(TConfigurationSetter); ok { + setter.SetTConfiguration(cfg) + } +} + +func checkSizeForProtocol(size int32, cfg *TConfiguration) error { + if size < 0 { + return NewTProtocolExceptionWithType( + NEGATIVE_SIZE, + fmt.Errorf("negative size: %d", size), + ) + } + if size > cfg.GetMaxMessageSize() { + return NewTProtocolExceptionWithType( + SIZE_LIMIT, + fmt.Errorf("size exceeded max allowed: %d", size), + ) + } + return nil +} + +type tTransportFactoryConf struct { + delegate TTransportFactory + cfg *TConfiguration +} + +func (f *tTransportFactoryConf) GetTransport(orig TTransport) (TTransport, error) { + trans, err := f.delegate.GetTransport(orig) + if err == nil { + PropagateTConfiguration(orig, f.cfg) + PropagateTConfiguration(trans, f.cfg) + } + return trans, err +} + +func (f *tTransportFactoryConf) SetTConfiguration(cfg *TConfiguration) { + PropagateTConfiguration(f.delegate, f.cfg) + f.cfg = cfg +} + +// TTransportFactoryConf wraps a TTransportFactory to propagate +// TConfiguration on the factory's GetTransport calls. +func TTransportFactoryConf(delegate TTransportFactory, conf *TConfiguration) TTransportFactory { + return &tTransportFactoryConf{ + delegate: delegate, + cfg: conf, + } +} + +type tProtocolFactoryConf struct { + delegate TProtocolFactory + cfg *TConfiguration +} + +func (f *tProtocolFactoryConf) GetProtocol(trans TTransport) TProtocol { + proto := f.delegate.GetProtocol(trans) + PropagateTConfiguration(trans, f.cfg) + PropagateTConfiguration(proto, f.cfg) + return proto +} + +func (f *tProtocolFactoryConf) SetTConfiguration(cfg *TConfiguration) { + PropagateTConfiguration(f.delegate, f.cfg) + f.cfg = cfg +} + +// TProtocolFactoryConf wraps a TProtocolFactory to propagate +// TConfiguration on the factory's GetProtocol calls. +func TProtocolFactoryConf(delegate TProtocolFactory, conf *TConfiguration) TProtocolFactory { + return &tProtocolFactoryConf{ + delegate: delegate, + cfg: conf, + } +} + +var ( + _ TConfigurationSetter = (*tTransportFactoryConf)(nil) + _ TConfigurationSetter = (*tProtocolFactoryConf)(nil) +) diff --git a/lib/go/thrift/configuration_test.go b/lib/go/thrift/configuration_test.go new file mode 100644 index 00000000000..f7478423154 --- /dev/null +++ b/lib/go/thrift/configuration_test.go @@ -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. + */ + +package thrift + +import ( + "crypto/tls" + "testing" + "time" +) + +func TestTConfiguration(t *testing.T) { + invalidProtoID := THeaderProtocolID(-1) + if invalidProtoID.Validate() == nil { + t.Fatalf("Expected %v to be an invalid THeaderProtocolID, it passes the validation", invalidProtoID) + } + + tlsConfig := &tls.Config{ + Time: time.Now, + } + + for _, c := range []struct { + label string + cfg *TConfiguration + expectedMessageSize int32 + expectedFrameSize int32 + expectedConnectTimeout time.Duration + expectedSocketTimeout time.Duration + expectedTLSConfig *tls.Config + expectedBinaryRead bool + expectedBinaryWrite bool + expectedProtoID THeaderProtocolID + }{ + { + label: "nil", + cfg: nil, + expectedMessageSize: DEFAULT_MAX_MESSAGE_SIZE, + expectedFrameSize: DEFAULT_MAX_FRAME_SIZE, + expectedConnectTimeout: DEFAULT_CONNECT_TIMEOUT, + expectedSocketTimeout: DEFAULT_SOCKET_TIMEOUT, + expectedTLSConfig: nil, + expectedBinaryRead: DEFAULT_TBINARY_STRICT_READ, + expectedBinaryWrite: DEFAULT_TBINARY_STRICT_WRITE, + expectedProtoID: THeaderProtocolDefault, + }, + { + label: "empty", + cfg: &TConfiguration{}, + expectedMessageSize: DEFAULT_MAX_MESSAGE_SIZE, + expectedFrameSize: DEFAULT_MAX_FRAME_SIZE, + expectedConnectTimeout: DEFAULT_CONNECT_TIMEOUT, + expectedSocketTimeout: DEFAULT_SOCKET_TIMEOUT, + expectedTLSConfig: nil, + expectedBinaryRead: DEFAULT_TBINARY_STRICT_READ, + expectedBinaryWrite: DEFAULT_TBINARY_STRICT_WRITE, + expectedProtoID: THeaderProtocolDefault, + }, + { + label: "normal", + cfg: &TConfiguration{ + MaxMessageSize: 1024, + MaxFrameSize: 1024, + ConnectTimeout: time.Millisecond, + SocketTimeout: time.Millisecond * 2, + TLSConfig: tlsConfig, + TBinaryStrictRead: BoolPtr(true), + TBinaryStrictWrite: BoolPtr(false), + THeaderProtocolID: THeaderProtocolIDPtrMust(THeaderProtocolCompact), + }, + expectedMessageSize: 1024, + expectedFrameSize: 1024, + expectedConnectTimeout: time.Millisecond, + expectedSocketTimeout: time.Millisecond * 2, + expectedTLSConfig: tlsConfig, + expectedBinaryRead: true, + expectedBinaryWrite: false, + expectedProtoID: THeaderProtocolCompact, + }, + { + label: "message %#v", tdp.LogPrefix, name, typeId, seqid, err) +func (tdp *TDebugProtocol) logf(format string, v ...interface{}) { + fallbackLogger(tdp.Logger)(fmt.Sprintf(format, v...)) +} + +func (tdp *TDebugProtocol) WriteMessageBegin(ctx context.Context, name string, typeId TMessageType, seqid int32) error { + err := tdp.Delegate.WriteMessageBegin(ctx, name, typeId, seqid) + tdp.logf("%sWriteMessageBegin(name=%#v, typeId=%#v, seqid=%#v) => %#v", tdp.LogPrefix, name, typeId, seqid, err) + if tdp.DuplicateTo != nil { + tdp.DuplicateTo.WriteMessageBegin(ctx, name, typeId, seqid) + } return err } -func (tdp *TDebugProtocol) WriteMessageEnd() error { - err := tdp.Delegate.WriteMessageEnd() - log.Printf("%sWriteMessageEnd() => %#v", tdp.LogPrefix, err) +func (tdp *TDebugProtocol) WriteMessageEnd(ctx context.Context) error { + err := tdp.Delegate.WriteMessageEnd(ctx) + tdp.logf("%sWriteMessageEnd() => %#v", tdp.LogPrefix, err) + if tdp.DuplicateTo != nil { + tdp.DuplicateTo.WriteMessageEnd(ctx) + } return err } -func (tdp *TDebugProtocol) WriteStructBegin(name string) error { - err := tdp.Delegate.WriteStructBegin(name) - log.Printf("%sWriteStructBegin(name=%#v) => %#v", tdp.LogPrefix, name, err) +func (tdp *TDebugProtocol) WriteStructBegin(ctx context.Context, name string) error { + err := tdp.Delegate.WriteStructBegin(ctx, name) + tdp.logf("%sWriteStructBegin(name=%#v) => %#v", tdp.LogPrefix, name, err) + if tdp.DuplicateTo != nil { + tdp.DuplicateTo.WriteStructBegin(ctx, name) + } return err } -func (tdp *TDebugProtocol) WriteStructEnd() error { - err := tdp.Delegate.WriteStructEnd() - log.Printf("%sWriteStructEnd() => %#v", tdp.LogPrefix, err) +func (tdp *TDebugProtocol) WriteStructEnd(ctx context.Context) error { + err := tdp.Delegate.WriteStructEnd(ctx) + tdp.logf("%sWriteStructEnd() => %#v", tdp.LogPrefix, err) + if tdp.DuplicateTo != nil { + tdp.DuplicateTo.WriteStructEnd(ctx) + } return err } -func (tdp *TDebugProtocol) WriteFieldBegin(name string, typeId TType, id int16) error { - err := tdp.Delegate.WriteFieldBegin(name, typeId, id) - log.Printf("%sWriteFieldBegin(name=%#v, typeId=%#v, id%#v) => %#v", tdp.LogPrefix, name, typeId, id, err) +func (tdp *TDebugProtocol) WriteFieldBegin(ctx context.Context, name string, typeId TType, id int16) error { + err := tdp.Delegate.WriteFieldBegin(ctx, name, typeId, id) + tdp.logf("%sWriteFieldBegin(name=%#v, typeId=%#v, id%#v) => %#v", tdp.LogPrefix, name, typeId, id, err) + if tdp.DuplicateTo != nil { + tdp.DuplicateTo.WriteFieldBegin(ctx, name, typeId, id) + } return err } -func (tdp *TDebugProtocol) WriteFieldEnd() error { - err := tdp.Delegate.WriteFieldEnd() - log.Printf("%sWriteFieldEnd() => %#v", tdp.LogPrefix, err) +func (tdp *TDebugProtocol) WriteFieldEnd(ctx context.Context) error { + err := tdp.Delegate.WriteFieldEnd(ctx) + tdp.logf("%sWriteFieldEnd() => %#v", tdp.LogPrefix, err) + if tdp.DuplicateTo != nil { + tdp.DuplicateTo.WriteFieldEnd(ctx) + } return err } -func (tdp *TDebugProtocol) WriteFieldStop() error { - err := tdp.Delegate.WriteFieldStop() - log.Printf("%sWriteFieldStop() => %#v", tdp.LogPrefix, err) +func (tdp *TDebugProtocol) WriteFieldStop(ctx context.Context) error { + err := tdp.Delegate.WriteFieldStop(ctx) + tdp.logf("%sWriteFieldStop() => %#v", tdp.LogPrefix, err) + if tdp.DuplicateTo != nil { + tdp.DuplicateTo.WriteFieldStop(ctx) + } return err } -func (tdp *TDebugProtocol) WriteMapBegin(keyType TType, valueType TType, size int) error { - err := tdp.Delegate.WriteMapBegin(keyType, valueType, size) - log.Printf("%sWriteMapBegin(keyType=%#v, valueType=%#v, size=%#v) => %#v", tdp.LogPrefix, keyType, valueType, size, err) +func (tdp *TDebugProtocol) WriteMapBegin(ctx context.Context, keyType TType, valueType TType, size int) error { + err := tdp.Delegate.WriteMapBegin(ctx, keyType, valueType, size) + tdp.logf("%sWriteMapBegin(keyType=%#v, valueType=%#v, size=%#v) => %#v", tdp.LogPrefix, keyType, valueType, size, err) + if tdp.DuplicateTo != nil { + tdp.DuplicateTo.WriteMapBegin(ctx, keyType, valueType, size) + } return err } -func (tdp *TDebugProtocol) WriteMapEnd() error { - err := tdp.Delegate.WriteMapEnd() - log.Printf("%sWriteMapEnd() => %#v", tdp.LogPrefix, err) +func (tdp *TDebugProtocol) WriteMapEnd(ctx context.Context) error { + err := tdp.Delegate.WriteMapEnd(ctx) + tdp.logf("%sWriteMapEnd() => %#v", tdp.LogPrefix, err) + if tdp.DuplicateTo != nil { + tdp.DuplicateTo.WriteMapEnd(ctx) + } return err } -func (tdp *TDebugProtocol) WriteListBegin(elemType TType, size int) error { - err := tdp.Delegate.WriteListBegin(elemType, size) - log.Printf("%sWriteListBegin(elemType=%#v, size=%#v) => %#v", tdp.LogPrefix, elemType, size, err) +func (tdp *TDebugProtocol) WriteListBegin(ctx context.Context, elemType TType, size int) error { + err := tdp.Delegate.WriteListBegin(ctx, elemType, size) + tdp.logf("%sWriteListBegin(elemType=%#v, size=%#v) => %#v", tdp.LogPrefix, elemType, size, err) + if tdp.DuplicateTo != nil { + tdp.DuplicateTo.WriteListBegin(ctx, elemType, size) + } return err } -func (tdp *TDebugProtocol) WriteListEnd() error { - err := tdp.Delegate.WriteListEnd() - log.Printf("%sWriteListEnd() => %#v", tdp.LogPrefix, err) +func (tdp *TDebugProtocol) WriteListEnd(ctx context.Context) error { + err := tdp.Delegate.WriteListEnd(ctx) + tdp.logf("%sWriteListEnd() => %#v", tdp.LogPrefix, err) + if tdp.DuplicateTo != nil { + tdp.DuplicateTo.WriteListEnd(ctx) + } return err } -func (tdp *TDebugProtocol) WriteSetBegin(elemType TType, size int) error { - err := tdp.Delegate.WriteSetBegin(elemType, size) - log.Printf("%sWriteSetBegin(elemType=%#v, size=%#v) => %#v", tdp.LogPrefix, elemType, size, err) +func (tdp *TDebugProtocol) WriteSetBegin(ctx context.Context, elemType TType, size int) error { + err := tdp.Delegate.WriteSetBegin(ctx, elemType, size) + tdp.logf("%sWriteSetBegin(elemType=%#v, size=%#v) => %#v", tdp.LogPrefix, elemType, size, err) + if tdp.DuplicateTo != nil { + tdp.DuplicateTo.WriteSetBegin(ctx, elemType, size) + } return err } -func (tdp *TDebugProtocol) WriteSetEnd() error { - err := tdp.Delegate.WriteSetEnd() - log.Printf("%sWriteSetEnd() => %#v", tdp.LogPrefix, err) +func (tdp *TDebugProtocol) WriteSetEnd(ctx context.Context) error { + err := tdp.Delegate.WriteSetEnd(ctx) + tdp.logf("%sWriteSetEnd() => %#v", tdp.LogPrefix, err) + if tdp.DuplicateTo != nil { + tdp.DuplicateTo.WriteSetEnd(ctx) + } return err } -func (tdp *TDebugProtocol) WriteBool(value bool) error { - err := tdp.Delegate.WriteBool(value) - log.Printf("%sWriteBool(value=%#v) => %#v", tdp.LogPrefix, value, err) +func (tdp *TDebugProtocol) WriteBool(ctx context.Context, value bool) error { + err := tdp.Delegate.WriteBool(ctx, value) + tdp.logf("%sWriteBool(value=%#v) => %#v", tdp.LogPrefix, value, err) + if tdp.DuplicateTo != nil { + tdp.DuplicateTo.WriteBool(ctx, value) + } return err } -func (tdp *TDebugProtocol) WriteByte(value int8) error { - err := tdp.Delegate.WriteByte(value) - log.Printf("%sWriteByte(value=%#v) => %#v", tdp.LogPrefix, value, err) +func (tdp *TDebugProtocol) WriteByte(ctx context.Context, value int8) error { + err := tdp.Delegate.WriteByte(ctx, value) + tdp.logf("%sWriteByte(value=%#v) => %#v", tdp.LogPrefix, value, err) + if tdp.DuplicateTo != nil { + tdp.DuplicateTo.WriteByte(ctx, value) + } return err } -func (tdp *TDebugProtocol) WriteI16(value int16) error { - err := tdp.Delegate.WriteI16(value) - log.Printf("%sWriteI16(value=%#v) => %#v", tdp.LogPrefix, value, err) +func (tdp *TDebugProtocol) WriteI16(ctx context.Context, value int16) error { + err := tdp.Delegate.WriteI16(ctx, value) + tdp.logf("%sWriteI16(value=%#v) => %#v", tdp.LogPrefix, value, err) + if tdp.DuplicateTo != nil { + tdp.DuplicateTo.WriteI16(ctx, value) + } return err } -func (tdp *TDebugProtocol) WriteI32(value int32) error { - err := tdp.Delegate.WriteI32(value) - log.Printf("%sWriteI32(value=%#v) => %#v", tdp.LogPrefix, value, err) +func (tdp *TDebugProtocol) WriteI32(ctx context.Context, value int32) error { + err := tdp.Delegate.WriteI32(ctx, value) + tdp.logf("%sWriteI32(value=%#v) => %#v", tdp.LogPrefix, value, err) + if tdp.DuplicateTo != nil { + tdp.DuplicateTo.WriteI32(ctx, value) + } return err } -func (tdp *TDebugProtocol) WriteI64(value int64) error { - err := tdp.Delegate.WriteI64(value) - log.Printf("%sWriteI64(value=%#v) => %#v", tdp.LogPrefix, value, err) +func (tdp *TDebugProtocol) WriteI64(ctx context.Context, value int64) error { + err := tdp.Delegate.WriteI64(ctx, value) + tdp.logf("%sWriteI64(value=%#v) => %#v", tdp.LogPrefix, value, err) + if tdp.DuplicateTo != nil { + tdp.DuplicateTo.WriteI64(ctx, value) + } return err } -func (tdp *TDebugProtocol) WriteDouble(value float64) error { - err := tdp.Delegate.WriteDouble(value) - log.Printf("%sWriteDouble(value=%#v) => %#v", tdp.LogPrefix, value, err) +func (tdp *TDebugProtocol) WriteDouble(ctx context.Context, value float64) error { + err := tdp.Delegate.WriteDouble(ctx, value) + tdp.logf("%sWriteDouble(value=%#v) => %#v", tdp.LogPrefix, value, err) + if tdp.DuplicateTo != nil { + tdp.DuplicateTo.WriteDouble(ctx, value) + } return err } -func (tdp *TDebugProtocol) WriteString(value string) error { - err := tdp.Delegate.WriteString(value) - log.Printf("%sWriteString(value=%#v) => %#v", tdp.LogPrefix, value, err) +func (tdp *TDebugProtocol) WriteString(ctx context.Context, value string) error { + err := tdp.Delegate.WriteString(ctx, value) + tdp.logf("%sWriteString(value=%#v) => %#v", tdp.LogPrefix, value, err) + if tdp.DuplicateTo != nil { + tdp.DuplicateTo.WriteString(ctx, value) + } return err } -func (tdp *TDebugProtocol) WriteBinary(value []byte) error { - err := tdp.Delegate.WriteBinary(value) - log.Printf("%sWriteBinary(value=%#v) => %#v", tdp.LogPrefix, value, err) +func (tdp *TDebugProtocol) WriteBinary(ctx context.Context, value []byte) error { + err := tdp.Delegate.WriteBinary(ctx, value) + tdp.logf("%sWriteBinary(value=%#v) => %#v", tdp.LogPrefix, value, err) + if tdp.DuplicateTo != nil { + tdp.DuplicateTo.WriteBinary(ctx, value) + } return err } -func (tdp *TDebugProtocol) ReadMessageBegin() (name string, typeId TMessageType, seqid int32, err error) { - name, typeId, seqid, err = tdp.Delegate.ReadMessageBegin() - log.Printf("%sReadMessageBegin() (name=%#v, typeId=%#v, seqid=%#v, err=%#v)", tdp.LogPrefix, name, typeId, seqid, err) +func (tdp *TDebugProtocol) ReadMessageBegin(ctx context.Context) (name string, typeId TMessageType, seqid int32, err error) { + name, typeId, seqid, err = tdp.Delegate.ReadMessageBegin(ctx) + tdp.logf("%sReadMessageBegin() (name=%#v, typeId=%#v, seqid=%#v, err=%#v)", tdp.LogPrefix, name, typeId, seqid, err) + if tdp.DuplicateTo != nil { + tdp.DuplicateTo.WriteMessageBegin(ctx, name, typeId, seqid) + } return } -func (tdp *TDebugProtocol) ReadMessageEnd() (err error) { - err = tdp.Delegate.ReadMessageEnd() - log.Printf("%sReadMessageEnd() err=%#v", tdp.LogPrefix, err) +func (tdp *TDebugProtocol) ReadMessageEnd(ctx context.Context) (err error) { + err = tdp.Delegate.ReadMessageEnd(ctx) + tdp.logf("%sReadMessageEnd() err=%#v", tdp.LogPrefix, err) + if tdp.DuplicateTo != nil { + tdp.DuplicateTo.WriteMessageEnd(ctx) + } return } -func (tdp *TDebugProtocol) ReadStructBegin() (name string, err error) { - name, err = tdp.Delegate.ReadStructBegin() - log.Printf("%sReadStructBegin() (name%#v, err=%#v)", tdp.LogPrefix, name, err) +func (tdp *TDebugProtocol) ReadStructBegin(ctx context.Context) (name string, err error) { + name, err = tdp.Delegate.ReadStructBegin(ctx) + tdp.logf("%sReadStructBegin() (name%#v, err=%#v)", tdp.LogPrefix, name, err) + if tdp.DuplicateTo != nil { + tdp.DuplicateTo.WriteStructBegin(ctx, name) + } return } -func (tdp *TDebugProtocol) ReadStructEnd() (err error) { - err = tdp.Delegate.ReadStructEnd() - log.Printf("%sReadStructEnd() err=%#v", tdp.LogPrefix, err) +func (tdp *TDebugProtocol) ReadStructEnd(ctx context.Context) (err error) { + err = tdp.Delegate.ReadStructEnd(ctx) + tdp.logf("%sReadStructEnd() err=%#v", tdp.LogPrefix, err) + if tdp.DuplicateTo != nil { + tdp.DuplicateTo.WriteStructEnd(ctx) + } return } -func (tdp *TDebugProtocol) ReadFieldBegin() (name string, typeId TType, id int16, err error) { - name, typeId, id, err = tdp.Delegate.ReadFieldBegin() - log.Printf("%sReadFieldBegin() (name=%#v, typeId=%#v, id=%#v, err=%#v)", tdp.LogPrefix, name, typeId, id, err) +func (tdp *TDebugProtocol) ReadFieldBegin(ctx context.Context) (name string, typeId TType, id int16, err error) { + name, typeId, id, err = tdp.Delegate.ReadFieldBegin(ctx) + tdp.logf("%sReadFieldBegin() (name=%#v, typeId=%#v, id=%#v, err=%#v)", tdp.LogPrefix, name, typeId, id, err) + if tdp.DuplicateTo != nil { + tdp.DuplicateTo.WriteFieldBegin(ctx, name, typeId, id) + } return } -func (tdp *TDebugProtocol) ReadFieldEnd() (err error) { - err = tdp.Delegate.ReadFieldEnd() - log.Printf("%sReadFieldEnd() err=%#v", tdp.LogPrefix, err) +func (tdp *TDebugProtocol) ReadFieldEnd(ctx context.Context) (err error) { + err = tdp.Delegate.ReadFieldEnd(ctx) + tdp.logf("%sReadFieldEnd() err=%#v", tdp.LogPrefix, err) + if tdp.DuplicateTo != nil { + tdp.DuplicateTo.WriteFieldEnd(ctx) + } return } -func (tdp *TDebugProtocol) ReadMapBegin() (keyType TType, valueType TType, size int, err error) { - keyType, valueType, size, err = tdp.Delegate.ReadMapBegin() - log.Printf("%sReadMapBegin() (keyType=%#v, valueType=%#v, size=%#v, err=%#v)", tdp.LogPrefix, keyType, valueType, size, err) +func (tdp *TDebugProtocol) ReadMapBegin(ctx context.Context) (keyType TType, valueType TType, size int, err error) { + keyType, valueType, size, err = tdp.Delegate.ReadMapBegin(ctx) + tdp.logf("%sReadMapBegin() (keyType=%#v, valueType=%#v, size=%#v, err=%#v)", tdp.LogPrefix, keyType, valueType, size, err) + if tdp.DuplicateTo != nil { + tdp.DuplicateTo.WriteMapBegin(ctx, keyType, valueType, size) + } return } -func (tdp *TDebugProtocol) ReadMapEnd() (err error) { - err = tdp.Delegate.ReadMapEnd() - log.Printf("%sReadMapEnd() err=%#v", tdp.LogPrefix, err) +func (tdp *TDebugProtocol) ReadMapEnd(ctx context.Context) (err error) { + err = tdp.Delegate.ReadMapEnd(ctx) + tdp.logf("%sReadMapEnd() err=%#v", tdp.LogPrefix, err) + if tdp.DuplicateTo != nil { + tdp.DuplicateTo.WriteMapEnd(ctx) + } return } -func (tdp *TDebugProtocol) ReadListBegin() (elemType TType, size int, err error) { - elemType, size, err = tdp.Delegate.ReadListBegin() - log.Printf("%sReadListBegin() (elemType=%#v, size=%#v, err=%#v)", tdp.LogPrefix, elemType, size, err) +func (tdp *TDebugProtocol) ReadListBegin(ctx context.Context) (elemType TType, size int, err error) { + elemType, size, err = tdp.Delegate.ReadListBegin(ctx) + tdp.logf("%sReadListBegin() (elemType=%#v, size=%#v, err=%#v)", tdp.LogPrefix, elemType, size, err) + if tdp.DuplicateTo != nil { + tdp.DuplicateTo.WriteListBegin(ctx, elemType, size) + } return } -func (tdp *TDebugProtocol) ReadListEnd() (err error) { - err = tdp.Delegate.ReadListEnd() - log.Printf("%sReadListEnd() err=%#v", tdp.LogPrefix, err) +func (tdp *TDebugProtocol) ReadListEnd(ctx context.Context) (err error) { + err = tdp.Delegate.ReadListEnd(ctx) + tdp.logf("%sReadListEnd() err=%#v", tdp.LogPrefix, err) + if tdp.DuplicateTo != nil { + tdp.DuplicateTo.WriteListEnd(ctx) + } return } -func (tdp *TDebugProtocol) ReadSetBegin() (elemType TType, size int, err error) { - elemType, size, err = tdp.Delegate.ReadSetBegin() - log.Printf("%sReadSetBegin() (elemType=%#v, size=%#v, err=%#v)", tdp.LogPrefix, elemType, size, err) +func (tdp *TDebugProtocol) ReadSetBegin(ctx context.Context) (elemType TType, size int, err error) { + elemType, size, err = tdp.Delegate.ReadSetBegin(ctx) + tdp.logf("%sReadSetBegin() (elemType=%#v, size=%#v, err=%#v)", tdp.LogPrefix, elemType, size, err) + if tdp.DuplicateTo != nil { + tdp.DuplicateTo.WriteSetBegin(ctx, elemType, size) + } return } -func (tdp *TDebugProtocol) ReadSetEnd() (err error) { - err = tdp.Delegate.ReadSetEnd() - log.Printf("%sReadSetEnd() err=%#v", tdp.LogPrefix, err) +func (tdp *TDebugProtocol) ReadSetEnd(ctx context.Context) (err error) { + err = tdp.Delegate.ReadSetEnd(ctx) + tdp.logf("%sReadSetEnd() err=%#v", tdp.LogPrefix, err) + if tdp.DuplicateTo != nil { + tdp.DuplicateTo.WriteSetEnd(ctx) + } return } -func (tdp *TDebugProtocol) ReadBool() (value bool, err error) { - value, err = tdp.Delegate.ReadBool() - log.Printf("%sReadBool() (value=%#v, err=%#v)", tdp.LogPrefix, value, err) +func (tdp *TDebugProtocol) ReadBool(ctx context.Context) (value bool, err error) { + value, err = tdp.Delegate.ReadBool(ctx) + tdp.logf("%sReadBool() (value=%#v, err=%#v)", tdp.LogPrefix, value, err) + if tdp.DuplicateTo != nil { + tdp.DuplicateTo.WriteBool(ctx, value) + } return } -func (tdp *TDebugProtocol) ReadByte() (value int8, err error) { - value, err = tdp.Delegate.ReadByte() - log.Printf("%sReadByte() (value=%#v, err=%#v)", tdp.LogPrefix, value, err) +func (tdp *TDebugProtocol) ReadByte(ctx context.Context) (value int8, err error) { + value, err = tdp.Delegate.ReadByte(ctx) + tdp.logf("%sReadByte() (value=%#v, err=%#v)", tdp.LogPrefix, value, err) + if tdp.DuplicateTo != nil { + tdp.DuplicateTo.WriteByte(ctx, value) + } return } -func (tdp *TDebugProtocol) ReadI16() (value int16, err error) { - value, err = tdp.Delegate.ReadI16() - log.Printf("%sReadI16() (value=%#v, err=%#v)", tdp.LogPrefix, value, err) +func (tdp *TDebugProtocol) ReadI16(ctx context.Context) (value int16, err error) { + value, err = tdp.Delegate.ReadI16(ctx) + tdp.logf("%sReadI16() (value=%#v, err=%#v)", tdp.LogPrefix, value, err) + if tdp.DuplicateTo != nil { + tdp.DuplicateTo.WriteI16(ctx, value) + } return } -func (tdp *TDebugProtocol) ReadI32() (value int32, err error) { - value, err = tdp.Delegate.ReadI32() - log.Printf("%sReadI32() (value=%#v, err=%#v)", tdp.LogPrefix, value, err) +func (tdp *TDebugProtocol) ReadI32(ctx context.Context) (value int32, err error) { + value, err = tdp.Delegate.ReadI32(ctx) + tdp.logf("%sReadI32() (value=%#v, err=%#v)", tdp.LogPrefix, value, err) + if tdp.DuplicateTo != nil { + tdp.DuplicateTo.WriteI32(ctx, value) + } return } -func (tdp *TDebugProtocol) ReadI64() (value int64, err error) { - value, err = tdp.Delegate.ReadI64() - log.Printf("%sReadI64() (value=%#v, err=%#v)", tdp.LogPrefix, value, err) +func (tdp *TDebugProtocol) ReadI64(ctx context.Context) (value int64, err error) { + value, err = tdp.Delegate.ReadI64(ctx) + tdp.logf("%sReadI64() (value=%#v, err=%#v)", tdp.LogPrefix, value, err) + if tdp.DuplicateTo != nil { + tdp.DuplicateTo.WriteI64(ctx, value) + } return } -func (tdp *TDebugProtocol) ReadDouble() (value float64, err error) { - value, err = tdp.Delegate.ReadDouble() - log.Printf("%sReadDouble() (value=%#v, err=%#v)", tdp.LogPrefix, value, err) +func (tdp *TDebugProtocol) ReadDouble(ctx context.Context) (value float64, err error) { + value, err = tdp.Delegate.ReadDouble(ctx) + tdp.logf("%sReadDouble() (value=%#v, err=%#v)", tdp.LogPrefix, value, err) + if tdp.DuplicateTo != nil { + tdp.DuplicateTo.WriteDouble(ctx, value) + } return } -func (tdp *TDebugProtocol) ReadString() (value string, err error) { - value, err = tdp.Delegate.ReadString() - log.Printf("%sReadString() (value=%#v, err=%#v)", tdp.LogPrefix, value, err) +func (tdp *TDebugProtocol) ReadString(ctx context.Context) (value string, err error) { + value, err = tdp.Delegate.ReadString(ctx) + tdp.logf("%sReadString() (value=%#v, err=%#v)", tdp.LogPrefix, value, err) + if tdp.DuplicateTo != nil { + tdp.DuplicateTo.WriteString(ctx, value) + } return } -func (tdp *TDebugProtocol) ReadBinary() (value []byte, err error) { - value, err = tdp.Delegate.ReadBinary() - log.Printf("%sReadBinary() (value=%#v, err=%#v)", tdp.LogPrefix, value, err) +func (tdp *TDebugProtocol) ReadBinary(ctx context.Context) (value []byte, err error) { + value, err = tdp.Delegate.ReadBinary(ctx) + tdp.logf("%sReadBinary() (value=%#v, err=%#v)", tdp.LogPrefix, value, err) + if tdp.DuplicateTo != nil { + tdp.DuplicateTo.WriteBinary(ctx, value) + } return } -func (tdp *TDebugProtocol) Skip(fieldType TType) (err error) { - err = tdp.Delegate.Skip(fieldType) - log.Printf("%sSkip(fieldType=%#v) (err=%#v)", tdp.LogPrefix, fieldType, err) +func (tdp *TDebugProtocol) Skip(ctx context.Context, fieldType TType) (err error) { + err = tdp.Delegate.Skip(ctx, fieldType) + tdp.logf("%sSkip(fieldType=%#v) (err=%#v)", tdp.LogPrefix, fieldType, err) + if tdp.DuplicateTo != nil { + tdp.DuplicateTo.Skip(ctx, fieldType) + } return } func (tdp *TDebugProtocol) Flush(ctx context.Context) (err error) { err = tdp.Delegate.Flush(ctx) - log.Printf("%sFlush() (err=%#v)", tdp.LogPrefix, err) + tdp.logf("%sFlush() (err=%#v)", tdp.LogPrefix, err) + if tdp.DuplicateTo != nil { + tdp.DuplicateTo.Flush(ctx) + } return } func (tdp *TDebugProtocol) Transport() TTransport { return tdp.Delegate.Transport() } + +// SetTConfiguration implements TConfigurationSetter for propagation. +func (tdp *TDebugProtocol) SetTConfiguration(conf *TConfiguration) { + PropagateTConfiguration(tdp.Delegate, conf) + PropagateTConfiguration(tdp.DuplicateTo, conf) +} + +var _ TConfigurationSetter = (*TDebugProtocol)(nil) diff --git a/lib/go/thrift/deserializer.go b/lib/go/thrift/deserializer.go index 91a0983a4ad..cefc7ecda5d 100644 --- a/lib/go/thrift/deserializer.go +++ b/lib/go/thrift/deserializer.go @@ -19,40 +19,103 @@ package thrift +import ( + "context" + "sync" +) + type TDeserializer struct { - Transport TTransport + Transport *TMemoryBuffer Protocol TProtocol } func NewTDeserializer() *TDeserializer { - var transport TTransport - transport = NewTMemoryBufferLen(1024) - - protocol := NewTBinaryProtocolFactoryDefault().GetProtocol(transport) + transport := NewTMemoryBufferLen(1024) + protocol := NewTBinaryProtocolTransport(transport) return &TDeserializer{ - transport, - protocol} + Transport: transport, + Protocol: protocol, + } } -func (t *TDeserializer) ReadString(msg TStruct, s string) (err error) { +func (t *TDeserializer) ReadString(ctx context.Context, msg TStruct, s string) (err error) { + t.Transport.Reset() + err = nil if _, err = t.Transport.Write([]byte(s)); err != nil { return } - if err = msg.Read(t.Protocol); err != nil { + if err = msg.Read(ctx, t.Protocol); err != nil { return } return } -func (t *TDeserializer) Read(msg TStruct, b []byte) (err error) { +func (t *TDeserializer) Read(ctx context.Context, msg TStruct, b []byte) (err error) { + t.Transport.Reset() + err = nil if _, err = t.Transport.Write(b); err != nil { return } - if err = msg.Read(t.Protocol); err != nil { + if err = msg.Read(ctx, t.Protocol); err != nil { return } return } + +// TDeserializerPool is the thread-safe version of TDeserializer, +// it uses resource pool of TDeserializer under the hood. +// +// It must be initialized with either NewTDeserializerPool or +// NewTDeserializerPoolSizeFactory. +type TDeserializerPool struct { + pool sync.Pool +} + +// NewTDeserializerPool creates a new TDeserializerPool. +// +// NewTDeserializer can be used as the arg here. +func NewTDeserializerPool(f func() *TDeserializer) *TDeserializerPool { + return &TDeserializerPool{ + pool: sync.Pool{ + New: func() interface{} { + return f() + }, + }, + } +} + +// NewTDeserializerPoolSizeFactory creates a new TDeserializerPool with +// the given size and protocol factory. +// +// Note that the size is not the limit. The TMemoryBuffer underneath can grow +// larger than that. It just dictates the initial size. +func NewTDeserializerPoolSizeFactory(size int, factory TProtocolFactory) *TDeserializerPool { + return &TDeserializerPool{ + pool: sync.Pool{ + New: func() interface{} { + transport := NewTMemoryBufferLen(size) + protocol := factory.GetProtocol(transport) + + return &TDeserializer{ + Transport: transport, + Protocol: protocol, + } + }, + }, + } +} + +func (t *TDeserializerPool) ReadString(ctx context.Context, msg TStruct, s string) error { + d := t.pool.Get().(*TDeserializer) + defer t.pool.Put(d) + return d.ReadString(ctx, msg, s) +} + +func (t *TDeserializerPool) Read(ctx context.Context, msg TStruct, b []byte) error { + d := t.pool.Get().(*TDeserializer) + defer t.pool.Put(d) + return d.Read(ctx, msg, b) +} diff --git a/lib/go/thrift/example_client_middleware_test.go b/lib/go/thrift/example_client_middleware_test.go new file mode 100644 index 00000000000..a68cdbc348b --- /dev/null +++ b/lib/go/thrift/example_client_middleware_test.go @@ -0,0 +1,77 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package thrift + +import ( + "context" + "log" +) + +// BEGIN THRIFT GENERATED CODE SECTION +// +// In real code this section should be from thrift generated code instead, +// but for this example we just define some placeholders here. + +type MyEndpointRequest struct{} + +type MyEndpointResponse struct{} + +type MyService interface { + MyEndpoint(ctx context.Context, req *MyEndpointRequest) (*MyEndpointResponse, error) +} + +func NewMyServiceClient(_ TClient) MyService { + // In real code this certainly won't return nil. + return nil +} + +// END THRIFT GENERATED CODE SECTION + +func simpleClientLoggingMiddleware(next TClient) TClient { + return WrappedTClient{ + Wrapped: func(ctx context.Context, method string, args, result TStruct) (ResponseMeta, error) { + log.Printf("Before: %q", method) + log.Printf("Args: %#v", args) + headers, err := next.Call(ctx, method, args, result) + log.Printf("After: %q", method) + log.Printf("Result: %#v", result) + if err != nil { + log.Printf("Error: %v", err) + } + return headers, err + }, + } +} + +// This example demonstrates how to define and use a simple logging middleware +// to your thrift client. +func ExampleClientMiddleware() { + var ( + trans TTransport + protoFactory TProtocolFactory + ) + var client TClient + client = NewTStandardClient( + protoFactory.GetProtocol(trans), + protoFactory.GetProtocol(trans), + ) + client = WrapClient(client, simpleClientLoggingMiddleware) + myServiceClient := NewMyServiceClient(client) + myServiceClient.MyEndpoint(context.Background(), &MyEndpointRequest{}) +} diff --git a/lib/go/thrift/example_processor_middleware_test.go b/lib/go/thrift/example_processor_middleware_test.go new file mode 100644 index 00000000000..724a4f25cff --- /dev/null +++ b/lib/go/thrift/example_processor_middleware_test.go @@ -0,0 +1,54 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package thrift + +import ( + "context" + "log" +) + +func SimpleProcessorLoggingMiddleware(name string, next TProcessorFunction) TProcessorFunction { + return WrappedTProcessorFunction{ + Wrapped: func(ctx context.Context, seqId int32, in, out TProtocol) (bool, TException) { + log.Printf("Before: %q", name) + success, err := next.Process(ctx, seqId, in, out) + log.Printf("After: %q", name) + log.Printf("Success: %v", success) + if err != nil { + log.Printf("Error: %v", err) + } + return success, err + }, + } +} + +// This example demonstrates how to define and use a simple logging middleware +// to your thrift server/processor. +func ExampleProcessorMiddleware() { + var ( + processor TProcessor + trans TServerTransport + transFactory TTransportFactory + protoFactory TProtocolFactory + ) + processor = WrapProcessor(processor, SimpleProcessorLoggingMiddleware) + server := NewTSimpleServer4(processor, trans, transFactory, protoFactory) + log.Fatal(server.Serve()) +} diff --git a/lib/go/thrift/exception.go b/lib/go/thrift/exception.go index ea8d6f66114..53bf862ea5b 100644 --- a/lib/go/thrift/exception.go +++ b/lib/go/thrift/exception.go @@ -26,19 +26,91 @@ import ( // Generic Thrift exception type TException interface { error + + TExceptionType() TExceptionType } // Prepends additional information to an error without losing the Thrift exception interface func PrependError(prepend string, err error) error { - if t, ok := err.(TTransportException); ok { - return NewTTransportException(t.TypeId(), prepend+t.Error()) + msg := prepend + err.Error() + + var te TException + if errors.As(err, &te) { + switch te.TExceptionType() { + case TExceptionTypeTransport: + if t, ok := err.(TTransportException); ok { + return prependTTransportException(prepend, t) + } + case TExceptionTypeProtocol: + if t, ok := err.(TProtocolException); ok { + return prependTProtocolException(prepend, t) + } + case TExceptionTypeApplication: + var t TApplicationException + if errors.As(err, &t) { + return NewTApplicationException(t.TypeId(), msg) + } + } + + return wrappedTException{ + err: err, + msg: msg, + tExceptionType: te.TExceptionType(), + } + } + + return errors.New(msg) +} + +// TExceptionType is an enum type to categorize different "subclasses" of TExceptions. +type TExceptionType byte + +// TExceptionType values +const ( + TExceptionTypeUnknown TExceptionType = iota + TExceptionTypeCompiled // TExceptions defined in thrift files and generated by thrift compiler + TExceptionTypeApplication // TApplicationExceptions + TExceptionTypeProtocol // TProtocolExceptions + TExceptionTypeTransport // TTransportExceptions +) + +// WrapTException wraps an error into TException. +// +// If err is nil or already TException, it's returned as-is. +// Otherwise it will be wraped into TException with TExceptionType() returning +// TExceptionTypeUnknown, and Unwrap() returning the original error. +func WrapTException(err error) TException { + if err == nil { + return nil } - if t, ok := err.(TProtocolException); ok { - return NewTProtocolExceptionWithType(t.TypeId(), errors.New(prepend+err.Error())) + + if te, ok := err.(TException); ok { + return te } - if t, ok := err.(TApplicationException); ok { - return NewTApplicationException(t.TypeId(), prepend+t.Error()) + + return wrappedTException{ + err: err, + msg: err.Error(), + tExceptionType: TExceptionTypeUnknown, } +} + +type wrappedTException struct { + err error + msg string + tExceptionType TExceptionType +} - return errors.New(prepend + err.Error()) +func (w wrappedTException) Error() string { + return w.msg } + +func (w wrappedTException) TExceptionType() TExceptionType { + return w.tExceptionType +} + +func (w wrappedTException) Unwrap() error { + return w.err +} + +var _ TException = wrappedTException{} diff --git a/lib/go/thrift/field.go b/lib/go/thrift/field.go deleted file mode 100644 index 9d665255097..00000000000 --- a/lib/go/thrift/field.go +++ /dev/null @@ -1,79 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -package thrift - -// Helper class that encapsulates field metadata. -type field struct { - name string - typeId TType - id int -} - -func newField(n string, t TType, i int) *field { - return &field{name: n, typeId: t, id: i} -} - -func (p *field) Name() string { - if p == nil { - return "" - } - return p.name -} - -func (p *field) TypeId() TType { - if p == nil { - return TType(VOID) - } - return p.typeId -} - -func (p *field) Id() int { - if p == nil { - return -1 - } - return p.id -} - -func (p *field) String() string { - if p == nil { - return "" - } - return "" -} - -var ANONYMOUS_FIELD *field - -type fieldSlice []field - -func (p fieldSlice) Len() int { - return len(p) -} - -func (p fieldSlice) Less(i, j int) bool { - return p[i].Id() < p[j].Id() -} - -func (p fieldSlice) Swap(i, j int) { - p[i], p[j] = p[j], p[i] -} - -func init() { - ANONYMOUS_FIELD = newField("", STOP, 0) -} diff --git a/lib/go/thrift/framed_transport.go b/lib/go/thrift/framed_transport.go index 81fa65aaae5..f683e7f544b 100644 --- a/lib/go/thrift/framed_transport.go +++ b/lib/go/thrift/framed_transport.go @@ -28,44 +28,92 @@ import ( "io" ) +// Deprecated: Use DEFAULT_MAX_FRAME_SIZE instead. const DEFAULT_MAX_LENGTH = 16384000 type TFramedTransport struct { transport TTransport - buf bytes.Buffer - reader *bufio.Reader - frameSize uint32 //Current remaining size of the frame. if ==0 read next frame header - buffer [4]byte - maxLength uint32 + + cfg *TConfiguration + + writeBuf bytes.Buffer + + reader *bufio.Reader + readBuf bytes.Buffer + + buffer [4]byte } type tFramedTransportFactory struct { - factory TTransportFactory - maxLength uint32 + factory TTransportFactory + cfg *TConfiguration } +// Deprecated: Use NewTFramedTransportFactoryConf instead. func NewTFramedTransportFactory(factory TTransportFactory) TTransportFactory { - return &tFramedTransportFactory{factory: factory, maxLength: DEFAULT_MAX_LENGTH} + return NewTFramedTransportFactoryConf(factory, &TConfiguration{ + MaxFrameSize: DEFAULT_MAX_LENGTH, + + noPropagation: true, + }) } +// Deprecated: Use NewTFramedTransportFactoryConf instead. func NewTFramedTransportFactoryMaxLength(factory TTransportFactory, maxLength uint32) TTransportFactory { - return &tFramedTransportFactory{factory: factory, maxLength: maxLength} + return NewTFramedTransportFactoryConf(factory, &TConfiguration{ + MaxFrameSize: int32(maxLength), + + noPropagation: true, + }) +} + +func NewTFramedTransportFactoryConf(factory TTransportFactory, conf *TConfiguration) TTransportFactory { + PropagateTConfiguration(factory, conf) + return &tFramedTransportFactory{ + factory: factory, + cfg: conf, + } } func (p *tFramedTransportFactory) GetTransport(base TTransport) (TTransport, error) { + PropagateTConfiguration(base, p.cfg) tt, err := p.factory.GetTransport(base) if err != nil { return nil, err } - return NewTFramedTransportMaxLength(tt, p.maxLength), nil + return NewTFramedTransportConf(tt, p.cfg), nil +} + +func (p *tFramedTransportFactory) SetTConfiguration(cfg *TConfiguration) { + PropagateTConfiguration(p.factory, cfg) + p.cfg = cfg } +// Deprecated: Use NewTFramedTransportConf instead. func NewTFramedTransport(transport TTransport) *TFramedTransport { - return &TFramedTransport{transport: transport, reader: bufio.NewReader(transport), maxLength: DEFAULT_MAX_LENGTH} + return NewTFramedTransportConf(transport, &TConfiguration{ + MaxFrameSize: DEFAULT_MAX_LENGTH, + + noPropagation: true, + }) } +// Deprecated: Use NewTFramedTransportConf instead. func NewTFramedTransportMaxLength(transport TTransport, maxLength uint32) *TFramedTransport { - return &TFramedTransport{transport: transport, reader: bufio.NewReader(transport), maxLength: maxLength} + return NewTFramedTransportConf(transport, &TConfiguration{ + MaxFrameSize: int32(maxLength), + + noPropagation: true, + }) +} + +func NewTFramedTransportConf(transport TTransport, conf *TConfiguration) *TFramedTransport { + PropagateTConfiguration(transport, conf) + return &TFramedTransport{ + transport: transport, + reader: bufio.NewReader(transport), + cfg: conf, + } } func (p *TFramedTransport) Open() error { @@ -80,75 +128,65 @@ func (p *TFramedTransport) Close() error { return p.transport.Close() } -func (p *TFramedTransport) Read(buf []byte) (l int, err error) { - if p.frameSize == 0 { - p.frameSize, err = p.readFrameHeader() - if err != nil { - return - } +func (p *TFramedTransport) Read(buf []byte) (read int, err error) { + read, err = p.readBuf.Read(buf) + if err != io.EOF { + return } - if p.frameSize < uint32(len(buf)) { - frameSize := p.frameSize - tmp := make([]byte, p.frameSize) - l, err = p.Read(tmp) - copy(buf, tmp) - if err == nil { - err = NewTTransportExceptionFromError(fmt.Errorf("Not enough frame size %d to read %d bytes", frameSize, len(buf))) - return - } + + // For bytes.Buffer.Read, EOF would only happen when read is zero, + // but still, do a sanity check, + // in case that behavior is changed in a future version of go stdlib. + // When that happens, just return nil error, + // and let the caller call Read again to read the next frame. + if read > 0 { + return read, nil } - got, err := p.reader.Read(buf) - p.frameSize = p.frameSize - uint32(got) - //sanity check - if p.frameSize < 0 { - return 0, NewTTransportException(UNKNOWN_TRANSPORT_EXCEPTION, "Negative frame size") + + // Reaching here means that the last Read finished the last frame, + // so we need to read the next frame into readBuf now. + if err = p.readFrame(); err != nil { + return read, err } - return got, NewTTransportExceptionFromError(err) + newRead, err := p.Read(buf[read:]) + return read + newRead, err } func (p *TFramedTransport) ReadByte() (c byte, err error) { - if p.frameSize == 0 { - p.frameSize, err = p.readFrameHeader() - if err != nil { - return - } - } - if p.frameSize < 1 { - return 0, NewTTransportExceptionFromError(fmt.Errorf("Not enough frame size %d to read %d bytes", p.frameSize, 1)) - } - c, err = p.reader.ReadByte() - if err == nil { - p.frameSize-- + buf := p.buffer[:1] + _, err = p.Read(buf) + if err != nil { + return } + c = buf[0] return } func (p *TFramedTransport) Write(buf []byte) (int, error) { - n, err := p.buf.Write(buf) + n, err := p.writeBuf.Write(buf) return n, NewTTransportExceptionFromError(err) } func (p *TFramedTransport) WriteByte(c byte) error { - return p.buf.WriteByte(c) + return p.writeBuf.WriteByte(c) } func (p *TFramedTransport) WriteString(s string) (n int, err error) { - return p.buf.WriteString(s) + return p.writeBuf.WriteString(s) } func (p *TFramedTransport) Flush(ctx context.Context) error { - size := p.buf.Len() + size := p.writeBuf.Len() buf := p.buffer[:4] binary.BigEndian.PutUint32(buf, uint32(size)) _, err := p.transport.Write(buf) if err != nil { - p.buf.Truncate(0) + p.writeBuf.Reset() return NewTTransportExceptionFromError(err) } if size > 0 { - if n, err := p.buf.WriteTo(p.transport); err != nil { - print("Error while flushing write buffer of size ", size, " to transport, only wrote ", n, " bytes: ", err.Error(), "\n") - p.buf.Truncate(0) + if _, err := io.Copy(p.transport, &p.writeBuf); err != nil { + p.writeBuf.Reset() return NewTTransportExceptionFromError(err) } } @@ -156,18 +194,30 @@ func (p *TFramedTransport) Flush(ctx context.Context) error { return NewTTransportExceptionFromError(err) } -func (p *TFramedTransport) readFrameHeader() (uint32, error) { +func (p *TFramedTransport) readFrame() error { buf := p.buffer[:4] if _, err := io.ReadFull(p.reader, buf); err != nil { - return 0, err + return err } size := binary.BigEndian.Uint32(buf) - if size < 0 || size > p.maxLength { - return 0, NewTTransportException(UNKNOWN_TRANSPORT_EXCEPTION, fmt.Sprintf("Incorrect frame size (%d)", size)) + if size < 0 || size > uint32(p.cfg.GetMaxFrameSize()) { + return NewTTransportException(UNKNOWN_TRANSPORT_EXCEPTION, fmt.Sprintf("Incorrect frame size (%d)", size)) } - return size, nil + _, err := io.CopyN(&p.readBuf, p.reader, int64(size)) + return NewTTransportExceptionFromError(err) } func (p *TFramedTransport) RemainingBytes() (num_bytes uint64) { - return uint64(p.frameSize) + return uint64(p.readBuf.Len()) } + +// SetTConfiguration implements TConfigurationSetter. +func (p *TFramedTransport) SetTConfiguration(cfg *TConfiguration) { + PropagateTConfiguration(p.transport, cfg) + p.cfg = cfg +} + +var ( + _ TConfigurationSetter = (*tFramedTransportFactory)(nil) + _ TConfigurationSetter = (*TFramedTransport)(nil) +) diff --git a/lib/go/thrift/header_context.go b/lib/go/thrift/header_context.go new file mode 100644 index 00000000000..ac9bd4882be --- /dev/null +++ b/lib/go/thrift/header_context.go @@ -0,0 +1,110 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package thrift + +import ( + "context" +) + +// See https://godoc.org/context#WithValue on why do we need the unexported typedefs. +type ( + headerKey string + headerKeyList int +) + +// Values for headerKeyList. +const ( + headerKeyListRead headerKeyList = iota + headerKeyListWrite +) + +// SetHeader sets a header in the context. +func SetHeader(ctx context.Context, key, value string) context.Context { + return context.WithValue( + ctx, + headerKey(key), + value, + ) +} + +// UnsetHeader unsets a previously set header in the context. +func UnsetHeader(ctx context.Context, key string) context.Context { + return context.WithValue( + ctx, + headerKey(key), + nil, + ) +} + +// GetHeader returns a value of the given header from the context. +func GetHeader(ctx context.Context, key string) (value string, ok bool) { + if v := ctx.Value(headerKey(key)); v != nil { + value, ok = v.(string) + } + return +} + +// SetReadHeaderList sets the key list of read THeaders in the context. +func SetReadHeaderList(ctx context.Context, keys []string) context.Context { + return context.WithValue( + ctx, + headerKeyListRead, + keys, + ) +} + +// GetReadHeaderList returns the key list of read THeaders from the context. +func GetReadHeaderList(ctx context.Context) []string { + if v := ctx.Value(headerKeyListRead); v != nil { + if value, ok := v.([]string); ok { + return value + } + } + return nil +} + +// SetWriteHeaderList sets the key list of THeaders to write in the context. +func SetWriteHeaderList(ctx context.Context, keys []string) context.Context { + return context.WithValue( + ctx, + headerKeyListWrite, + keys, + ) +} + +// GetWriteHeaderList returns the key list of THeaders to write from the context. +func GetWriteHeaderList(ctx context.Context) []string { + if v := ctx.Value(headerKeyListWrite); v != nil { + if value, ok := v.([]string); ok { + return value + } + } + return nil +} + +// AddReadTHeaderToContext adds the whole THeader headers into context. +func AddReadTHeaderToContext(ctx context.Context, headers THeaderMap) context.Context { + keys := make([]string, 0, len(headers)) + for key, value := range headers { + ctx = SetHeader(ctx, key, value) + keys = append(keys, key) + } + return SetReadHeaderList(ctx, keys) +} diff --git a/lib/go/thrift/header_context_test.go b/lib/go/thrift/header_context_test.go new file mode 100644 index 00000000000..16b10c85898 --- /dev/null +++ b/lib/go/thrift/header_context_test.go @@ -0,0 +1,139 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package thrift + +import ( + "context" + "reflect" + "testing" +) + +func TestSetGetUnsetHeader(t *testing.T) { + const ( + key = "foo" + value = "bar" + ) + ctx := context.Background() + + ctx = SetHeader(ctx, key, value) + + checkGet := func(t *testing.T, ctx context.Context) { + t.Helper() + got, ok := GetHeader(ctx, key) + if !ok { + t.Fatalf("Cannot get header %q back after setting it.", key) + } + if got != value { + t.Fatalf("Header value expected %q, got %q instead", value, got) + } + } + + checkGet(t, ctx) + + t.Run( + "NoConflicts", + func(t *testing.T) { + type otherType string + const otherValue = "bar2" + + ctx = context.WithValue(ctx, otherType(key), otherValue) + checkGet(t, ctx) + }, + ) + + t.Run( + "GetHeaderOnNonExistKey", + func(t *testing.T) { + const otherKey = "foo2" + + if _, ok := GetHeader(ctx, otherKey); ok { + t.Errorf("GetHeader returned ok on non-existing key %q", otherKey) + } + }, + ) + + t.Run( + "Unset", + func(t *testing.T) { + ctx := UnsetHeader(ctx, key) + + if _, ok := GetHeader(ctx, key); ok { + t.Errorf("GetHeader returned ok on unset key %q", key) + } + }, + ) +} + +func TestReadKeyList(t *testing.T) { + headers := THeaderMap{ + "key1": "value1", + "key2": "value2", + } + ctx := context.Background() + + ctx = AddReadTHeaderToContext(ctx, headers) + + got := make(THeaderMap) + keys := GetReadHeaderList(ctx) + t.Logf("keys: %+v", keys) + for _, key := range keys { + value, ok := GetHeader(ctx, key) + if ok { + got[key] = value + } else { + t.Errorf("Cannot get key %q from context", key) + } + } + + if !reflect.DeepEqual(headers, got) { + t.Errorf("Expected header map %+v, got %+v", headers, got) + } + + writtenKeys := GetWriteHeaderList(ctx) + if len(writtenKeys) > 0 { + t.Errorf( + "Expected empty GetWriteHeaderList() result, got %+v", + writtenKeys, + ) + } +} + +func TestWriteKeyList(t *testing.T) { + keys := []string{ + "key1", + "key2", + } + ctx := context.Background() + + ctx = SetWriteHeaderList(ctx, keys) + got := GetWriteHeaderList(ctx) + + if !reflect.DeepEqual(keys, got) { + t.Errorf("Expected header keys %+v, got %+v", keys, got) + } + + readKeys := GetReadHeaderList(ctx) + if len(readKeys) > 0 { + t.Errorf( + "Expected empty GetReadHeaderList() result, got %+v", + readKeys, + ) + } +} diff --git a/lib/go/thrift/header_protocol.go b/lib/go/thrift/header_protocol.go new file mode 100644 index 00000000000..878041f8df1 --- /dev/null +++ b/lib/go/thrift/header_protocol.go @@ -0,0 +1,351 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package thrift + +import ( + "context" + "errors" +) + +// THeaderProtocol is a thrift protocol that implements THeader: +// https://github.com/apache/thrift/blob/master/doc/specs/HeaderFormat.md +// +// It supports either binary or compact protocol as the wrapped protocol. +// +// Most of the THeader handlings are happening inside THeaderTransport. +type THeaderProtocol struct { + transport *THeaderTransport + + // Will be initialized on first read/write. + protocol TProtocol + + cfg *TConfiguration +} + +// Deprecated: Use NewTHeaderProtocolConf instead. +func NewTHeaderProtocol(trans TTransport) *THeaderProtocol { + return newTHeaderProtocolConf(trans, &TConfiguration{ + noPropagation: true, + }) +} + +// NewTHeaderProtocolConf creates a new THeaderProtocol from the underlying +// transport with given TConfiguration. +// +// The passed in transport will be wrapped with THeaderTransport. +// +// Note that THeaderTransport handles frame and zlib by itself, +// so the underlying transport should be a raw socket transports (TSocket or TSSLSocket), +// instead of rich transports like TZlibTransport or TFramedTransport. +func NewTHeaderProtocolConf(trans TTransport, conf *TConfiguration) *THeaderProtocol { + return newTHeaderProtocolConf(trans, conf) +} + +func newTHeaderProtocolConf(trans TTransport, cfg *TConfiguration) *THeaderProtocol { + t := NewTHeaderTransportConf(trans, cfg) + p, _ := t.cfg.GetTHeaderProtocolID().GetProtocol(t) + PropagateTConfiguration(p, cfg) + return &THeaderProtocol{ + transport: t, + protocol: p, + cfg: cfg, + } +} + +type tHeaderProtocolFactory struct { + cfg *TConfiguration +} + +func (f tHeaderProtocolFactory) GetProtocol(trans TTransport) TProtocol { + return newTHeaderProtocolConf(trans, f.cfg) +} + +func (f *tHeaderProtocolFactory) SetTConfiguration(cfg *TConfiguration) { + f.cfg = cfg +} + +// Deprecated: Use NewTHeaderProtocolFactoryConf instead. +func NewTHeaderProtocolFactory() TProtocolFactory { + return NewTHeaderProtocolFactoryConf(&TConfiguration{ + noPropagation: true, + }) +} + +// NewTHeaderProtocolFactoryConf creates a factory for THeader with given +// TConfiguration. +func NewTHeaderProtocolFactoryConf(conf *TConfiguration) TProtocolFactory { + return tHeaderProtocolFactory{ + cfg: conf, + } +} + +// Transport returns the underlying transport. +// +// It's guaranteed to be of type *THeaderTransport. +func (p *THeaderProtocol) Transport() TTransport { + return p.transport +} + +// GetReadHeaders returns the THeaderMap read from transport. +func (p *THeaderProtocol) GetReadHeaders() THeaderMap { + return p.transport.GetReadHeaders() +} + +// SetWriteHeader sets a header for write. +func (p *THeaderProtocol) SetWriteHeader(key, value string) { + p.transport.SetWriteHeader(key, value) +} + +// ClearWriteHeaders clears all write headers previously set. +func (p *THeaderProtocol) ClearWriteHeaders() { + p.transport.ClearWriteHeaders() +} + +// AddTransform add a transform for writing. +func (p *THeaderProtocol) AddTransform(transform THeaderTransformID) error { + return p.transport.AddTransform(transform) +} + +func (p *THeaderProtocol) Flush(ctx context.Context) error { + return p.transport.Flush(ctx) +} + +func (p *THeaderProtocol) WriteMessageBegin(ctx context.Context, name string, typeID TMessageType, seqID int32) error { + newProto, err := p.transport.Protocol().GetProtocol(p.transport) + if err != nil { + return err + } + PropagateTConfiguration(newProto, p.cfg) + p.protocol = newProto + p.transport.SequenceID = seqID + return p.protocol.WriteMessageBegin(ctx, name, typeID, seqID) +} + +func (p *THeaderProtocol) WriteMessageEnd(ctx context.Context) error { + if err := p.protocol.WriteMessageEnd(ctx); err != nil { + return err + } + return p.transport.Flush(ctx) +} + +func (p *THeaderProtocol) WriteStructBegin(ctx context.Context, name string) error { + return p.protocol.WriteStructBegin(ctx, name) +} + +func (p *THeaderProtocol) WriteStructEnd(ctx context.Context) error { + return p.protocol.WriteStructEnd(ctx) +} + +func (p *THeaderProtocol) WriteFieldBegin(ctx context.Context, name string, typeID TType, id int16) error { + return p.protocol.WriteFieldBegin(ctx, name, typeID, id) +} + +func (p *THeaderProtocol) WriteFieldEnd(ctx context.Context) error { + return p.protocol.WriteFieldEnd(ctx) +} + +func (p *THeaderProtocol) WriteFieldStop(ctx context.Context) error { + return p.protocol.WriteFieldStop(ctx) +} + +func (p *THeaderProtocol) WriteMapBegin(ctx context.Context, keyType TType, valueType TType, size int) error { + return p.protocol.WriteMapBegin(ctx, keyType, valueType, size) +} + +func (p *THeaderProtocol) WriteMapEnd(ctx context.Context) error { + return p.protocol.WriteMapEnd(ctx) +} + +func (p *THeaderProtocol) WriteListBegin(ctx context.Context, elemType TType, size int) error { + return p.protocol.WriteListBegin(ctx, elemType, size) +} + +func (p *THeaderProtocol) WriteListEnd(ctx context.Context) error { + return p.protocol.WriteListEnd(ctx) +} + +func (p *THeaderProtocol) WriteSetBegin(ctx context.Context, elemType TType, size int) error { + return p.protocol.WriteSetBegin(ctx, elemType, size) +} + +func (p *THeaderProtocol) WriteSetEnd(ctx context.Context) error { + return p.protocol.WriteSetEnd(ctx) +} + +func (p *THeaderProtocol) WriteBool(ctx context.Context, value bool) error { + return p.protocol.WriteBool(ctx, value) +} + +func (p *THeaderProtocol) WriteByte(ctx context.Context, value int8) error { + return p.protocol.WriteByte(ctx, value) +} + +func (p *THeaderProtocol) WriteI16(ctx context.Context, value int16) error { + return p.protocol.WriteI16(ctx, value) +} + +func (p *THeaderProtocol) WriteI32(ctx context.Context, value int32) error { + return p.protocol.WriteI32(ctx, value) +} + +func (p *THeaderProtocol) WriteI64(ctx context.Context, value int64) error { + return p.protocol.WriteI64(ctx, value) +} + +func (p *THeaderProtocol) WriteDouble(ctx context.Context, value float64) error { + return p.protocol.WriteDouble(ctx, value) +} + +func (p *THeaderProtocol) WriteString(ctx context.Context, value string) error { + return p.protocol.WriteString(ctx, value) +} + +func (p *THeaderProtocol) WriteBinary(ctx context.Context, value []byte) error { + return p.protocol.WriteBinary(ctx, value) +} + +// ReadFrame calls underlying THeaderTransport's ReadFrame function. +func (p *THeaderProtocol) ReadFrame(ctx context.Context) error { + return p.transport.ReadFrame(ctx) +} + +func (p *THeaderProtocol) ReadMessageBegin(ctx context.Context) (name string, typeID TMessageType, seqID int32, err error) { + if err = p.transport.ReadFrame(ctx); err != nil { + return + } + + var newProto TProtocol + newProto, err = p.transport.Protocol().GetProtocol(p.transport) + if err != nil { + var tAppExc TApplicationException + if !errors.As(err, &tAppExc) { + return + } + if e := p.protocol.WriteMessageBegin(ctx, "", EXCEPTION, seqID); e != nil { + return + } + if e := tAppExc.Write(ctx, p.protocol); e != nil { + return + } + if e := p.protocol.WriteMessageEnd(ctx); e != nil { + return + } + if e := p.transport.Flush(ctx); e != nil { + return + } + return + } + PropagateTConfiguration(newProto, p.cfg) + p.protocol = newProto + + return p.protocol.ReadMessageBegin(ctx) +} + +func (p *THeaderProtocol) ReadMessageEnd(ctx context.Context) error { + return p.protocol.ReadMessageEnd(ctx) +} + +func (p *THeaderProtocol) ReadStructBegin(ctx context.Context) (name string, err error) { + return p.protocol.ReadStructBegin(ctx) +} + +func (p *THeaderProtocol) ReadStructEnd(ctx context.Context) error { + return p.protocol.ReadStructEnd(ctx) +} + +func (p *THeaderProtocol) ReadFieldBegin(ctx context.Context) (name string, typeID TType, id int16, err error) { + return p.protocol.ReadFieldBegin(ctx) +} + +func (p *THeaderProtocol) ReadFieldEnd(ctx context.Context) error { + return p.protocol.ReadFieldEnd(ctx) +} + +func (p *THeaderProtocol) ReadMapBegin(ctx context.Context) (keyType TType, valueType TType, size int, err error) { + return p.protocol.ReadMapBegin(ctx) +} + +func (p *THeaderProtocol) ReadMapEnd(ctx context.Context) error { + return p.protocol.ReadMapEnd(ctx) +} + +func (p *THeaderProtocol) ReadListBegin(ctx context.Context) (elemType TType, size int, err error) { + return p.protocol.ReadListBegin(ctx) +} + +func (p *THeaderProtocol) ReadListEnd(ctx context.Context) error { + return p.protocol.ReadListEnd(ctx) +} + +func (p *THeaderProtocol) ReadSetBegin(ctx context.Context) (elemType TType, size int, err error) { + return p.protocol.ReadSetBegin(ctx) +} + +func (p *THeaderProtocol) ReadSetEnd(ctx context.Context) error { + return p.protocol.ReadSetEnd(ctx) +} + +func (p *THeaderProtocol) ReadBool(ctx context.Context) (value bool, err error) { + return p.protocol.ReadBool(ctx) +} + +func (p *THeaderProtocol) ReadByte(ctx context.Context) (value int8, err error) { + return p.protocol.ReadByte(ctx) +} + +func (p *THeaderProtocol) ReadI16(ctx context.Context) (value int16, err error) { + return p.protocol.ReadI16(ctx) +} + +func (p *THeaderProtocol) ReadI32(ctx context.Context) (value int32, err error) { + return p.protocol.ReadI32(ctx) +} + +func (p *THeaderProtocol) ReadI64(ctx context.Context) (value int64, err error) { + return p.protocol.ReadI64(ctx) +} + +func (p *THeaderProtocol) ReadDouble(ctx context.Context) (value float64, err error) { + return p.protocol.ReadDouble(ctx) +} + +func (p *THeaderProtocol) ReadString(ctx context.Context) (value string, err error) { + return p.protocol.ReadString(ctx) +} + +func (p *THeaderProtocol) ReadBinary(ctx context.Context) (value []byte, err error) { + return p.protocol.ReadBinary(ctx) +} + +func (p *THeaderProtocol) Skip(ctx context.Context, fieldType TType) error { + return p.protocol.Skip(ctx, fieldType) +} + +// SetTConfiguration implements TConfigurationSetter. +func (p *THeaderProtocol) SetTConfiguration(cfg *TConfiguration) { + PropagateTConfiguration(p.transport, cfg) + PropagateTConfiguration(p.protocol, cfg) + p.cfg = cfg +} + +var ( + _ TConfigurationSetter = (*tHeaderProtocolFactory)(nil) + _ TConfigurationSetter = (*THeaderProtocol)(nil) +) diff --git a/lib/go/thrift/header_protocol_test.go b/lib/go/thrift/header_protocol_test.go new file mode 100644 index 00000000000..48a69bf23b0 --- /dev/null +++ b/lib/go/thrift/header_protocol_test.go @@ -0,0 +1,42 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package thrift + +import ( + "testing" +) + +func TestReadWriteHeaderProtocol(t *testing.T) { + t.Run( + "default", + func(t *testing.T) { + ReadWriteProtocolTest(t, NewTHeaderProtocolFactory()) + }, + ) + + t.Run( + "compact", + func(t *testing.T) { + ReadWriteProtocolTest(t, NewTHeaderProtocolFactoryConf(&TConfiguration{ + THeaderProtocolID: THeaderProtocolIDPtrMust(THeaderProtocolCompact), + })) + }, + ) +} diff --git a/lib/go/thrift/header_transport.go b/lib/go/thrift/header_transport.go new file mode 100644 index 00000000000..f5736df4276 --- /dev/null +++ b/lib/go/thrift/header_transport.go @@ -0,0 +1,810 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package thrift + +import ( + "bufio" + "bytes" + "compress/zlib" + "context" + "encoding/binary" + "errors" + "fmt" + "io" + "io/ioutil" +) + +// Size in bytes for 32-bit ints. +const size32 = 4 + +type headerMeta struct { + MagicFlags uint32 + SequenceID int32 + HeaderLength uint16 +} + +const headerMetaSize = 10 + +type clientType int + +const ( + clientUnknown clientType = iota + clientHeaders + clientFramedBinary + clientUnframedBinary + clientFramedCompact + clientUnframedCompact +) + +// Constants defined in THeader format: +// https://github.com/apache/thrift/blob/master/doc/specs/HeaderFormat.md +const ( + THeaderHeaderMagic uint32 = 0x0fff0000 + THeaderHeaderMask uint32 = 0xffff0000 + THeaderFlagsMask uint32 = 0x0000ffff + THeaderMaxFrameSize uint32 = 0x3fffffff +) + +// THeaderMap is the type of the header map in THeader transport. +type THeaderMap map[string]string + +// THeaderProtocolID is the wrapped protocol id used in THeader. +type THeaderProtocolID int32 + +// Supported THeaderProtocolID values. +const ( + THeaderProtocolBinary THeaderProtocolID = 0x00 + THeaderProtocolCompact THeaderProtocolID = 0x02 + THeaderProtocolDefault = THeaderProtocolBinary +) + +// Declared globally to avoid repetitive allocations, not really used. +var globalMemoryBuffer = NewTMemoryBuffer() + +// Validate checks whether the THeaderProtocolID is a valid/supported one. +func (id THeaderProtocolID) Validate() error { + _, err := id.GetProtocol(globalMemoryBuffer) + return err +} + +// GetProtocol gets the corresponding TProtocol from the wrapped protocol id. +func (id THeaderProtocolID) GetProtocol(trans TTransport) (TProtocol, error) { + switch id { + default: + return nil, NewTApplicationException( + INVALID_PROTOCOL, + fmt.Sprintf("THeader protocol id %d not supported", id), + ) + case THeaderProtocolBinary: + return NewTBinaryProtocolTransport(trans), nil + case THeaderProtocolCompact: + return NewTCompactProtocol(trans), nil + } +} + +// THeaderTransformID defines the numeric id of the transform used. +type THeaderTransformID int32 + +// THeaderTransformID values. +// +// Values not defined here are not currently supported, namely HMAC and Snappy. +const ( + TransformNone THeaderTransformID = iota // 0, no special handling + TransformZlib // 1, zlib +) + +var supportedTransformIDs = map[THeaderTransformID]bool{ + TransformNone: true, + TransformZlib: true, +} + +// TransformReader is an io.ReadCloser that handles transforms reading. +type TransformReader struct { + io.Reader + + closers []io.Closer +} + +var _ io.ReadCloser = (*TransformReader)(nil) + +// NewTransformReaderWithCapacity initializes a TransformReader with expected +// closers capacity. +// +// If you don't know the closers capacity beforehand, just use +// +// &TransformReader{Reader: baseReader} +// +// instead would be sufficient. +func NewTransformReaderWithCapacity(baseReader io.Reader, capacity int) *TransformReader { + return &TransformReader{ + Reader: baseReader, + closers: make([]io.Closer, 0, capacity), + } +} + +// Close calls the underlying closers in appropriate order, +// stops at and returns the first error encountered. +func (tr *TransformReader) Close() error { + // Call closers in reversed order + for i := len(tr.closers) - 1; i >= 0; i-- { + if err := tr.closers[i].Close(); err != nil { + return err + } + } + return nil +} + +// AddTransform adds a transform. +func (tr *TransformReader) AddTransform(id THeaderTransformID) error { + switch id { + default: + return NewTApplicationException( + INVALID_TRANSFORM, + fmt.Sprintf("THeaderTransformID %d not supported", id), + ) + case TransformNone: + // no-op + case TransformZlib: + readCloser, err := zlib.NewReader(tr.Reader) + if err != nil { + return err + } + tr.Reader = readCloser + tr.closers = append(tr.closers, readCloser) + } + return nil +} + +// TransformWriter is an io.WriteCloser that handles transforms writing. +type TransformWriter struct { + io.Writer + + closers []io.Closer +} + +var _ io.WriteCloser = (*TransformWriter)(nil) + +// NewTransformWriter creates a new TransformWriter with base writer and transforms. +func NewTransformWriter(baseWriter io.Writer, transforms []THeaderTransformID) (io.WriteCloser, error) { + writer := &TransformWriter{ + Writer: baseWriter, + closers: make([]io.Closer, 0, len(transforms)), + } + for _, id := range transforms { + if err := writer.AddTransform(id); err != nil { + return nil, err + } + } + return writer, nil +} + +// Close calls the underlying closers in appropriate order, +// stops at and returns the first error encountered. +func (tw *TransformWriter) Close() error { + // Call closers in reversed order + for i := len(tw.closers) - 1; i >= 0; i-- { + if err := tw.closers[i].Close(); err != nil { + return err + } + } + return nil +} + +// AddTransform adds a transform. +func (tw *TransformWriter) AddTransform(id THeaderTransformID) error { + switch id { + default: + return NewTApplicationException( + INVALID_TRANSFORM, + fmt.Sprintf("THeaderTransformID %d not supported", id), + ) + case TransformNone: + // no-op + case TransformZlib: + writeCloser := zlib.NewWriter(tw.Writer) + tw.Writer = writeCloser + tw.closers = append(tw.closers, writeCloser) + } + return nil +} + +// THeaderInfoType is the type id of the info headers. +type THeaderInfoType int32 + +// Supported THeaderInfoType values. +const ( + _ THeaderInfoType = iota // Skip 0 + InfoKeyValue // 1 + // Rest of the info types are not supported. +) + +// THeaderTransport is a Transport mode that implements THeader. +// +// Note that THeaderTransport handles frame and zlib by itself, +// so the underlying transport should be a raw socket transports (TSocket or TSSLSocket), +// instead of rich transports like TZlibTransport or TFramedTransport. +type THeaderTransport struct { + SequenceID int32 + Flags uint32 + + transport TTransport + + // THeaderMap for read and write + readHeaders THeaderMap + writeHeaders THeaderMap + + // Reading related variables. + reader *bufio.Reader + // When frame is detected, we read the frame fully into frameBuffer. + frameBuffer bytes.Buffer + // When it's non-nil, Read should read from frameReader instead of + // reader, and EOF error indicates end of frame instead of end of all + // transport. + frameReader io.ReadCloser + + // Writing related variables + writeBuffer bytes.Buffer + writeTransforms []THeaderTransformID + + clientType clientType + protocolID THeaderProtocolID + cfg *TConfiguration + + // buffer is used in the following scenarios to avoid repetitive + // allocations, while 4 is big enough for all those scenarios: + // + // * header padding (max size 4) + // * write the frame size (size 4) + buffer [4]byte +} + +var _ TTransport = (*THeaderTransport)(nil) + +// Deprecated: Use NewTHeaderTransportConf instead. +func NewTHeaderTransport(trans TTransport) *THeaderTransport { + return NewTHeaderTransportConf(trans, &TConfiguration{ + noPropagation: true, + }) +} + +// NewTHeaderTransportConf creates THeaderTransport from the +// underlying transport, with given TConfiguration attached. +// +// If trans is already a *THeaderTransport, it will be returned as is, +// but with TConfiguration overridden by the value passed in. +// +// The protocol ID in TConfiguration is only useful for client transports. +// For servers, +// the protocol ID will be overridden again to the one set by the client, +// to ensure that servers always speak the same dialect as the client. +func NewTHeaderTransportConf(trans TTransport, conf *TConfiguration) *THeaderTransport { + if ht, ok := trans.(*THeaderTransport); ok { + ht.SetTConfiguration(conf) + return ht + } + PropagateTConfiguration(trans, conf) + return &THeaderTransport{ + transport: trans, + reader: bufio.NewReader(trans), + writeHeaders: make(THeaderMap), + protocolID: conf.GetTHeaderProtocolID(), + cfg: conf, + } +} + +// Open calls the underlying transport's Open function. +func (t *THeaderTransport) Open() error { + return t.transport.Open() +} + +// IsOpen calls the underlying transport's IsOpen function. +func (t *THeaderTransport) IsOpen() bool { + return t.transport.IsOpen() +} + +// ReadFrame tries to read the frame header, guess the client type, and handle +// unframed clients. +func (t *THeaderTransport) ReadFrame(ctx context.Context) error { + if !t.needReadFrame() { + // No need to read frame, skipping. + return nil + } + + // Peek and handle the first 32 bits. + // They could either be the length field of a framed message, + // or the first bytes of an unframed message. + var buf []byte + var err error + // This is also usually the first read from a connection, + // so handle retries around socket timeouts. + _, deadlineSet := ctx.Deadline() + for { + buf, err = t.reader.Peek(size32) + if deadlineSet && isTimeoutError(err) && ctx.Err() == nil { + // This is I/O timeout and we still have time, + // continue trying + continue + } + // For anything else, do not retry + break + } + if err != nil { + return err + } + + frameSize := binary.BigEndian.Uint32(buf) + if frameSize&VERSION_MASK == VERSION_1 { + t.clientType = clientUnframedBinary + return nil + } + if buf[0] == COMPACT_PROTOCOL_ID && buf[1]&COMPACT_VERSION_MASK == COMPACT_VERSION { + t.clientType = clientUnframedCompact + return nil + } + + // At this point it should be a framed message, + // sanity check on frameSize then discard the peeked part. + if frameSize > THeaderMaxFrameSize || frameSize > uint32(t.cfg.GetMaxFrameSize()) { + return NewTProtocolExceptionWithType( + SIZE_LIMIT, + errors.New("frame too large"), + ) + } + t.reader.Discard(size32) + + // Read the frame fully into frameBuffer. + _, err = io.CopyN(&t.frameBuffer, t.reader, int64(frameSize)) + if err != nil { + return err + } + t.frameReader = ioutil.NopCloser(&t.frameBuffer) + + // Peek and handle the next 32 bits. + buf = t.frameBuffer.Bytes()[:size32] + version := binary.BigEndian.Uint32(buf) + if version&THeaderHeaderMask == THeaderHeaderMagic { + t.clientType = clientHeaders + return t.parseHeaders(ctx, frameSize) + } + if version&VERSION_MASK == VERSION_1 { + t.clientType = clientFramedBinary + return nil + } + if buf[0] == COMPACT_PROTOCOL_ID && buf[1]&COMPACT_VERSION_MASK == COMPACT_VERSION { + t.clientType = clientFramedCompact + return nil + } + if err := t.endOfFrame(); err != nil { + return err + } + return NewTProtocolExceptionWithType( + NOT_IMPLEMENTED, + errors.New("unsupported client transport type"), + ) +} + +// endOfFrame does end of frame handling. +// +// It closes frameReader, and also resets frame related states. +func (t *THeaderTransport) endOfFrame() error { + defer func() { + t.frameBuffer.Reset() + t.frameReader = nil + }() + return t.frameReader.Close() +} + +func (t *THeaderTransport) parseHeaders(ctx context.Context, frameSize uint32) error { + if t.clientType != clientHeaders { + return nil + } + + var err error + var meta headerMeta + if err = binary.Read(&t.frameBuffer, binary.BigEndian, &meta); err != nil { + return err + } + frameSize -= headerMetaSize + t.Flags = meta.MagicFlags & THeaderFlagsMask + t.SequenceID = meta.SequenceID + headerLength := int64(meta.HeaderLength) * 4 + if int64(frameSize) < headerLength { + return NewTProtocolExceptionWithType( + SIZE_LIMIT, + errors.New("header size is larger than the whole frame"), + ) + } + headerBuf := NewTMemoryBuffer() + _, err = io.CopyN(headerBuf, &t.frameBuffer, headerLength) + if err != nil { + return err + } + hp := NewTCompactProtocol(headerBuf) + hp.SetTConfiguration(t.cfg) + + // At this point the header is already read into headerBuf, + // and t.frameBuffer starts from the actual payload. + protoID, err := hp.readVarint32() + if err != nil { + return err + } + t.protocolID = THeaderProtocolID(protoID) + + var transformCount int32 + transformCount, err = hp.readVarint32() + if err != nil { + return err + } + if transformCount > 0 { + reader := NewTransformReaderWithCapacity( + &t.frameBuffer, + int(transformCount), + ) + t.frameReader = reader + transformIDs := make([]THeaderTransformID, transformCount) + for i := 0; i < int(transformCount); i++ { + id, err := hp.readVarint32() + if err != nil { + return err + } + transformIDs[i] = THeaderTransformID(id) + } + // The transform IDs on the wire was added based on the order of + // writing, so on the reading side we need to reverse the order. + for i := transformCount - 1; i >= 0; i-- { + id := transformIDs[i] + if err := reader.AddTransform(id); err != nil { + return err + } + } + } + + // The info part does not use the transforms yet, so it's + // important to continue using headerBuf. + headers := make(THeaderMap) + for { + infoType, err := hp.readVarint32() + if errors.Is(err, io.EOF) { + break + } + if err != nil { + return err + } + if THeaderInfoType(infoType) == InfoKeyValue { + count, err := hp.readVarint32() + if err != nil { + return err + } + for i := 0; i < int(count); i++ { + key, err := hp.ReadString(ctx) + if err != nil { + return err + } + value, err := hp.ReadString(ctx) + if err != nil { + return err + } + headers[key] = value + } + } else { + // Skip reading info section on the first + // unsupported info type. + break + } + } + t.readHeaders = headers + + return nil +} + +func (t *THeaderTransport) needReadFrame() bool { + if t.clientType == clientUnknown { + // This is a new connection that's never read before. + return true + } + if t.isFramed() && t.frameReader == nil { + // We just finished the last frame. + return true + } + return false +} + +func (t *THeaderTransport) Read(p []byte) (read int, err error) { + // Here using context.Background instead of a context passed in is safe. + // First is that there's no way to pass context into this function. + // Then, 99% of the case when calling this Read frame is already read + // into frameReader. ReadFrame here is more of preventing bugs that + // didn't call ReadFrame before calling Read. + err = t.ReadFrame(context.Background()) + if err != nil { + return + } + if t.frameReader != nil { + read, err = t.frameReader.Read(p) + if err == nil && t.frameBuffer.Len() <= 0 { + // the last Read finished the frame, do endOfFrame + // handling here. + err = t.endOfFrame() + } else if err == io.EOF { + err = t.endOfFrame() + if err != nil { + return + } + if read == 0 { + // Try to read the next frame when we hit EOF + // (end of frame) immediately. + // When we got here, it means the last read + // finished the previous frame, but didn't + // do endOfFrame handling yet. + // We have to read the next frame here, + // as otherwise we would return 0 and nil, + // which is a case not handled well by most + // protocol implementations. + return t.Read(p) + } + } + return + } + return t.reader.Read(p) +} + +// Write writes data to the write buffer. +// +// You need to call Flush to actually write them to the transport. +func (t *THeaderTransport) Write(p []byte) (int, error) { + return t.writeBuffer.Write(p) +} + +// Flush writes the appropriate header and the write buffer to the underlying transport. +func (t *THeaderTransport) Flush(ctx context.Context) error { + if t.writeBuffer.Len() == 0 { + return nil + } + + defer t.writeBuffer.Reset() + + switch t.clientType { + default: + fallthrough + case clientUnknown: + t.clientType = clientHeaders + fallthrough + case clientHeaders: + headers := NewTMemoryBuffer() + hp := NewTCompactProtocol(headers) + hp.SetTConfiguration(t.cfg) + if _, err := hp.writeVarint32(int32(t.protocolID)); err != nil { + return NewTTransportExceptionFromError(err) + } + if _, err := hp.writeVarint32(int32(len(t.writeTransforms))); err != nil { + return NewTTransportExceptionFromError(err) + } + for _, transform := range t.writeTransforms { + if _, err := hp.writeVarint32(int32(transform)); err != nil { + return NewTTransportExceptionFromError(err) + } + } + if len(t.writeHeaders) > 0 { + if _, err := hp.writeVarint32(int32(InfoKeyValue)); err != nil { + return NewTTransportExceptionFromError(err) + } + if _, err := hp.writeVarint32(int32(len(t.writeHeaders))); err != nil { + return NewTTransportExceptionFromError(err) + } + for key, value := range t.writeHeaders { + if err := hp.WriteString(ctx, key); err != nil { + return NewTTransportExceptionFromError(err) + } + if err := hp.WriteString(ctx, value); err != nil { + return NewTTransportExceptionFromError(err) + } + } + } + padding := 4 - headers.Len()%4 + if padding < 4 { + buf := t.buffer[:padding] + for i := range buf { + buf[i] = 0 + } + if _, err := headers.Write(buf); err != nil { + return NewTTransportExceptionFromError(err) + } + } + + var payload bytes.Buffer + meta := headerMeta{ + MagicFlags: THeaderHeaderMagic + t.Flags&THeaderFlagsMask, + SequenceID: t.SequenceID, + HeaderLength: uint16(headers.Len() / 4), + } + if err := binary.Write(&payload, binary.BigEndian, meta); err != nil { + return NewTTransportExceptionFromError(err) + } + if _, err := io.Copy(&payload, headers); err != nil { + return NewTTransportExceptionFromError(err) + } + + writer, err := NewTransformWriter(&payload, t.writeTransforms) + if err != nil { + return NewTTransportExceptionFromError(err) + } + if _, err := io.Copy(writer, &t.writeBuffer); err != nil { + return NewTTransportExceptionFromError(err) + } + if err := writer.Close(); err != nil { + return NewTTransportExceptionFromError(err) + } + + // First write frame length + buf := t.buffer[:size32] + binary.BigEndian.PutUint32(buf, uint32(payload.Len())) + if _, err := t.transport.Write(buf); err != nil { + return NewTTransportExceptionFromError(err) + } + // Then write the payload + if _, err := io.Copy(t.transport, &payload); err != nil { + return NewTTransportExceptionFromError(err) + } + + case clientFramedBinary, clientFramedCompact: + buf := t.buffer[:size32] + binary.BigEndian.PutUint32(buf, uint32(t.writeBuffer.Len())) + if _, err := t.transport.Write(buf); err != nil { + return NewTTransportExceptionFromError(err) + } + fallthrough + case clientUnframedBinary, clientUnframedCompact: + if _, err := io.Copy(t.transport, &t.writeBuffer); err != nil { + return NewTTransportExceptionFromError(err) + } + } + + select { + default: + case <-ctx.Done(): + return NewTTransportExceptionFromError(ctx.Err()) + } + + return t.transport.Flush(ctx) +} + +// Close closes the transport, along with its underlying transport. +func (t *THeaderTransport) Close() error { + if err := t.Flush(context.Background()); err != nil { + return err + } + return t.transport.Close() +} + +// RemainingBytes calls underlying transport's RemainingBytes. +// +// Even in framed cases, because of all the possible compression transforms +// involved, the remaining frame size is likely to be different from the actual +// remaining readable bytes, so we don't bother to keep tracking the remaining +// frame size by ourselves and just use the underlying transport's +// RemainingBytes directly. +func (t *THeaderTransport) RemainingBytes() uint64 { + return t.transport.RemainingBytes() +} + +// GetReadHeaders returns the THeaderMap read from transport. +func (t *THeaderTransport) GetReadHeaders() THeaderMap { + return t.readHeaders +} + +// SetWriteHeader sets a header for write. +func (t *THeaderTransport) SetWriteHeader(key, value string) { + t.writeHeaders[key] = value +} + +// ClearWriteHeaders clears all write headers previously set. +func (t *THeaderTransport) ClearWriteHeaders() { + t.writeHeaders = make(THeaderMap) +} + +// AddTransform add a transform for writing. +func (t *THeaderTransport) AddTransform(transform THeaderTransformID) error { + if !supportedTransformIDs[transform] { + return NewTProtocolExceptionWithType( + NOT_IMPLEMENTED, + fmt.Errorf("THeaderTransformID %d not supported", transform), + ) + } + t.writeTransforms = append(t.writeTransforms, transform) + return nil +} + +// Protocol returns the wrapped protocol id used in this THeaderTransport. +func (t *THeaderTransport) Protocol() THeaderProtocolID { + switch t.clientType { + default: + return t.protocolID + case clientFramedBinary, clientUnframedBinary: + return THeaderProtocolBinary + case clientFramedCompact, clientUnframedCompact: + return THeaderProtocolCompact + } +} + +func (t *THeaderTransport) isFramed() bool { + switch t.clientType { + default: + return false + case clientHeaders, clientFramedBinary, clientFramedCompact: + return true + } +} + +// SetTConfiguration implements TConfigurationSetter. +func (t *THeaderTransport) SetTConfiguration(cfg *TConfiguration) { + PropagateTConfiguration(t.transport, cfg) + t.cfg = cfg +} + +// THeaderTransportFactory is a TTransportFactory implementation to create +// THeaderTransport. +// +// It also implements TConfigurationSetter. +type THeaderTransportFactory struct { + // The underlying factory, could be nil. + Factory TTransportFactory + + cfg *TConfiguration +} + +// Deprecated: Use NewTHeaderTransportFactoryConf instead. +func NewTHeaderTransportFactory(factory TTransportFactory) TTransportFactory { + return NewTHeaderTransportFactoryConf(factory, &TConfiguration{ + noPropagation: true, + }) +} + +// NewTHeaderTransportFactoryConf creates a new *THeaderTransportFactory with +// the given *TConfiguration. +func NewTHeaderTransportFactoryConf(factory TTransportFactory, conf *TConfiguration) TTransportFactory { + return &THeaderTransportFactory{ + Factory: factory, + + cfg: conf, + } +} + +// GetTransport implements TTransportFactory. +func (f *THeaderTransportFactory) GetTransport(trans TTransport) (TTransport, error) { + if f.Factory != nil { + t, err := f.Factory.GetTransport(trans) + if err != nil { + return nil, err + } + return NewTHeaderTransportConf(t, f.cfg), nil + } + return NewTHeaderTransportConf(trans, f.cfg), nil +} + +// SetTConfiguration implements TConfigurationSetter. +func (f *THeaderTransportFactory) SetTConfiguration(cfg *TConfiguration) { + PropagateTConfiguration(f.Factory, f.cfg) + f.cfg = cfg +} + +var ( + _ TConfigurationSetter = (*THeaderTransportFactory)(nil) + _ TConfigurationSetter = (*THeaderTransport)(nil) +) diff --git a/lib/go/thrift/header_transport_test.go b/lib/go/thrift/header_transport_test.go new file mode 100644 index 00000000000..65e69ee5a37 --- /dev/null +++ b/lib/go/thrift/header_transport_test.go @@ -0,0 +1,307 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package thrift + +import ( + "context" + "fmt" + "io" + "io/ioutil" + "strings" + "testing" + "testing/quick" +) + +func testTHeaderHeadersReadWriteProtocolID(t *testing.T, protoID THeaderProtocolID) { + trans := NewTMemoryBuffer() + reader := NewTHeaderTransport(trans) + writer := NewTHeaderTransportConf(trans, &TConfiguration{ + THeaderProtocolID: &protoID, + }) + + const key1 = "key1" + const value1 = "value1" + const key2 = "key2" + const value2 = "value2" + const payload1 = "hello, world1\n" + const payload2 = "hello, world2\n" + + // Write + if err := writer.AddTransform(TransformZlib); err != nil { + t.Fatalf( + "writer.AddTransform(TransformZlib) returned error: %v", + err, + ) + } + // Use double zlib to make sure that we close them in the right order. + if err := writer.AddTransform(TransformZlib); err != nil { + t.Fatalf( + "writer.AddTransform(TransformZlib) returned error: %v", + err, + ) + } + if err := writer.AddTransform(TransformNone); err != nil { + t.Fatalf( + "writer.AddTransform(TransformNone) returned error: %v", + err, + ) + } + writer.SetWriteHeader(key1, value1) + writer.SetWriteHeader(key2, value2) + if _, err := writer.Write([]byte(payload1)); err != nil { + t.Errorf("writer.Write returned error: %v", err) + } + if err := writer.Flush(context.Background()); err != nil { + t.Errorf("writer.Flush returned error: %v", err) + } + if _, err := writer.Write([]byte(payload2)); err != nil { + t.Errorf("writer.Write returned error: %v", err) + } + if err := writer.Flush(context.Background()); err != nil { + t.Errorf("writer.Flush returned error: %v", err) + } + + // Read + + // Make sure multiple calls to ReadFrame is fine. + if err := reader.ReadFrame(context.Background()); err != nil { + t.Errorf("reader.ReadFrame returned error: %v", err) + } + if err := reader.ReadFrame(context.Background()); err != nil { + t.Errorf("reader.ReadFrame returned error: %v", err) + } + read, err := ioutil.ReadAll(reader) + if err != nil { + t.Errorf("Read returned error: %v", err) + } + if err := reader.ReadFrame(context.Background()); err != nil && err != io.EOF { + t.Errorf("reader.ReadFrame returned error: %v", err) + } + if string(read) != payload1+payload2 { + t.Errorf( + "Read content expected %q, got %q", + payload1+payload2, + read, + ) + } + if prot := reader.Protocol(); prot != protoID { + t.Errorf( + "reader.Protocol() expected %d, got %d", + protoID, + prot, + ) + } + if reader.clientType != clientHeaders { + t.Errorf( + "reader.clientType expected %d, got %d", + clientHeaders, + reader.clientType, + ) + } + headers := reader.GetReadHeaders() + if len(headers) != 2 || headers[key1] != value1 || headers[key2] != value2 { + t.Errorf( + "reader.GetReadHeaders() expected size 2, actual content: %+v", + headers, + ) + } +} + +func TestTHeaderHeadersReadWrite(t *testing.T) { + for label, id := range map[string]THeaderProtocolID{ + "default": THeaderProtocolDefault, + "binary": THeaderProtocolBinary, + "compact": THeaderProtocolCompact, + } { + t.Run(label, func(t *testing.T) { + testTHeaderHeadersReadWriteProtocolID(t, id) + }) + } +} + +func TestTHeaderTransportNoDoubleWrapping(t *testing.T) { + trans := NewTMemoryBuffer() + orig := NewTHeaderTransport(trans) + wrapped := NewTHeaderTransport(orig) + + if wrapped != orig { + t.Errorf("NewTHeaderTransport double wrapped THeaderTransport") + } +} + +func TestTHeaderTransportNoReadBeyondFrame(t *testing.T) { + trans := NewTMemoryBuffer() + writeContent := func(writer TTransport, content string) error { + if _, err := io.Copy(writer, strings.NewReader(content)); err != nil { + return err + } + if err := writer.Flush(context.Background()); err != nil { + return err + } + return nil + } + f := func(content string) bool { + trans.Reset() + if len(content) == 0 { + return true + } + + reader := NewTHeaderTransport(trans) + writer := NewTHeaderTransport(trans) + // Write content twice + if err := writeContent(writer, content); err != nil { + t.Error(err) + } + if err := writeContent(writer, content); err != nil { + t.Error(err) + } + // buf is big enough to read both content out, + // but it shouldn't read beyond the first one in a single Read call. + buf := make([]byte, len(content)*3) + read, err := reader.Read(buf) + if err != nil { + t.Error(err) + } + if read == 0 || read > len(content) { + t.Errorf( + "Expected read in no more than %d:%q, got %d:%q", + len(content), + content, + read, + buf[:read], + ) + } + + // Check for endOfFrame handling + if !reader.needReadFrame() { + t.Error("Expected needReadFrame to be true after read the frame fully, got false") + } + return !t.Failed() + } + if err := quick.Check(f, nil); err != nil { + t.Error(err) + } +} + +func TestTHeaderTransportEndOfFrameHandling(t *testing.T) { + trans := NewTMemoryBuffer() + writeContent := func(writer TTransport, content string) error { + if _, err := io.Copy(writer, strings.NewReader(content)); err != nil { + return err + } + if err := writer.Flush(context.Background()); err != nil { + return err + } + return nil + } + + readFully := func(content string) bool { + trans.Reset() + if len(content) == 0 { + return true + } + + reader := NewTHeaderTransport(trans) + writer := NewTHeaderTransport(trans) + // Write content + if err := writeContent(writer, content); err != nil { + t.Error(err) + } + buf := make([]byte, len(content)) + _, err := reader.Read(buf) + if err != nil { + t.Error(err) + } + if !reader.needReadFrame() { + t.Error("Expected needReadFrame to be true after read the frame fully, got false") + } + return !t.Failed() + } + if err := quick.Check(readFully, nil); err != nil { + t.Error(err) + } + + readPartially := func(content string) bool { + trans.Reset() + if len(content) < 1 { + return true + } + + reader := NewTHeaderTransport(trans) + writer := NewTHeaderTransport(trans) + // Write content + if err := writeContent(writer, content); err != nil { + t.Error(err) + } + // Make the buf smaller so it can't read fully + buf := make([]byte, len(content)-1) + _, err := reader.Read(buf) + if err != nil { + t.Error(err) + } + if reader.needReadFrame() { + t.Error("Expected needReadFrame to be false before read the frame fully, got true") + } + return !t.Failed() + } + if err := quick.Check(readPartially, nil); err != nil { + t.Error(err) + } +} + +func BenchmarkTHeaderProtocolIDValidate(b *testing.B) { + for _, c := range []THeaderProtocolID{ + THeaderProtocolBinary, + THeaderProtocolCompact, + -1, + } { + b.Run(fmt.Sprintf("%2v", c), func(b *testing.B) { + b.RunParallel(func(pb *testing.PB) { + for pb.Next() { + c.Validate() + } + }) + }) + } +} + +func TestSetTHeaderTransportProtocolID(t *testing.T) { + const expected = THeaderProtocolCompact + factory := NewTHeaderTransportFactoryConf(nil, &TConfiguration{ + THeaderProtocolID: THeaderProtocolIDPtrMust(expected), + }) + buf := NewTMemoryBuffer() + trans, err := factory.GetTransport(buf) + if err != nil { + t.Fatalf("Failed to get transport from factory: %v", err) + } + ht, ok := trans.(*THeaderTransport) + if !ok { + t.Fatalf("Transport is not *THeaderTransport: %#v", trans) + } + if actual := ht.Protocol(); actual != expected { + t.Errorf("Expected protocol id %v, got %v", expected, actual) + } + + ht.SetTConfiguration(&TConfiguration{}) + if actual := ht.Protocol(); actual != expected { + t.Errorf("Expected protocol id %v, got %v", expected, actual) + } +} diff --git a/lib/go/thrift/http_client.go b/lib/go/thrift/http_client.go index 5c82bf53875..15015864f0f 100644 --- a/lib/go/thrift/http_client.go +++ b/lib/go/thrift/http_client.go @@ -22,6 +22,7 @@ package thrift import ( "bytes" "context" + "errors" "io" "io/ioutil" "net/http" @@ -159,26 +160,37 @@ func (p *THttpClient) Read(buf []byte) (int, error) { return 0, NewTTransportException(NOT_OPEN, "Response buffer is empty, no request.") } n, err := p.response.Body.Read(buf) - if n > 0 && (err == nil || err == io.EOF) { + if n > 0 && (err == nil || errors.Is(err, io.EOF)) { return n, nil } return n, NewTTransportExceptionFromError(err) } func (p *THttpClient) ReadByte() (c byte, err error) { + if p.response == nil { + return 0, NewTTransportException(NOT_OPEN, "Response buffer is empty, no request.") + } return readByte(p.response.Body) } func (p *THttpClient) Write(buf []byte) (int, error) { - n, err := p.requestBuffer.Write(buf) - return n, err + if p.requestBuffer == nil { + return 0, NewTTransportException(NOT_OPEN, "Request buffer is nil, connection may have been closed.") + } + return p.requestBuffer.Write(buf) } func (p *THttpClient) WriteByte(c byte) error { + if p.requestBuffer == nil { + return NewTTransportException(NOT_OPEN, "Request buffer is nil, connection may have been closed.") + } return p.requestBuffer.WriteByte(c) } func (p *THttpClient) WriteString(s string) (n int, err error) { + if p.requestBuffer == nil { + return 0, NewTTransportException(NOT_OPEN, "Request buffer is nil, connection may have been closed.") + } return p.requestBuffer.WriteString(s) } @@ -186,7 +198,11 @@ func (p *THttpClient) Flush(ctx context.Context) error { // Close any previous response body to avoid leaking connections. p.closeResponse() - req, err := http.NewRequest("POST", p.url.String(), p.requestBuffer) + // Give up the ownership of the current request buffer to http request, + // and create a new buffer for the next request. + buf := p.requestBuffer + p.requestBuffer = new(bytes.Buffer) + req, err := http.NewRequest("POST", p.url.String(), buf) if err != nil { return NewTTransportExceptionFromError(err) } @@ -218,7 +234,7 @@ func (p *THttpClient) RemainingBytes() (num_bytes uint64) { } const maxSize = ^uint64(0) - return maxSize // the thruth is, we just don't know unless framed is used + return maxSize // the truth is, we just don't know unless framed is used } // Deprecated: Use NewTHttpClientTransportFactory instead. diff --git a/lib/go/thrift/http_client_test.go b/lib/go/thrift/http_client_test.go index 453680ace87..eba366815ca 100644 --- a/lib/go/thrift/http_client_test.go +++ b/lib/go/thrift/http_client_test.go @@ -20,6 +20,8 @@ package thrift import ( + "bytes" + "context" "net/http" "testing" ) @@ -32,9 +34,16 @@ func TestHttpClient(t *testing.T) { trans, err := NewTHttpPostClient("http://" + addr.String()) if err != nil { l.Close() - t.Fatalf("Unable to connect to %s: %s", addr.String(), err) + t.Fatalf("Unable to connect to %s: %v", addr.String(), err) } TransportTest(t, trans, trans) + + t.Run("nilBuffer", func(t *testing.T) { + _ = trans.Close() + if _, err = trans.Write([]byte{1, 2, 3, 4}); err == nil { + t.Fatal("writing to a closed transport did not result in an error") + } + }) } func TestHttpClientHeaders(t *testing.T) { @@ -45,7 +54,7 @@ func TestHttpClientHeaders(t *testing.T) { trans, err := NewTHttpPostClient("http://" + addr.String()) if err != nil { l.Close() - t.Fatalf("Unable to connect to %s: %s", addr.String(), err) + t.Fatalf("Unable to connect to %s: %v", addr.String(), err) } TransportHeaderTest(t, trans, trans) } @@ -65,7 +74,7 @@ func TestHttpCustomClient(t *testing.T) { }) if err != nil { l.Close() - t.Fatalf("Unable to connect to %s: %s", addr.String(), err) + t.Fatalf("Unable to connect to %s: %v", addr.String(), err) } TransportHeaderTest(t, trans, trans) @@ -87,7 +96,7 @@ func TestHttpCustomClientPackageScope(t *testing.T) { trans, err := NewTHttpPostClient("http://" + addr.String()) if err != nil { l.Close() - t.Fatalf("Unable to connect to %s: %s", addr.String(), err) + t.Fatalf("Unable to connect to %s: %v", addr.String(), err) } TransportHeaderTest(t, trans, trans) @@ -96,6 +105,54 @@ func TestHttpCustomClientPackageScope(t *testing.T) { } } +func TestHTTPClientFlushesRequestBufferOnErrors(t *testing.T) { + var ( + write1 = []byte("write 1") + write2 = []byte("write 2") + ) + + l, addr := HttpClientSetupForTest(t) + if l != nil { + defer l.Close() + } + trans, err := NewTHttpPostClient("http://" + addr.String()) + if err != nil { + t.Fatalf("Unable to connect to %s: %v", addr.String(), err) + } + defer trans.Close() + + _, err = trans.Write(write1) + if err != nil { + t.Fatalf("Failed to write to transport: %v", err) + } + ctx, cancel := context.WithCancel(context.Background()) + cancel() + err = trans.Flush(ctx) + if err == nil { + t.Fatal("Expected flush error") + } + + _, err = trans.Write(write2) + if err != nil { + t.Fatalf("Failed to write to transport: %v", err) + } + err = trans.Flush(context.Background()) + if err != nil { + t.Fatalf("Failed to flush: %v", err) + } + + data := make([]byte, 1024) + n, err := trans.Read(data) + if err != nil { + t.Fatalf("Failed to read: %v", err) + } + + data = data[:n] + if !bytes.Equal(data, write2) { + t.Fatalf("Received unexpected data: %q, expected: %q", data, write2) + } +} + type customHttpTransport struct { hit bool } diff --git a/lib/go/thrift/http_transport.go b/lib/go/thrift/http_transport.go index 66f0f388a09..bc6922762ad 100644 --- a/lib/go/thrift/http_transport.go +++ b/lib/go/thrift/http_transport.go @@ -24,6 +24,7 @@ import ( "io" "net/http" "strings" + "sync" ) // NewThriftHandlerFunc is a function that create a ready to use Apache Thrift Handler function @@ -40,14 +41,24 @@ func NewThriftHandlerFunc(processor TProcessor, // gz transparently compresses the HTTP response if the client supports it. func gz(handler http.HandlerFunc) http.HandlerFunc { + sp := &sync.Pool{ + New: func() interface{} { + return gzip.NewWriter(nil) + }, + } + return func(w http.ResponseWriter, r *http.Request) { if !strings.Contains(r.Header.Get("Accept-Encoding"), "gzip") { handler(w, r) return } w.Header().Set("Content-Encoding", "gzip") - gz := gzip.NewWriter(w) - defer gz.Close() + gz := sp.Get().(*gzip.Writer) + gz.Reset(w) + defer func() { + _ = gz.Close() + sp.Put(gz) + }() gzw := gzipResponseWriter{Writer: gz, ResponseWriter: w} handler(gzw, r) } diff --git a/lib/go/thrift/iostream_transport.go b/lib/go/thrift/iostream_transport.go index fea93bceffa..1c477990fea 100644 --- a/lib/go/thrift/iostream_transport.go +++ b/lib/go/thrift/iostream_transport.go @@ -210,5 +210,13 @@ func (p *StreamTransport) WriteString(s string) (n int, err error) { func (p *StreamTransport) RemainingBytes() (num_bytes uint64) { const maxSize = ^uint64(0) - return maxSize // the thruth is, we just don't know unless framed is used + return maxSize // the truth is, we just don't know unless framed is used } + +// SetTConfiguration implements TConfigurationSetter for propagation. +func (p *StreamTransport) SetTConfiguration(conf *TConfiguration) { + PropagateTConfiguration(p.Reader, conf) + PropagateTConfiguration(p.Writer, conf) +} + +var _ TConfigurationSetter = (*StreamTransport)(nil) diff --git a/lib/go/thrift/json_protocol.go b/lib/go/thrift/json_protocol.go index 7be685d43f2..8e59d16cfda 100644 --- a/lib/go/thrift/json_protocol.go +++ b/lib/go/thrift/json_protocol.go @@ -32,10 +32,7 @@ const ( // for references to _ParseContext see tsimplejson_protocol.go // JSON protocol implementation for thrift. -// -// This protocol produces/consumes a simple output format -// suitable for parsing by scripting languages. It should not be -// confused with the full-featured TJSONProtocol. +// Utilizes Simple JSON protocol // type TJSONProtocol struct { *TSimpleJSONProtocol @@ -44,8 +41,8 @@ type TJSONProtocol struct { // Constructor func NewTJSONProtocol(t TTransport) *TJSONProtocol { v := &TJSONProtocol{TSimpleJSONProtocol: NewTSimpleJSONProtocol(t)} - v.parseContextStack = append(v.parseContextStack, int(_CONTEXT_IN_TOPLEVEL)) - v.dumpContext = append(v.dumpContext, int(_CONTEXT_IN_TOPLEVEL)) + v.parseContextStack.push(_CONTEXT_IN_TOPLEVEL) + v.dumpContext.push(_CONTEXT_IN_TOPLEVEL) return v } @@ -60,43 +57,43 @@ func NewTJSONProtocolFactory() *TJSONProtocolFactory { return &TJSONProtocolFactory{} } -func (p *TJSONProtocol) WriteMessageBegin(name string, typeId TMessageType, seqId int32) error { +func (p *TJSONProtocol) WriteMessageBegin(ctx context.Context, name string, typeId TMessageType, seqId int32) error { p.resetContextStack() // THRIFT-3735 if e := p.OutputListBegin(); e != nil { return e } - if e := p.WriteI32(THRIFT_JSON_PROTOCOL_VERSION); e != nil { + if e := p.WriteI32(ctx, THRIFT_JSON_PROTOCOL_VERSION); e != nil { return e } - if e := p.WriteString(name); e != nil { + if e := p.WriteString(ctx, name); e != nil { return e } - if e := p.WriteByte(int8(typeId)); e != nil { + if e := p.WriteByte(ctx, int8(typeId)); e != nil { return e } - if e := p.WriteI32(seqId); e != nil { + if e := p.WriteI32(ctx, seqId); e != nil { return e } return nil } -func (p *TJSONProtocol) WriteMessageEnd() error { +func (p *TJSONProtocol) WriteMessageEnd(ctx context.Context) error { return p.OutputListEnd() } -func (p *TJSONProtocol) WriteStructBegin(name string) error { +func (p *TJSONProtocol) WriteStructBegin(ctx context.Context, name string) error { if e := p.OutputObjectBegin(); e != nil { return e } return nil } -func (p *TJSONProtocol) WriteStructEnd() error { +func (p *TJSONProtocol) WriteStructEnd(ctx context.Context) error { return p.OutputObjectEnd() } -func (p *TJSONProtocol) WriteFieldBegin(name string, typeId TType, id int16) error { - if e := p.WriteI16(id); e != nil { +func (p *TJSONProtocol) WriteFieldBegin(ctx context.Context, name string, typeId TType, id int16) error { + if e := p.WriteI16(ctx, id); e != nil { return e } if e := p.OutputObjectBegin(); e != nil { @@ -106,19 +103,19 @@ func (p *TJSONProtocol) WriteFieldBegin(name string, typeId TType, id int16) err if e1 != nil { return e1 } - if e := p.WriteString(s); e != nil { + if e := p.WriteString(ctx, s); e != nil { return e } return nil } -func (p *TJSONProtocol) WriteFieldEnd() error { +func (p *TJSONProtocol) WriteFieldEnd(ctx context.Context) error { return p.OutputObjectEnd() } -func (p *TJSONProtocol) WriteFieldStop() error { return nil } +func (p *TJSONProtocol) WriteFieldStop(ctx context.Context) error { return nil } -func (p *TJSONProtocol) WriteMapBegin(keyType TType, valueType TType, size int) error { +func (p *TJSONProtocol) WriteMapBegin(ctx context.Context, keyType TType, valueType TType, size int) error { if e := p.OutputListBegin(); e != nil { return e } @@ -126,77 +123,77 @@ func (p *TJSONProtocol) WriteMapBegin(keyType TType, valueType TType, size int) if e1 != nil { return e1 } - if e := p.WriteString(s); e != nil { + if e := p.WriteString(ctx, s); e != nil { return e } s, e1 = p.TypeIdToString(valueType) if e1 != nil { return e1 } - if e := p.WriteString(s); e != nil { + if e := p.WriteString(ctx, s); e != nil { return e } - if e := p.WriteI64(int64(size)); e != nil { + if e := p.WriteI64(ctx, int64(size)); e != nil { return e } return p.OutputObjectBegin() } -func (p *TJSONProtocol) WriteMapEnd() error { +func (p *TJSONProtocol) WriteMapEnd(ctx context.Context) error { if e := p.OutputObjectEnd(); e != nil { return e } return p.OutputListEnd() } -func (p *TJSONProtocol) WriteListBegin(elemType TType, size int) error { +func (p *TJSONProtocol) WriteListBegin(ctx context.Context, elemType TType, size int) error { return p.OutputElemListBegin(elemType, size) } -func (p *TJSONProtocol) WriteListEnd() error { +func (p *TJSONProtocol) WriteListEnd(ctx context.Context) error { return p.OutputListEnd() } -func (p *TJSONProtocol) WriteSetBegin(elemType TType, size int) error { +func (p *TJSONProtocol) WriteSetBegin(ctx context.Context, elemType TType, size int) error { return p.OutputElemListBegin(elemType, size) } -func (p *TJSONProtocol) WriteSetEnd() error { +func (p *TJSONProtocol) WriteSetEnd(ctx context.Context) error { return p.OutputListEnd() } -func (p *TJSONProtocol) WriteBool(b bool) error { +func (p *TJSONProtocol) WriteBool(ctx context.Context, b bool) error { if b { - return p.WriteI32(1) + return p.WriteI32(ctx, 1) } - return p.WriteI32(0) + return p.WriteI32(ctx, 0) } -func (p *TJSONProtocol) WriteByte(b int8) error { - return p.WriteI32(int32(b)) +func (p *TJSONProtocol) WriteByte(ctx context.Context, b int8) error { + return p.WriteI32(ctx, int32(b)) } -func (p *TJSONProtocol) WriteI16(v int16) error { - return p.WriteI32(int32(v)) +func (p *TJSONProtocol) WriteI16(ctx context.Context, v int16) error { + return p.WriteI32(ctx, int32(v)) } -func (p *TJSONProtocol) WriteI32(v int32) error { +func (p *TJSONProtocol) WriteI32(ctx context.Context, v int32) error { return p.OutputI64(int64(v)) } -func (p *TJSONProtocol) WriteI64(v int64) error { +func (p *TJSONProtocol) WriteI64(ctx context.Context, v int64) error { return p.OutputI64(int64(v)) } -func (p *TJSONProtocol) WriteDouble(v float64) error { +func (p *TJSONProtocol) WriteDouble(ctx context.Context, v float64) error { return p.OutputF64(v) } -func (p *TJSONProtocol) WriteString(v string) error { +func (p *TJSONProtocol) WriteString(ctx context.Context, v string) error { return p.OutputString(v) } -func (p *TJSONProtocol) WriteBinary(v []byte) error { +func (p *TJSONProtocol) WriteBinary(ctx context.Context, v []byte) error { // JSON library only takes in a string, // not an arbitrary byte array, to ensure bytes are transmitted // efficiently we must convert this into a valid JSON string @@ -222,12 +219,12 @@ func (p *TJSONProtocol) WriteBinary(v []byte) error { } // Reading methods. -func (p *TJSONProtocol) ReadMessageBegin() (name string, typeId TMessageType, seqId int32, err error) { +func (p *TJSONProtocol) ReadMessageBegin(ctx context.Context) (name string, typeId TMessageType, seqId int32, err error) { p.resetContextStack() // THRIFT-3735 if isNull, err := p.ParseListBegin(); isNull || err != nil { return name, typeId, seqId, err } - version, err := p.ReadI32() + version, err := p.ReadI32(ctx) if err != nil { return name, typeId, seqId, err } @@ -236,47 +233,47 @@ func (p *TJSONProtocol) ReadMessageBegin() (name string, typeId TMessageType, se return name, typeId, seqId, NewTProtocolExceptionWithType(INVALID_DATA, e) } - if name, err = p.ReadString(); err != nil { + if name, err = p.ReadString(ctx); err != nil { return name, typeId, seqId, err } - bTypeId, err := p.ReadByte() + bTypeId, err := p.ReadByte(ctx) typeId = TMessageType(bTypeId) if err != nil { return name, typeId, seqId, err } - if seqId, err = p.ReadI32(); err != nil { + if seqId, err = p.ReadI32(ctx); err != nil { return name, typeId, seqId, err } return name, typeId, seqId, nil } -func (p *TJSONProtocol) ReadMessageEnd() error { +func (p *TJSONProtocol) ReadMessageEnd(ctx context.Context) error { err := p.ParseListEnd() return err } -func (p *TJSONProtocol) ReadStructBegin() (name string, err error) { +func (p *TJSONProtocol) ReadStructBegin(ctx context.Context) (name string, err error) { _, err = p.ParseObjectStart() return "", err } -func (p *TJSONProtocol) ReadStructEnd() error { +func (p *TJSONProtocol) ReadStructEnd(ctx context.Context) error { return p.ParseObjectEnd() } -func (p *TJSONProtocol) ReadFieldBegin() (string, TType, int16, error) { +func (p *TJSONProtocol) ReadFieldBegin(ctx context.Context) (string, TType, int16, error) { b, _ := p.reader.Peek(1) if len(b) < 1 || b[0] == JSON_RBRACE[0] || b[0] == JSON_RBRACKET[0] { return "", STOP, -1, nil } - fieldId, err := p.ReadI16() + fieldId, err := p.ReadI16(ctx) if err != nil { return "", STOP, fieldId, err } if _, err = p.ParseObjectStart(); err != nil { return "", STOP, fieldId, err } - sType, err := p.ReadString() + sType, err := p.ReadString(ctx) if err != nil { return "", STOP, fieldId, err } @@ -284,17 +281,17 @@ func (p *TJSONProtocol) ReadFieldBegin() (string, TType, int16, error) { return "", fType, fieldId, err } -func (p *TJSONProtocol) ReadFieldEnd() error { +func (p *TJSONProtocol) ReadFieldEnd(ctx context.Context) error { return p.ParseObjectEnd() } -func (p *TJSONProtocol) ReadMapBegin() (keyType TType, valueType TType, size int, e error) { +func (p *TJSONProtocol) ReadMapBegin(ctx context.Context) (keyType TType, valueType TType, size int, e error) { if isNull, e := p.ParseListBegin(); isNull || e != nil { return VOID, VOID, 0, e } // read keyType - sKeyType, e := p.ReadString() + sKeyType, e := p.ReadString(ctx) if e != nil { return keyType, valueType, size, e } @@ -304,7 +301,7 @@ func (p *TJSONProtocol) ReadMapBegin() (keyType TType, valueType TType, size int } // read valueType - sValueType, e := p.ReadString() + sValueType, e := p.ReadString(ctx) if e != nil { return keyType, valueType, size, e } @@ -314,7 +311,7 @@ func (p *TJSONProtocol) ReadMapBegin() (keyType TType, valueType TType, size int } // read size - iSize, e := p.ReadI64() + iSize, e := p.ReadI64(ctx) if e != nil { return keyType, valueType, size, e } @@ -324,7 +321,7 @@ func (p *TJSONProtocol) ReadMapBegin() (keyType TType, valueType TType, size int return keyType, valueType, size, e } -func (p *TJSONProtocol) ReadMapEnd() error { +func (p *TJSONProtocol) ReadMapEnd(ctx context.Context) error { e := p.ParseObjectEnd() if e != nil { return e @@ -332,53 +329,53 @@ func (p *TJSONProtocol) ReadMapEnd() error { return p.ParseListEnd() } -func (p *TJSONProtocol) ReadListBegin() (elemType TType, size int, e error) { +func (p *TJSONProtocol) ReadListBegin(ctx context.Context) (elemType TType, size int, e error) { return p.ParseElemListBegin() } -func (p *TJSONProtocol) ReadListEnd() error { +func (p *TJSONProtocol) ReadListEnd(ctx context.Context) error { return p.ParseListEnd() } -func (p *TJSONProtocol) ReadSetBegin() (elemType TType, size int, e error) { +func (p *TJSONProtocol) ReadSetBegin(ctx context.Context) (elemType TType, size int, e error) { return p.ParseElemListBegin() } -func (p *TJSONProtocol) ReadSetEnd() error { +func (p *TJSONProtocol) ReadSetEnd(ctx context.Context) error { return p.ParseListEnd() } -func (p *TJSONProtocol) ReadBool() (bool, error) { - value, err := p.ReadI32() +func (p *TJSONProtocol) ReadBool(ctx context.Context) (bool, error) { + value, err := p.ReadI32(ctx) return (value != 0), err } -func (p *TJSONProtocol) ReadByte() (int8, error) { - v, err := p.ReadI64() +func (p *TJSONProtocol) ReadByte(ctx context.Context) (int8, error) { + v, err := p.ReadI64(ctx) return int8(v), err } -func (p *TJSONProtocol) ReadI16() (int16, error) { - v, err := p.ReadI64() +func (p *TJSONProtocol) ReadI16(ctx context.Context) (int16, error) { + v, err := p.ReadI64(ctx) return int16(v), err } -func (p *TJSONProtocol) ReadI32() (int32, error) { - v, err := p.ReadI64() +func (p *TJSONProtocol) ReadI32(ctx context.Context) (int32, error) { + v, err := p.ReadI64(ctx) return int32(v), err } -func (p *TJSONProtocol) ReadI64() (int64, error) { +func (p *TJSONProtocol) ReadI64(ctx context.Context) (int64, error) { v, _, err := p.ParseI64() return v, err } -func (p *TJSONProtocol) ReadDouble() (float64, error) { +func (p *TJSONProtocol) ReadDouble(ctx context.Context) (float64, error) { v, _, err := p.ParseF64() return v, err } -func (p *TJSONProtocol) ReadString() (string, error) { +func (p *TJSONProtocol) ReadString(ctx context.Context) (string, error) { var v string if err := p.ParsePreValue(); err != nil { return v, err @@ -408,7 +405,7 @@ func (p *TJSONProtocol) ReadString() (string, error) { return v, p.ParsePostValue() } -func (p *TJSONProtocol) ReadBinary() ([]byte, error) { +func (p *TJSONProtocol) ReadBinary(ctx context.Context) ([]byte, error) { var v []byte if err := p.ParsePreValue(); err != nil { return nil, err @@ -447,8 +444,8 @@ func (p *TJSONProtocol) Flush(ctx context.Context) (err error) { return NewTProtocolException(err) } -func (p *TJSONProtocol) Skip(fieldType TType) (err error) { - return SkipDefaultDepth(p, fieldType) +func (p *TJSONProtocol) Skip(ctx context.Context, fieldType TType) (err error) { + return SkipDefaultDepth(ctx, p, fieldType) } func (p *TJSONProtocol) Transport() TTransport { @@ -463,10 +460,10 @@ func (p *TJSONProtocol) OutputElemListBegin(elemType TType, size int) error { if e1 != nil { return e1 } - if e := p.WriteString(s); e != nil { + if e := p.OutputString(s); e != nil { return e } - if e := p.WriteI64(int64(size)); e != nil { + if e := p.OutputI64(int64(size)); e != nil { return e } return nil @@ -476,7 +473,11 @@ func (p *TJSONProtocol) ParseElemListBegin() (elemType TType, size int, e error) if isNull, e := p.ParseListBegin(); isNull || e != nil { return VOID, 0, e } - sElemType, err := p.ReadString() + // We don't really use the ctx in ReadString implementation, + // so this is safe for now. + // We might want to add context to ParseElemListBegin if we start to use + // ctx in ReadString implementation in the future. + sElemType, err := p.ReadString(context.Background()) if err != nil { return VOID, size, err } @@ -484,7 +485,7 @@ func (p *TJSONProtocol) ParseElemListBegin() (elemType TType, size int, e error) if err != nil { return elemType, size, err } - nSize, err2 := p.ReadI64() + nSize, _, err2 := p.ParseI64() size = int(nSize) return elemType, size, err2 } @@ -493,7 +494,11 @@ func (p *TJSONProtocol) readElemListBegin() (elemType TType, size int, e error) if isNull, e := p.ParseListBegin(); isNull || e != nil { return VOID, 0, e } - sElemType, err := p.ReadString() + // We don't really use the ctx in ReadString implementation, + // so this is safe for now. + // We might want to add context to ParseElemListBegin if we start to use + // ctx in ReadString implementation in the future. + sElemType, err := p.ReadString(context.Background()) if err != nil { return VOID, size, err } @@ -501,7 +506,7 @@ func (p *TJSONProtocol) readElemListBegin() (elemType TType, size int, e error) if err != nil { return elemType, size, err } - nSize, err2 := p.ReadI64() + nSize, _, err2 := p.ParseI64() size = int(nSize) return elemType, size, err2 } @@ -582,3 +587,5 @@ func (p *TJSONProtocol) StringToTypeId(fieldType string) (TType, error) { e := fmt.Errorf("Unknown type identifier: %s", fieldType) return TType(STOP), NewTProtocolExceptionWithType(INVALID_DATA, e) } + +var _ TConfigurationSetter = (*TJSONProtocol)(nil) diff --git a/lib/go/thrift/json_protocol_test.go b/lib/go/thrift/json_protocol_test.go index 59c4d64a260..39e52d15069 100644 --- a/lib/go/thrift/json_protocol_test.go +++ b/lib/go/thrift/json_protocol_test.go @@ -34,7 +34,7 @@ func TestWriteJSONProtocolBool(t *testing.T) { trans := NewTMemoryBuffer() p := NewTJSONProtocol(trans) for _, value := range BOOL_VALUES { - if e := p.WriteBool(value); e != nil { + if e := p.WriteBool(context.Background(), value); e != nil { t.Fatalf("Unable to write %s value %v due to error: %s", thetype, value, e.Error()) } if e := p.Flush(context.Background()); e != nil { @@ -71,7 +71,7 @@ func TestReadJSONProtocolBool(t *testing.T) { } trans.Flush(context.Background()) s := trans.String() - v, e := p.ReadBool() + v, e := p.ReadBool(context.Background()) if e != nil { t.Fatalf("Unable to read %s value %v due to error: %s", thetype, value, e.Error()) } @@ -92,7 +92,7 @@ func TestWriteJSONProtocolByte(t *testing.T) { trans := NewTMemoryBuffer() p := NewTJSONProtocol(trans) for _, value := range BYTE_VALUES { - if e := p.WriteByte(value); e != nil { + if e := p.WriteByte(context.Background(), value); e != nil { t.Fatalf("Unable to write %s value %v due to error: %s", thetype, value, e.Error()) } if e := p.Flush(context.Background()); e != nil { @@ -119,7 +119,7 @@ func TestReadJSONProtocolByte(t *testing.T) { trans.WriteString(strconv.Itoa(int(value))) trans.Flush(context.Background()) s := trans.String() - v, e := p.ReadByte() + v, e := p.ReadByte(context.Background()) if e != nil { t.Fatalf("Unable to read %s value %v due to error: %s", thetype, value, e.Error()) } @@ -139,7 +139,7 @@ func TestWriteJSONProtocolI16(t *testing.T) { trans := NewTMemoryBuffer() p := NewTJSONProtocol(trans) for _, value := range INT16_VALUES { - if e := p.WriteI16(value); e != nil { + if e := p.WriteI16(context.Background(), value); e != nil { t.Fatalf("Unable to write %s value %v due to error: %s", thetype, value, e.Error()) } if e := p.Flush(context.Background()); e != nil { @@ -166,7 +166,7 @@ func TestReadJSONProtocolI16(t *testing.T) { trans.WriteString(strconv.Itoa(int(value))) trans.Flush(context.Background()) s := trans.String() - v, e := p.ReadI16() + v, e := p.ReadI16(context.Background()) if e != nil { t.Fatalf("Unable to read %s value %v due to error: %s", thetype, value, e.Error()) } @@ -186,7 +186,7 @@ func TestWriteJSONProtocolI32(t *testing.T) { trans := NewTMemoryBuffer() p := NewTJSONProtocol(trans) for _, value := range INT32_VALUES { - if e := p.WriteI32(value); e != nil { + if e := p.WriteI32(context.Background(), value); e != nil { t.Fatalf("Unable to write %s value %v due to error: %s", thetype, value, e.Error()) } if e := p.Flush(context.Background()); e != nil { @@ -213,7 +213,7 @@ func TestReadJSONProtocolI32(t *testing.T) { trans.WriteString(strconv.Itoa(int(value))) trans.Flush(context.Background()) s := trans.String() - v, e := p.ReadI32() + v, e := p.ReadI32(context.Background()) if e != nil { t.Fatalf("Unable to read %s value %v due to error: %s", thetype, value, e.Error()) } @@ -233,7 +233,7 @@ func TestWriteJSONProtocolI64(t *testing.T) { trans := NewTMemoryBuffer() p := NewTJSONProtocol(trans) for _, value := range INT64_VALUES { - if e := p.WriteI64(value); e != nil { + if e := p.WriteI64(context.Background(), value); e != nil { t.Fatalf("Unable to write %s value %v due to error: %s", thetype, value, e.Error()) } if e := p.Flush(context.Background()); e != nil { @@ -260,7 +260,7 @@ func TestReadJSONProtocolI64(t *testing.T) { trans.WriteString(strconv.FormatInt(value, 10)) trans.Flush(context.Background()) s := trans.String() - v, e := p.ReadI64() + v, e := p.ReadI64(context.Background()) if e != nil { t.Fatalf("Unable to read %s value %v due to error: %s", thetype, value, e.Error()) } @@ -280,7 +280,7 @@ func TestWriteJSONProtocolDouble(t *testing.T) { trans := NewTMemoryBuffer() p := NewTJSONProtocol(trans) for _, value := range DOUBLE_VALUES { - if e := p.WriteDouble(value); e != nil { + if e := p.WriteDouble(context.Background(), value); e != nil { t.Fatalf("Unable to write %s value %v due to error: %s", thetype, value, e.Error()) } if e := p.Flush(context.Background()); e != nil { @@ -322,7 +322,7 @@ func TestReadJSONProtocolDouble(t *testing.T) { trans.WriteString(n.String()) trans.Flush(context.Background()) s := trans.String() - v, e := p.ReadDouble() + v, e := p.ReadDouble(context.Background()) if e != nil { t.Fatalf("Unable to read %s value %v due to error: %s", thetype, value, e.Error()) } @@ -356,7 +356,7 @@ func TestWriteJSONProtocolString(t *testing.T) { trans := NewTMemoryBuffer() p := NewTJSONProtocol(trans) for _, value := range STRING_VALUES { - if e := p.WriteString(value); e != nil { + if e := p.WriteString(context.Background(), value); e != nil { t.Fatalf("Unable to write %s value %v due to error: %s", thetype, value, e.Error()) } if e := p.Flush(context.Background()); e != nil { @@ -383,7 +383,7 @@ func TestReadJSONProtocolString(t *testing.T) { trans.WriteString(jsonQuote(value)) trans.Flush(context.Background()) s := trans.String() - v, e := p.ReadString() + v, e := p.ReadString(context.Background()) if e != nil { t.Fatalf("Unable to read %s value %v due to error: %s", thetype, value, e.Error()) } @@ -407,7 +407,7 @@ func TestWriteJSONProtocolBinary(t *testing.T) { b64String := string(b64value) trans := NewTMemoryBuffer() p := NewTJSONProtocol(trans) - if e := p.WriteBinary(value); e != nil { + if e := p.WriteBinary(context.Background(), value); e != nil { t.Fatalf("Unable to write %s value %v due to error: %s", thetype, value, e.Error()) } if e := p.Flush(context.Background()); e != nil { @@ -418,7 +418,7 @@ func TestWriteJSONProtocolBinary(t *testing.T) { if s != expectedString { t.Fatalf("Bad value for %s %v\n wrote: \"%v\"\nexpected: \"%v\"", thetype, value, s, expectedString) } - v1, err := p.ReadBinary() + v1, err := p.ReadBinary(context.Background()) if err != nil { t.Fatalf("Unable to read binary: %s", err.Error()) } @@ -444,7 +444,7 @@ func TestReadJSONProtocolBinary(t *testing.T) { trans.WriteString(jsonQuote(b64String)) trans.Flush(context.Background()) s := trans.String() - v, e := p.ReadBinary() + v, e := p.ReadBinary(context.Background()) if e != nil { t.Fatalf("Unable to read %s value %v due to error: %s", thetype, value, e.Error()) } @@ -468,13 +468,13 @@ func TestWriteJSONProtocolList(t *testing.T) { thetype := "list" trans := NewTMemoryBuffer() p := NewTJSONProtocol(trans) - p.WriteListBegin(TType(DOUBLE), len(DOUBLE_VALUES)) + p.WriteListBegin(context.Background(), TType(DOUBLE), len(DOUBLE_VALUES)) for _, value := range DOUBLE_VALUES { - if e := p.WriteDouble(value); e != nil { + if e := p.WriteDouble(context.Background(), value); e != nil { t.Fatalf("Unable to write %s value %v due to error: %s", thetype, value, e.Error()) } } - p.WriteListEnd() + p.WriteListEnd(context.Background()) if e := p.Flush(context.Background()); e != nil { t.Fatalf("Unable to write %s due to error flushing: %s", thetype, e.Error()) } @@ -522,13 +522,13 @@ func TestWriteJSONProtocolSet(t *testing.T) { thetype := "set" trans := NewTMemoryBuffer() p := NewTJSONProtocol(trans) - p.WriteSetBegin(TType(DOUBLE), len(DOUBLE_VALUES)) + p.WriteSetBegin(context.Background(), TType(DOUBLE), len(DOUBLE_VALUES)) for _, value := range DOUBLE_VALUES { - if e := p.WriteDouble(value); e != nil { + if e := p.WriteDouble(context.Background(), value); e != nil { t.Fatalf("Unable to write %s value %v due to error: %s", thetype, value, e.Error()) } } - p.WriteSetEnd() + p.WriteSetEnd(context.Background()) if e := p.Flush(context.Background()); e != nil { t.Fatalf("Unable to write %s due to error flushing: %s", thetype, e.Error()) } @@ -576,16 +576,16 @@ func TestWriteJSONProtocolMap(t *testing.T) { thetype := "map" trans := NewTMemoryBuffer() p := NewTJSONProtocol(trans) - p.WriteMapBegin(TType(I32), TType(DOUBLE), len(DOUBLE_VALUES)) + p.WriteMapBegin(context.Background(), TType(I32), TType(DOUBLE), len(DOUBLE_VALUES)) for k, value := range DOUBLE_VALUES { - if e := p.WriteI32(int32(k)); e != nil { + if e := p.WriteI32(context.Background(), int32(k)); e != nil { t.Fatalf("Unable to write %s key int32 value %v due to error: %s", thetype, k, e.Error()) } - if e := p.WriteDouble(value); e != nil { + if e := p.WriteDouble(context.Background(), value); e != nil { t.Fatalf("Unable to write %s value float64 value %v due to error: %s", thetype, value, e.Error()) } } - p.WriteMapEnd() + p.WriteMapEnd(context.Background()) if e := p.Flush(context.Background()); e != nil { t.Fatalf("Unable to write %s due to error flushing: %s", thetype, e.Error()) } @@ -593,7 +593,7 @@ func TestWriteJSONProtocolMap(t *testing.T) { if str[0] != '[' || str[len(str)-1] != ']' { t.Fatalf("Bad value for %s, wrote: %v, in go: %v", thetype, str, DOUBLE_VALUES) } - expectedKeyType, expectedValueType, expectedSize, err := p.ReadMapBegin() + expectedKeyType, expectedValueType, expectedSize, err := p.ReadMapBegin(context.Background()) if err != nil { t.Fatalf("Error while reading map begin: %s", err.Error()) } @@ -607,14 +607,14 @@ func TestWriteJSONProtocolMap(t *testing.T) { t.Fatal("Expected map size of ", len(DOUBLE_VALUES), ", but was ", expectedSize) } for k, value := range DOUBLE_VALUES { - ik, err := p.ReadI32() + ik, err := p.ReadI32(context.Background()) if err != nil { - t.Fatalf("Bad key for %s index %v, wrote: %v, expected: %v, error: %s", thetype, k, ik, string(k), err.Error()) + t.Fatalf("Bad key for %s index %v, wrote: %v, expected: %v, error: %s", thetype, k, ik, k, err.Error()) } if int(ik) != k { t.Fatalf("Bad key for %s index %v, wrote: %v, expected: %v", thetype, k, ik, k) } - dv, err := p.ReadDouble() + dv, err := p.ReadDouble(context.Background()) if err != nil { t.Fatalf("Bad value for %s index %v, wrote: %v, expected: %v, error: %s", thetype, k, dv, value, err.Error()) } @@ -642,9 +642,13 @@ func TestWriteJSONProtocolMap(t *testing.T) { } } } - err = p.ReadMapEnd() + err = p.ReadMapEnd(context.Background()) if err != nil { t.Fatalf("Error while reading map end: %s", err.Error()) } trans.Close() } + +func TestTJSONProtocolUnmatchedBeginEnd(t *testing.T) { + UnmatchedBeginEndProtocolTest(t, NewTJSONProtocolFactory()) +} diff --git a/lib/go/thrift/logger.go b/lib/go/thrift/logger.go new file mode 100644 index 00000000000..c42aac998b7 --- /dev/null +++ b/lib/go/thrift/logger.go @@ -0,0 +1,69 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package thrift + +import ( + "log" + "os" + "testing" +) + +// Logger is a simple wrapper of a logging function. +// +// In reality the users might actually use different logging libraries, and they +// are not always compatible with each other. +// +// Logger is meant to be a simple common ground that it's easy to wrap whatever +// logging library they use into. +// +// See https://issues.apache.org/jira/browse/THRIFT-4985 for the design +// discussion behind it. +type Logger func(msg string) + +// NopLogger is a Logger implementation that does nothing. +func NopLogger(msg string) {} + +// StdLogger wraps stdlib log package into a Logger. +// +// If logger passed in is nil, it will fallback to use stderr and default flags. +func StdLogger(logger *log.Logger) Logger { + if logger == nil { + logger = log.New(os.Stderr, "", log.LstdFlags) + } + return func(msg string) { + logger.Print(msg) + } +} + +// TestLogger is a Logger implementation can be used in test codes. +// +// It fails the test when being called. +func TestLogger(tb testing.TB) Logger { + return func(msg string) { + tb.Errorf("logger called with msg: %q", msg) + } +} + +func fallbackLogger(logger Logger) Logger { + if logger == nil { + return StdLogger(nil) + } + return logger +} diff --git a/lib/go/thrift/middleware.go b/lib/go/thrift/middleware.go new file mode 100644 index 00000000000..8a788df02be --- /dev/null +++ b/lib/go/thrift/middleware.go @@ -0,0 +1,109 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package thrift + +import "context" + +// ProcessorMiddleware is a function that can be passed to WrapProcessor to wrap the +// TProcessorFunctions for that TProcessor. +// +// Middlewares are passed in the name of the function as set in the processor +// map of the TProcessor. +type ProcessorMiddleware func(name string, next TProcessorFunction) TProcessorFunction + +// WrapProcessor takes an existing TProcessor and wraps each of its inner +// TProcessorFunctions with the middlewares passed in and returns it. +// +// Middlewares will be called in the order that they are defined: +// +// 1. Middlewares[0] +// 2. Middlewares[1] +// ... +// N. Middlewares[n] +func WrapProcessor(processor TProcessor, middlewares ...ProcessorMiddleware) TProcessor { + for name, processorFunc := range processor.ProcessorMap() { + wrapped := processorFunc + // Add middlewares in reverse so the first in the list is the outermost. + for i := len(middlewares) - 1; i >= 0; i-- { + wrapped = middlewares[i](name, wrapped) + } + processor.AddToProcessorMap(name, wrapped) + } + return processor +} + +// WrappedTProcessorFunction is a convenience struct that implements the +// TProcessorFunction interface that can be used when implementing custom +// Middleware. +type WrappedTProcessorFunction struct { + // Wrapped is called by WrappedTProcessorFunction.Process and should be a + // "wrapped" call to a base TProcessorFunc.Process call. + Wrapped func(ctx context.Context, seqId int32, in, out TProtocol) (bool, TException) +} + +// Process implements the TProcessorFunction interface using p.Wrapped. +func (p WrappedTProcessorFunction) Process(ctx context.Context, seqID int32, in, out TProtocol) (bool, TException) { + return p.Wrapped(ctx, seqID, in, out) +} + +// verify that WrappedTProcessorFunction implements TProcessorFunction +var ( + _ TProcessorFunction = WrappedTProcessorFunction{} + _ TProcessorFunction = (*WrappedTProcessorFunction)(nil) +) + +// ClientMiddleware can be passed to WrapClient in order to wrap TClient calls +// with custom middleware. +type ClientMiddleware func(TClient) TClient + +// WrappedTClient is a convenience struct that implements the TClient interface +// using inner Wrapped function. +// +// This is provided to aid in developing ClientMiddleware. +type WrappedTClient struct { + Wrapped func(ctx context.Context, method string, args, result TStruct) (ResponseMeta, error) +} + +// Call implements the TClient interface by calling and returning c.Wrapped. +func (c WrappedTClient) Call(ctx context.Context, method string, args, result TStruct) (ResponseMeta, error) { + return c.Wrapped(ctx, method, args, result) +} + +// verify that WrappedTClient implements TClient +var ( + _ TClient = WrappedTClient{} + _ TClient = (*WrappedTClient)(nil) +) + +// WrapClient wraps the given TClient in the given middlewares. +// +// Middlewares will be called in the order that they are defined: +// +// 1. Middlewares[0] +// 2. Middlewares[1] +// ... +// N. Middlewares[n] +func WrapClient(client TClient, middlewares ...ClientMiddleware) TClient { + // Add middlewares in reverse so the first in the list is the outermost. + for i := len(middlewares) - 1; i >= 0; i-- { + client = middlewares[i](client) + } + return client +} diff --git a/lib/go/thrift/middleware_test.go b/lib/go/thrift/middleware_test.go new file mode 100644 index 00000000000..6c4058fe133 --- /dev/null +++ b/lib/go/thrift/middleware_test.go @@ -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. + */ + +package thrift + +import ( + "context" + "testing" +) + +type counter struct { + count int +} + +func (c *counter) incr() { + c.count++ +} + +func newCounter(t *testing.T) *counter { + c := counter{} + if c.count != 0 { + t.Fatal("Unexpected initial count.") + } + return &c +} + +func testProcessorMiddleware(c *counter) ProcessorMiddleware { + return func(name string, next TProcessorFunction) TProcessorFunction { + return WrappedTProcessorFunction{ + Wrapped: func(ctx context.Context, seqId int32, in, out TProtocol) (bool, TException) { + c.incr() + return next.Process(ctx, seqId, in, out) + }, + } + } +} + +func testClientMiddleware(c *counter) ClientMiddleware { + return func(next TClient) TClient { + return WrappedTClient{ + Wrapped: func(ctx context.Context, method string, args, result TStruct) (ResponseMeta, error) { + c.incr() + return next.Call(ctx, method, args, result) + }, + } + } +} + +func TestWrapProcessor(t *testing.T) { + name := "test" + processor := &mockWrappableProcessor{ + ProcessorFuncs: map[string]TProcessorFunction{ + name: WrappedTProcessorFunction{ + Wrapped: func(ctx context.Context, seqId int32, in, out TProtocol) (bool, TException) { + return true, nil + }, + }, + }, + } + c := newCounter(t) + ctx := setMockWrappableProcessorName(context.Background(), name) + wrapped := WrapProcessor(processor, testProcessorMiddleware(c)) + wrapped.Process(ctx, nil, nil) + if c.count != 1 { + t.Fatalf("Unexpected count value %v", c.count) + } +} + +func TestWrapTMultiplexedProcessor(t *testing.T) { + name := "test" + processorName := "foo" + c := newCounter(t) + processor := &TMultiplexedProcessor{} + processor.RegisterDefault(&mockWrappableProcessor{ + ProcessorFuncs: map[string]TProcessorFunction{ + name: WrappedTProcessorFunction{ + Wrapped: func(ctx context.Context, seqId int32, in, out TProtocol) (bool, TException) { + return true, nil + }, + }, + }, + }) + processor.RegisterProcessor(processorName, &mockWrappableProcessor{ + ProcessorFuncs: map[string]TProcessorFunction{ + name: WrappedTProcessorFunction{ + Wrapped: func(ctx context.Context, seqId int32, in, out TProtocol) (bool, TException) { + return true, nil + }, + }, + }, + }) + wrapped := WrapProcessor(processor, testProcessorMiddleware(c)) + ctx := setMockWrappableProcessorName(context.Background(), name) + in := NewStoredMessageProtocol(nil, name, 1, 1) + wrapped.Process(ctx, in, nil) + if c.count != 1 { + t.Fatalf("Unexpected count value %v", c.count) + } + + in = NewStoredMessageProtocol(nil, processorName+MULTIPLEXED_SEPARATOR+name, 1, 1) + wrapped.Process(ctx, in, nil) + if c.count != 2 { + t.Fatalf("Unexpected count value %v", c.count) + } +} + +func TestWrapClient(t *testing.T) { + client := WrappedTClient{ + Wrapped: func(ctx context.Context, method string, args, result TStruct) (ResponseMeta, error) { + return ResponseMeta{}, nil + }, + } + c := newCounter(t) + wrapped := WrapClient(client, testClientMiddleware(c)) + wrapped.Call(context.Background(), "test", nil, nil) + if c.count != 1 { + t.Fatalf("Unexpected count value %v", c.count) + } +} diff --git a/lib/go/thrift/multiplexed_protocol.go b/lib/go/thrift/multiplexed_protocol.go index d028a30b333..cacbf6bef3e 100644 --- a/lib/go/thrift/multiplexed_protocol.go +++ b/lib/go/thrift/multiplexed_protocol.go @@ -68,11 +68,11 @@ func NewTMultiplexedProtocol(protocol TProtocol, serviceName string) *TMultiplex } } -func (t *TMultiplexedProtocol) WriteMessageBegin(name string, typeId TMessageType, seqid int32) error { +func (t *TMultiplexedProtocol) WriteMessageBegin(ctx context.Context, name string, typeId TMessageType, seqid int32) error { if typeId == CALL || typeId == ONEWAY { - return t.TProtocol.WriteMessageBegin(t.serviceName+MULTIPLEXED_SEPARATOR+name, typeId, seqid) + return t.TProtocol.WriteMessageBegin(ctx, t.serviceName+MULTIPLEXED_SEPARATOR+name, typeId, seqid) } else { - return t.TProtocol.WriteMessageBegin(name, typeId, seqid) + return t.TProtocol.WriteMessageBegin(ctx, name, typeId, seqid) } } @@ -117,6 +117,67 @@ func NewTMultiplexedProcessor() *TMultiplexedProcessor { } } +// ProcessorMap returns a mapping of "{ProcessorName}{MULTIPLEXED_SEPARATOR}{FunctionName}" +// to TProcessorFunction for any registered processors. If there is also a +// DefaultProcessor, the keys for the methods on that processor will simply be +// "{FunctionName}". If the TMultiplexedProcessor has both a DefaultProcessor and +// other registered processors, then the keys will be a mix of both formats. +// +// The implementation differs with other TProcessors in that the map returned is +// a new map, while most TProcessors just return their internal mapping directly. +// This means that edits to the map returned by this implementation of ProcessorMap +// will not affect the underlying mapping within the TMultiplexedProcessor. +func (t *TMultiplexedProcessor) ProcessorMap() map[string]TProcessorFunction { + processorFuncMap := make(map[string]TProcessorFunction) + for name, processor := range t.serviceProcessorMap { + for method, processorFunc := range processor.ProcessorMap() { + processorFuncName := name + MULTIPLEXED_SEPARATOR + method + processorFuncMap[processorFuncName] = processorFunc + } + } + if t.DefaultProcessor != nil { + for method, processorFunc := range t.DefaultProcessor.ProcessorMap() { + processorFuncMap[method] = processorFunc + } + } + return processorFuncMap +} + +// AddToProcessorMap updates the underlying TProcessor ProccessorMaps depending on +// the format of "name". +// +// If "name" is in the format "{ProcessorName}{MULTIPLEXED_SEPARATOR}{FunctionName}", +// then it sets the given TProcessorFunction on the inner TProcessor with the +// ProcessorName component using the FunctionName component. +// +// If "name" is just in the format "{FunctionName}", that is to say there is no +// MULTIPLEXED_SEPARATOR, and the TMultiplexedProcessor has a DefaultProcessor +// configured, then it will set the given TProcessorFunction on the DefaultProcessor +// using the given name. +// +// If there is not a TProcessor available for the given name, then this function +// does nothing. This can happen when there is no TProcessor registered for +// the given ProcessorName or if all that is given is the FunctionName and there +// is no DefaultProcessor set. +func (t *TMultiplexedProcessor) AddToProcessorMap(name string, processorFunc TProcessorFunction) { + components := strings.SplitN(name, MULTIPLEXED_SEPARATOR, 2) + if len(components) != 2 { + if t.DefaultProcessor != nil && len(components) == 1 { + t.DefaultProcessor.AddToProcessorMap(components[0], processorFunc) + } + return + } + processorName := components[0] + funcName := components[1] + if processor, ok := t.serviceProcessorMap[processorName]; ok { + processor.AddToProcessorMap(funcName, processorFunc) + } + +} + +// verify that TMultiplexedProcessor implements TProcessor +var _ TProcessor = (*TMultiplexedProcessor)(nil) + func (t *TMultiplexedProcessor) RegisterDefault(processor TProcessor) { t.DefaultProcessor = processor } @@ -129,12 +190,12 @@ func (t *TMultiplexedProcessor) RegisterProcessor(name string, processor TProces } func (t *TMultiplexedProcessor) Process(ctx context.Context, in, out TProtocol) (bool, TException) { - name, typeId, seqid, err := in.ReadMessageBegin() + name, typeId, seqid, err := in.ReadMessageBegin(ctx) if err != nil { - return false, err + return false, NewTProtocolException(err) } if typeId != CALL && typeId != ONEWAY { - return false, fmt.Errorf("Unexpected message type %v", typeId) + return false, NewTProtocolException(fmt.Errorf("Unexpected message type %v", typeId)) } //extract the service name v := strings.SplitN(name, MULTIPLEXED_SEPARATOR, 2) @@ -143,11 +204,17 @@ func (t *TMultiplexedProcessor) Process(ctx context.Context, in, out TProtocol) smb := NewStoredMessageProtocol(in, name, typeId, seqid) return t.DefaultProcessor.Process(ctx, smb, out) } - return false, fmt.Errorf("Service name not found in message name: %s. Did you forget to use a TMultiplexProtocol in your client?", name) + return false, NewTProtocolException(fmt.Errorf( + "Service name not found in message name: %s. Did you forget to use a TMultiplexProtocol in your client?", + name, + )) } actualProcessor, ok := t.serviceProcessorMap[v[0]] if !ok { - return false, fmt.Errorf("Service name not found: %s. Did you forget to call registerProcessor()?", v[0]) + return false, NewTProtocolException(fmt.Errorf( + "Service name not found: %s. Did you forget to call registerProcessor()?", + v[0], + )) } smb := NewStoredMessageProtocol(in, v[1], typeId, seqid) return actualProcessor.Process(ctx, smb, out) @@ -165,6 +232,6 @@ func NewStoredMessageProtocol(protocol TProtocol, name string, typeId TMessageTy return &storedMessageProtocol{protocol, name, typeId, seqid} } -func (s *storedMessageProtocol) ReadMessageBegin() (name string, typeId TMessageType, seqid int32, err error) { +func (s *storedMessageProtocol) ReadMessageBegin(ctx context.Context) (name string, typeId TMessageType, seqid int32, err error) { return s.name, s.typeId, s.seqid, nil } diff --git a/lib/go/thrift/multiplexed_protocol_test.go b/lib/go/thrift/multiplexed_protocol_test.go new file mode 100644 index 00000000000..8e70ac5976f --- /dev/null +++ b/lib/go/thrift/multiplexed_protocol_test.go @@ -0,0 +1,53 @@ +package thrift + +import ( + "context" + "strings" + "testing" +) + +func TestMultiplexedProcessorMap(t *testing.T) { + name := "test" + processorName := "foo" + processor := &TMultiplexedProcessor{} + processor.RegisterDefault(&mockWrappableProcessor{ + ProcessorFuncs: map[string]TProcessorFunction{ + name: WrappedTProcessorFunction{ + Wrapped: func(ctx context.Context, seqId int32, in, out TProtocol) (bool, TException) { + return true, nil + }, + }, + }, + }) + processor.RegisterProcessor(processorName, &mockWrappableProcessor{ + ProcessorFuncs: map[string]TProcessorFunction{ + name: WrappedTProcessorFunction{ + Wrapped: func(ctx context.Context, seqId int32, in, out TProtocol) (bool, TException) { + return true, nil + }, + }, + }, + }) + + processorMap := processor.ProcessorMap() + if len(processorMap) != 2 { + t.Fatalf("Wrong processor map size %#v", processorMap) + } + for k := range processorMap { + components := strings.SplitN(k, MULTIPLEXED_SEPARATOR, 2) + if len(components) == 1 { + if components[0] != name { + t.Fatalf("Wrong name for default processor func, expected %q, got %q", name, components[0]) + } + } else if len(components) == 2 { + if components[0] != processorName { + t.Errorf("Wrong processor name, expected %q, got %q", processorName, components[0]) + } + if components[1] != name { + t.Errorf("Wrong name for processor func, expected %q, got %q", name, components[1]) + } + } else { + t.Fatalf("Wrong number of components %#v", components) + } + } +} diff --git a/lib/go/thrift/numeric.go b/lib/go/thrift/numeric.go index aa8daa9b54f..e4512d204c0 100644 --- a/lib/go/thrift/numeric.go +++ b/lib/go/thrift/numeric.go @@ -69,14 +69,14 @@ func NewNumericFromDouble(dValue float64) Numeric { func NewNumericFromI64(iValue int64) Numeric { dValue := float64(iValue) - sValue := string(iValue) + sValue := strconv.FormatInt(iValue, 10) isNil := false return &numeric{iValue: iValue, dValue: dValue, sValue: sValue, isNil: isNil} } func NewNumericFromI32(iValue int32) Numeric { dValue := float64(iValue) - sValue := string(iValue) + sValue := strconv.FormatInt(int64(iValue), 10) isNil := false return &numeric{iValue: int64(iValue), dValue: dValue, sValue: sValue, isNil: isNil} } diff --git a/lib/go/thrift/pointerize.go b/lib/go/thrift/pointerize.go index 8d6b2c2159d..fb564ea8193 100644 --- a/lib/go/thrift/pointerize.go +++ b/lib/go/thrift/pointerize.go @@ -41,6 +41,8 @@ package thrift func Float32Ptr(v float32) *float32 { return &v } func Float64Ptr(v float64) *float64 { return &v } func IntPtr(v int) *int { return &v } +func Int8Ptr(v int8) *int8 { return &v } +func Int16Ptr(v int16) *int16 { return &v } func Int32Ptr(v int32) *int32 { return &v } func Int64Ptr(v int64) *int64 { return &v } func StringPtr(v string) *string { return &v } diff --git a/lib/go/thrift/processor_factory.go b/lib/go/thrift/processor_factory.go index e4b132b307c..245a3ccfc98 100644 --- a/lib/go/thrift/processor_factory.go +++ b/lib/go/thrift/processor_factory.go @@ -25,6 +25,16 @@ import "context" // writes to some output stream. type TProcessor interface { Process(ctx context.Context, in, out TProtocol) (bool, TException) + + // ProcessorMap returns a map of thrift method names to TProcessorFunctions. + ProcessorMap() map[string]TProcessorFunction + + // AddToProcessorMap adds the given TProcessorFunction to the internal + // processor map at the given key. + // + // If one is already set at the given key, it will be replaced with the new + // TProcessorFunction. + AddToProcessorMap(string, TProcessorFunction) } type TProcessorFunction interface { diff --git a/lib/go/thrift/protocol.go b/lib/go/thrift/protocol.go index 615b7a4a8f3..0a69bd4162d 100644 --- a/lib/go/thrift/protocol.go +++ b/lib/go/thrift/protocol.go @@ -31,50 +31,50 @@ const ( ) type TProtocol interface { - WriteMessageBegin(name string, typeId TMessageType, seqid int32) error - WriteMessageEnd() error - WriteStructBegin(name string) error - WriteStructEnd() error - WriteFieldBegin(name string, typeId TType, id int16) error - WriteFieldEnd() error - WriteFieldStop() error - WriteMapBegin(keyType TType, valueType TType, size int) error - WriteMapEnd() error - WriteListBegin(elemType TType, size int) error - WriteListEnd() error - WriteSetBegin(elemType TType, size int) error - WriteSetEnd() error - WriteBool(value bool) error - WriteByte(value int8) error - WriteI16(value int16) error - WriteI32(value int32) error - WriteI64(value int64) error - WriteDouble(value float64) error - WriteString(value string) error - WriteBinary(value []byte) error + WriteMessageBegin(ctx context.Context, name string, typeId TMessageType, seqid int32) error + WriteMessageEnd(ctx context.Context) error + WriteStructBegin(ctx context.Context, name string) error + WriteStructEnd(ctx context.Context) error + WriteFieldBegin(ctx context.Context, name string, typeId TType, id int16) error + WriteFieldEnd(ctx context.Context) error + WriteFieldStop(ctx context.Context) error + WriteMapBegin(ctx context.Context, keyType TType, valueType TType, size int) error + WriteMapEnd(ctx context.Context) error + WriteListBegin(ctx context.Context, elemType TType, size int) error + WriteListEnd(ctx context.Context) error + WriteSetBegin(ctx context.Context, elemType TType, size int) error + WriteSetEnd(ctx context.Context) error + WriteBool(ctx context.Context, value bool) error + WriteByte(ctx context.Context, value int8) error + WriteI16(ctx context.Context, value int16) error + WriteI32(ctx context.Context, value int32) error + WriteI64(ctx context.Context, value int64) error + WriteDouble(ctx context.Context, value float64) error + WriteString(ctx context.Context, value string) error + WriteBinary(ctx context.Context, value []byte) error - ReadMessageBegin() (name string, typeId TMessageType, seqid int32, err error) - ReadMessageEnd() error - ReadStructBegin() (name string, err error) - ReadStructEnd() error - ReadFieldBegin() (name string, typeId TType, id int16, err error) - ReadFieldEnd() error - ReadMapBegin() (keyType TType, valueType TType, size int, err error) - ReadMapEnd() error - ReadListBegin() (elemType TType, size int, err error) - ReadListEnd() error - ReadSetBegin() (elemType TType, size int, err error) - ReadSetEnd() error - ReadBool() (value bool, err error) - ReadByte() (value int8, err error) - ReadI16() (value int16, err error) - ReadI32() (value int32, err error) - ReadI64() (value int64, err error) - ReadDouble() (value float64, err error) - ReadString() (value string, err error) - ReadBinary() (value []byte, err error) + ReadMessageBegin(ctx context.Context) (name string, typeId TMessageType, seqid int32, err error) + ReadMessageEnd(ctx context.Context) error + ReadStructBegin(ctx context.Context) (name string, err error) + ReadStructEnd(ctx context.Context) error + ReadFieldBegin(ctx context.Context) (name string, typeId TType, id int16, err error) + ReadFieldEnd(ctx context.Context) error + ReadMapBegin(ctx context.Context) (keyType TType, valueType TType, size int, err error) + ReadMapEnd(ctx context.Context) error + ReadListBegin(ctx context.Context) (elemType TType, size int, err error) + ReadListEnd(ctx context.Context) error + ReadSetBegin(ctx context.Context) (elemType TType, size int, err error) + ReadSetEnd(ctx context.Context) error + ReadBool(ctx context.Context) (value bool, err error) + ReadByte(ctx context.Context) (value int8, err error) + ReadI16(ctx context.Context) (value int16, err error) + ReadI32(ctx context.Context) (value int32, err error) + ReadI64(ctx context.Context) (value int64, err error) + ReadDouble(ctx context.Context) (value float64, err error) + ReadString(ctx context.Context) (value string, err error) + ReadBinary(ctx context.Context) (value []byte, err error) - Skip(fieldType TType) (err error) + Skip(ctx context.Context, fieldType TType) (err error) Flush(ctx context.Context) (err error) Transport() TTransport @@ -84,94 +84,92 @@ type TProtocol interface { const DEFAULT_RECURSION_DEPTH = 64 // Skips over the next data element from the provided input TProtocol object. -func SkipDefaultDepth(prot TProtocol, typeId TType) (err error) { - return Skip(prot, typeId, DEFAULT_RECURSION_DEPTH) +func SkipDefaultDepth(ctx context.Context, prot TProtocol, typeId TType) (err error) { + return Skip(ctx, prot, typeId, DEFAULT_RECURSION_DEPTH) } // Skips over the next data element from the provided input TProtocol object. -func Skip(self TProtocol, fieldType TType, maxDepth int) (err error) { +func Skip(ctx context.Context, self TProtocol, fieldType TType, maxDepth int) (err error) { if maxDepth <= 0 { return NewTProtocolExceptionWithType(DEPTH_LIMIT, errors.New("Depth limit exceeded")) } switch fieldType { - case STOP: - return case BOOL: - _, err = self.ReadBool() + _, err = self.ReadBool(ctx) return case BYTE: - _, err = self.ReadByte() + _, err = self.ReadByte(ctx) return case I16: - _, err = self.ReadI16() + _, err = self.ReadI16(ctx) return case I32: - _, err = self.ReadI32() + _, err = self.ReadI32(ctx) return case I64: - _, err = self.ReadI64() + _, err = self.ReadI64(ctx) return case DOUBLE: - _, err = self.ReadDouble() + _, err = self.ReadDouble(ctx) return case STRING: - _, err = self.ReadString() + _, err = self.ReadString(ctx) return case STRUCT: - if _, err = self.ReadStructBegin(); err != nil { + if _, err = self.ReadStructBegin(ctx); err != nil { return err } for { - _, typeId, _, _ := self.ReadFieldBegin() + _, typeId, _, _ := self.ReadFieldBegin(ctx) if typeId == STOP { break } - err := Skip(self, typeId, maxDepth-1) + err := Skip(ctx, self, typeId, maxDepth-1) if err != nil { return err } - self.ReadFieldEnd() + self.ReadFieldEnd(ctx) } - return self.ReadStructEnd() + return self.ReadStructEnd(ctx) case MAP: - keyType, valueType, size, err := self.ReadMapBegin() + keyType, valueType, size, err := self.ReadMapBegin(ctx) if err != nil { return err } for i := 0; i < size; i++ { - err := Skip(self, keyType, maxDepth-1) + err := Skip(ctx, self, keyType, maxDepth-1) if err != nil { return err } - self.Skip(valueType) + self.Skip(ctx, valueType) } - return self.ReadMapEnd() + return self.ReadMapEnd(ctx) case SET: - elemType, size, err := self.ReadSetBegin() + elemType, size, err := self.ReadSetBegin(ctx) if err != nil { return err } for i := 0; i < size; i++ { - err := Skip(self, elemType, maxDepth-1) + err := Skip(ctx, self, elemType, maxDepth-1) if err != nil { return err } } - return self.ReadSetEnd() + return self.ReadSetEnd(ctx) case LIST: - elemType, size, err := self.ReadListBegin() + elemType, size, err := self.ReadListBegin(ctx) if err != nil { return err } for i := 0; i < size; i++ { - err := Skip(self, elemType, maxDepth-1) + err := Skip(ctx, self, elemType, maxDepth-1) if err != nil { return err } } - return self.ReadListEnd() + return self.ReadListEnd(ctx) default: return NewTProtocolExceptionWithType(INVALID_DATA, errors.New(fmt.Sprintf("Unknown data type %d", fieldType))) } diff --git a/lib/go/thrift/protocol_exception.go b/lib/go/thrift/protocol_exception.go index 29ab75d9215..9dcf4bfd94c 100644 --- a/lib/go/thrift/protocol_exception.go +++ b/lib/go/thrift/protocol_exception.go @@ -21,6 +21,7 @@ package thrift import ( "encoding/base64" + "errors" ) // Thrift Protocol exception @@ -40,8 +41,15 @@ const ( ) type tProtocolException struct { - typeId int - message string + typeId int + err error + msg string +} + +var _ TProtocolException = (*tProtocolException)(nil) + +func (tProtocolException) TExceptionType() TExceptionType { + return TExceptionTypeProtocol } func (p *tProtocolException) TypeId() int { @@ -49,29 +57,48 @@ func (p *tProtocolException) TypeId() int { } func (p *tProtocolException) String() string { - return p.message + return p.msg } func (p *tProtocolException) Error() string { - return p.message + return p.msg +} + +func (p *tProtocolException) Unwrap() error { + return p.err } func NewTProtocolException(err error) TProtocolException { if err == nil { return nil } + if e, ok := err.(TProtocolException); ok { return e } - if _, ok := err.(base64.CorruptInputError); ok { - return &tProtocolException{INVALID_DATA, err.Error()} + + if errors.As(err, new(base64.CorruptInputError)) { + return NewTProtocolExceptionWithType(INVALID_DATA, err) } - return &tProtocolException{UNKNOWN_PROTOCOL_EXCEPTION, err.Error()} + + return NewTProtocolExceptionWithType(UNKNOWN_PROTOCOL_EXCEPTION, err) } func NewTProtocolExceptionWithType(errType int, err error) TProtocolException { if err == nil { return nil } - return &tProtocolException{errType, err.Error()} + return &tProtocolException{ + typeId: errType, + err: err, + msg: err.Error(), + } +} + +func prependTProtocolException(prepend string, err TProtocolException) TProtocolException { + return &tProtocolException{ + typeId: err.TypeId(), + err: err, + msg: prepend + err.Error(), + } } diff --git a/lib/go/thrift/protocol_test.go b/lib/go/thrift/protocol_test.go index 944055c0b4a..caac78e9956 100644 --- a/lib/go/thrift/protocol_test.go +++ b/lib/go/thrift/protocol_test.go @@ -217,27 +217,31 @@ func ReadWriteProtocolTest(t *testing.T, protocolFactory TProtocolFactory) { ReadWriteByte(t, p, trans) trans.Close() } + + t.Run("UnmatchedBeginEnd", func(t *testing.T) { + UnmatchedBeginEndProtocolTest(t, protocolFactory) + }) } func ReadWriteBool(t testing.TB, p TProtocol, trans TTransport) { thetype := TType(BOOL) thelen := len(BOOL_VALUES) - err := p.WriteListBegin(thetype, thelen) + err := p.WriteListBegin(context.Background(), thetype, thelen) if err != nil { t.Errorf("%s: %T %T %q Error writing list begin: %q", "ReadWriteBool", p, trans, err, thetype) } for k, v := range BOOL_VALUES { - err = p.WriteBool(v) + err = p.WriteBool(context.Background(), v) if err != nil { t.Errorf("%s: %T %T %v Error writing bool in list at index %v: %v", "ReadWriteBool", p, trans, err, k, v) } } - p.WriteListEnd() + p.WriteListEnd(context.Background()) if err != nil { t.Errorf("%s: %T %T %v Error writing list end: %v", "ReadWriteBool", p, trans, err, BOOL_VALUES) } p.Flush(context.Background()) - thetype2, thelen2, err := p.ReadListBegin() + thetype2, thelen2, err := p.ReadListBegin(context.Background()) if err != nil { t.Errorf("%s: %T %T %v Error reading list: %v", "ReadWriteBool", p, trans, err, BOOL_VALUES) } @@ -251,7 +255,7 @@ func ReadWriteBool(t testing.TB, p TProtocol, trans TTransport) { } } for k, v := range BOOL_VALUES { - value, err := p.ReadBool() + value, err := p.ReadBool(context.Background()) if err != nil { t.Errorf("%s: %T %T %v Error reading bool at index %v: %v", "ReadWriteBool", p, trans, err, k, v) } @@ -259,7 +263,7 @@ func ReadWriteBool(t testing.TB, p TProtocol, trans TTransport) { t.Errorf("%s: index %v %v %v %v != %v", "ReadWriteBool", k, p, trans, v, value) } } - err = p.ReadListEnd() + err = p.ReadListEnd(context.Background()) if err != nil { t.Errorf("%s: %T %T Unable to read list end: %q", "ReadWriteBool", p, trans, err) } @@ -268,17 +272,17 @@ func ReadWriteBool(t testing.TB, p TProtocol, trans TTransport) { func ReadWriteByte(t testing.TB, p TProtocol, trans TTransport) { thetype := TType(BYTE) thelen := len(BYTE_VALUES) - err := p.WriteListBegin(thetype, thelen) + err := p.WriteListBegin(context.Background(), thetype, thelen) if err != nil { t.Errorf("%s: %T %T %q Error writing list begin: %q", "ReadWriteByte", p, trans, err, thetype) } for k, v := range BYTE_VALUES { - err = p.WriteByte(v) + err = p.WriteByte(context.Background(), v) if err != nil { t.Errorf("%s: %T %T %q Error writing byte in list at index %d: %q", "ReadWriteByte", p, trans, err, k, v) } } - err = p.WriteListEnd() + err = p.WriteListEnd(context.Background()) if err != nil { t.Errorf("%s: %T %T %q Error writing list end: %q", "ReadWriteByte", p, trans, err, BYTE_VALUES) } @@ -286,7 +290,7 @@ func ReadWriteByte(t testing.TB, p TProtocol, trans TTransport) { if err != nil { t.Errorf("%s: %T %T %q Error flushing list of bytes: %q", "ReadWriteByte", p, trans, err, BYTE_VALUES) } - thetype2, thelen2, err := p.ReadListBegin() + thetype2, thelen2, err := p.ReadListBegin(context.Background()) if err != nil { t.Errorf("%s: %T %T %q Error reading list: %q", "ReadWriteByte", p, trans, err, BYTE_VALUES) } @@ -300,7 +304,7 @@ func ReadWriteByte(t testing.TB, p TProtocol, trans TTransport) { } } for k, v := range BYTE_VALUES { - value, err := p.ReadByte() + value, err := p.ReadByte(context.Background()) if err != nil { t.Errorf("%s: %T %T %q Error reading byte at index %d: %q", "ReadWriteByte", p, trans, err, k, v) } @@ -308,7 +312,7 @@ func ReadWriteByte(t testing.TB, p TProtocol, trans TTransport) { t.Errorf("%s: %T %T %d != %d", "ReadWriteByte", p, trans, v, value) } } - err = p.ReadListEnd() + err = p.ReadListEnd(context.Background()) if err != nil { t.Errorf("%s: %T %T Unable to read list end: %q", "ReadWriteByte", p, trans, err) } @@ -317,13 +321,13 @@ func ReadWriteByte(t testing.TB, p TProtocol, trans TTransport) { func ReadWriteI16(t testing.TB, p TProtocol, trans TTransport) { thetype := TType(I16) thelen := len(INT16_VALUES) - p.WriteListBegin(thetype, thelen) + p.WriteListBegin(context.Background(), thetype, thelen) for _, v := range INT16_VALUES { - p.WriteI16(v) + p.WriteI16(context.Background(), v) } - p.WriteListEnd() + p.WriteListEnd(context.Background()) p.Flush(context.Background()) - thetype2, thelen2, err := p.ReadListBegin() + thetype2, thelen2, err := p.ReadListBegin(context.Background()) if err != nil { t.Errorf("%s: %T %T %q Error reading list: %q", "ReadWriteI16", p, trans, err, INT16_VALUES) } @@ -337,7 +341,7 @@ func ReadWriteI16(t testing.TB, p TProtocol, trans TTransport) { } } for k, v := range INT16_VALUES { - value, err := p.ReadI16() + value, err := p.ReadI16(context.Background()) if err != nil { t.Errorf("%s: %T %T %q Error reading int16 at index %d: %q", "ReadWriteI16", p, trans, err, k, v) } @@ -345,7 +349,7 @@ func ReadWriteI16(t testing.TB, p TProtocol, trans TTransport) { t.Errorf("%s: %T %T %d != %d", "ReadWriteI16", p, trans, v, value) } } - err = p.ReadListEnd() + err = p.ReadListEnd(context.Background()) if err != nil { t.Errorf("%s: %T %T Unable to read list end: %q", "ReadWriteI16", p, trans, err) } @@ -354,13 +358,13 @@ func ReadWriteI16(t testing.TB, p TProtocol, trans TTransport) { func ReadWriteI32(t testing.TB, p TProtocol, trans TTransport) { thetype := TType(I32) thelen := len(INT32_VALUES) - p.WriteListBegin(thetype, thelen) + p.WriteListBegin(context.Background(), thetype, thelen) for _, v := range INT32_VALUES { - p.WriteI32(v) + p.WriteI32(context.Background(), v) } - p.WriteListEnd() + p.WriteListEnd(context.Background()) p.Flush(context.Background()) - thetype2, thelen2, err := p.ReadListBegin() + thetype2, thelen2, err := p.ReadListBegin(context.Background()) if err != nil { t.Errorf("%s: %T %T %q Error reading list: %q", "ReadWriteI32", p, trans, err, INT32_VALUES) } @@ -374,7 +378,7 @@ func ReadWriteI32(t testing.TB, p TProtocol, trans TTransport) { } } for k, v := range INT32_VALUES { - value, err := p.ReadI32() + value, err := p.ReadI32(context.Background()) if err != nil { t.Errorf("%s: %T %T %q Error reading int32 at index %d: %q", "ReadWriteI32", p, trans, err, k, v) } @@ -390,13 +394,13 @@ func ReadWriteI32(t testing.TB, p TProtocol, trans TTransport) { func ReadWriteI64(t testing.TB, p TProtocol, trans TTransport) { thetype := TType(I64) thelen := len(INT64_VALUES) - p.WriteListBegin(thetype, thelen) + p.WriteListBegin(context.Background(), thetype, thelen) for _, v := range INT64_VALUES { - p.WriteI64(v) + p.WriteI64(context.Background(), v) } - p.WriteListEnd() + p.WriteListEnd(context.Background()) p.Flush(context.Background()) - thetype2, thelen2, err := p.ReadListBegin() + thetype2, thelen2, err := p.ReadListBegin(context.Background()) if err != nil { t.Errorf("%s: %T %T %q Error reading list: %q", "ReadWriteI64", p, trans, err, INT64_VALUES) } @@ -410,7 +414,7 @@ func ReadWriteI64(t testing.TB, p TProtocol, trans TTransport) { } } for k, v := range INT64_VALUES { - value, err := p.ReadI64() + value, err := p.ReadI64(context.Background()) if err != nil { t.Errorf("%s: %T %T %q Error reading int64 at index %d: %q", "ReadWriteI64", p, trans, err, k, v) } @@ -426,13 +430,13 @@ func ReadWriteI64(t testing.TB, p TProtocol, trans TTransport) { func ReadWriteDouble(t testing.TB, p TProtocol, trans TTransport) { thetype := TType(DOUBLE) thelen := len(DOUBLE_VALUES) - p.WriteListBegin(thetype, thelen) + p.WriteListBegin(context.Background(), thetype, thelen) for _, v := range DOUBLE_VALUES { - p.WriteDouble(v) + p.WriteDouble(context.Background(), v) } - p.WriteListEnd() + p.WriteListEnd(context.Background()) p.Flush(context.Background()) - thetype2, thelen2, err := p.ReadListBegin() + thetype2, thelen2, err := p.ReadListBegin(context.Background()) if err != nil { t.Errorf("%s: %T %T %v Error reading list: %v", "ReadWriteDouble", p, trans, err, DOUBLE_VALUES) } @@ -443,7 +447,7 @@ func ReadWriteDouble(t testing.TB, p TProtocol, trans TTransport) { t.Errorf("%s: %T %T len %v != len %v", "ReadWriteDouble", p, trans, thelen, thelen2) } for k, v := range DOUBLE_VALUES { - value, err := p.ReadDouble() + value, err := p.ReadDouble(context.Background()) if err != nil { t.Errorf("%s: %T %T %q Error reading double at index %d: %v", "ReadWriteDouble", p, trans, err, k, v) } @@ -455,7 +459,7 @@ func ReadWriteDouble(t testing.TB, p TProtocol, trans TTransport) { t.Errorf("%s: %T %T %v != %v", "ReadWriteDouble", p, trans, v, value) } } - err = p.ReadListEnd() + err = p.ReadListEnd(context.Background()) if err != nil { t.Errorf("%s: %T %T Unable to read list end: %q", "ReadWriteDouble", p, trans, err) } @@ -464,13 +468,13 @@ func ReadWriteDouble(t testing.TB, p TProtocol, trans TTransport) { func ReadWriteString(t testing.TB, p TProtocol, trans TTransport) { thetype := TType(STRING) thelen := len(STRING_VALUES) - p.WriteListBegin(thetype, thelen) + p.WriteListBegin(context.Background(), thetype, thelen) for _, v := range STRING_VALUES { - p.WriteString(v) + p.WriteString(context.Background(), v) } - p.WriteListEnd() + p.WriteListEnd(context.Background()) p.Flush(context.Background()) - thetype2, thelen2, err := p.ReadListBegin() + thetype2, thelen2, err := p.ReadListBegin(context.Background()) if err != nil { t.Errorf("%s: %T %T %q Error reading list: %q", "ReadWriteString", p, trans, err, STRING_VALUES) } @@ -484,7 +488,7 @@ func ReadWriteString(t testing.TB, p TProtocol, trans TTransport) { } } for k, v := range STRING_VALUES { - value, err := p.ReadString() + value, err := p.ReadString(context.Background()) if err != nil { t.Errorf("%s: %T %T %q Error reading string at index %d: %q", "ReadWriteString", p, trans, err, k, v) } @@ -499,9 +503,9 @@ func ReadWriteString(t testing.TB, p TProtocol, trans TTransport) { func ReadWriteBinary(t testing.TB, p TProtocol, trans TTransport) { v := protocol_bdata - p.WriteBinary(v) + p.WriteBinary(context.Background(), v) p.Flush(context.Background()) - value, err := p.ReadBinary() + value, err := p.ReadBinary(context.Background()) if err != nil { t.Errorf("%s: %T %T Unable to read binary: %s", "ReadWriteBinary", p, trans, err.Error()) } @@ -515,3 +519,88 @@ func ReadWriteBinary(t testing.TB, p TProtocol, trans TTransport) { } } } + +func UnmatchedBeginEndProtocolTest(t *testing.T, protocolFactory TProtocolFactory) { + // NOTE: not all protocol implementations do strict state check to + // return an error on unmatched Begin/End calls. + // This test is only meant to make sure that those unmatched Begin/End + // calls won't cause panic. There's no real "test" here. + trans := NewTMemoryBuffer() + t.Run("Read", func(t *testing.T) { + t.Run("Message", func(t *testing.T) { + trans.Reset() + p := protocolFactory.GetProtocol(trans) + p.ReadMessageEnd(context.Background()) + p.ReadMessageEnd(context.Background()) + }) + t.Run("Struct", func(t *testing.T) { + trans.Reset() + p := protocolFactory.GetProtocol(trans) + p.ReadStructEnd(context.Background()) + p.ReadStructEnd(context.Background()) + }) + t.Run("Field", func(t *testing.T) { + trans.Reset() + p := protocolFactory.GetProtocol(trans) + p.ReadFieldEnd(context.Background()) + p.ReadFieldEnd(context.Background()) + }) + t.Run("Map", func(t *testing.T) { + trans.Reset() + p := protocolFactory.GetProtocol(trans) + p.ReadMapEnd(context.Background()) + p.ReadMapEnd(context.Background()) + }) + t.Run("List", func(t *testing.T) { + trans.Reset() + p := protocolFactory.GetProtocol(trans) + p.ReadListEnd(context.Background()) + p.ReadListEnd(context.Background()) + }) + t.Run("Set", func(t *testing.T) { + trans.Reset() + p := protocolFactory.GetProtocol(trans) + p.ReadSetEnd(context.Background()) + p.ReadSetEnd(context.Background()) + }) + }) + t.Run("Write", func(t *testing.T) { + t.Run("Message", func(t *testing.T) { + trans.Reset() + p := protocolFactory.GetProtocol(trans) + p.WriteMessageEnd(context.Background()) + p.WriteMessageEnd(context.Background()) + }) + t.Run("Struct", func(t *testing.T) { + trans.Reset() + p := protocolFactory.GetProtocol(trans) + p.WriteStructEnd(context.Background()) + p.WriteStructEnd(context.Background()) + }) + t.Run("Field", func(t *testing.T) { + trans.Reset() + p := protocolFactory.GetProtocol(trans) + p.WriteFieldEnd(context.Background()) + p.WriteFieldEnd(context.Background()) + }) + t.Run("Map", func(t *testing.T) { + trans.Reset() + p := protocolFactory.GetProtocol(trans) + p.WriteMapEnd(context.Background()) + p.WriteMapEnd(context.Background()) + }) + t.Run("List", func(t *testing.T) { + trans.Reset() + p := protocolFactory.GetProtocol(trans) + p.WriteListEnd(context.Background()) + p.WriteListEnd(context.Background()) + }) + t.Run("Set", func(t *testing.T) { + trans.Reset() + p := protocolFactory.GetProtocol(trans) + p.WriteSetEnd(context.Background()) + p.WriteSetEnd(context.Background()) + }) + }) + trans.Close() +} diff --git a/lib/go/thrift/response_helper.go b/lib/go/thrift/response_helper.go new file mode 100644 index 00000000000..d884c6ac6c4 --- /dev/null +++ b/lib/go/thrift/response_helper.go @@ -0,0 +1,94 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package thrift + +import ( + "context" +) + +// See https://godoc.org/context#WithValue on why do we need the unexported typedefs. +type responseHelperKey struct{} + +// TResponseHelper defines a object with a set of helper functions that can be +// retrieved from the context object passed into server handler functions. +// +// Use GetResponseHelper to retrieve the injected TResponseHelper implementation +// from the context object. +// +// The zero value of TResponseHelper is valid with all helper functions being +// no-op. +type TResponseHelper struct { + // THeader related functions + *THeaderResponseHelper +} + +// THeaderResponseHelper defines THeader related TResponseHelper functions. +// +// The zero value of *THeaderResponseHelper is valid with all helper functions +// being no-op. +type THeaderResponseHelper struct { + proto *THeaderProtocol +} + +// NewTHeaderResponseHelper creates a new THeaderResponseHelper from the +// underlying TProtocol. +func NewTHeaderResponseHelper(proto TProtocol) *THeaderResponseHelper { + if hp, ok := proto.(*THeaderProtocol); ok { + return &THeaderResponseHelper{ + proto: hp, + } + } + return nil +} + +// SetHeader sets a response header. +// +// It's no-op if the underlying protocol/transport does not support THeader. +func (h *THeaderResponseHelper) SetHeader(key, value string) { + if h != nil && h.proto != nil { + h.proto.SetWriteHeader(key, value) + } +} + +// ClearHeaders clears all the response headers previously set. +// +// It's no-op if the underlying protocol/transport does not support THeader. +func (h *THeaderResponseHelper) ClearHeaders() { + if h != nil && h.proto != nil { + h.proto.ClearWriteHeaders() + } +} + +// GetResponseHelper retrieves the TResponseHelper implementation injected into +// the context object. +// +// If no helper was found in the context object, a nop helper with ok == false +// will be returned. +func GetResponseHelper(ctx context.Context) (helper TResponseHelper, ok bool) { + if v := ctx.Value(responseHelperKey{}); v != nil { + helper, ok = v.(TResponseHelper) + } + return +} + +// SetResponseHelper injects TResponseHelper into the context object. +func SetResponseHelper(ctx context.Context, helper TResponseHelper) context.Context { + return context.WithValue(ctx, responseHelperKey{}, helper) +} diff --git a/lib/go/thrift/response_helper_test.go b/lib/go/thrift/response_helper_test.go new file mode 100644 index 00000000000..69f76d36515 --- /dev/null +++ b/lib/go/thrift/response_helper_test.go @@ -0,0 +1,137 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package thrift + +import ( + "context" + "testing" +) + +func TestResponseHelperContext(t *testing.T) { + ctx := context.Background() + + t.Run( + "empty-noop", + func(t *testing.T) { + helper, ok := GetResponseHelper(ctx) + if ok { + t.Error("GetResponseHelper expected ok == false") + } + // Just make sure those function calls does not panic + helper.SetHeader("foo", "bar") + helper.ClearHeaders() + }, + ) + + t.Run( + "set-get", + func(t *testing.T) { + trans := NewTHeaderTransport(NewTMemoryBuffer()) + proto := NewTHeaderProtocol(trans) + ctx = SetResponseHelper( + ctx, + TResponseHelper{ + THeaderResponseHelper: NewTHeaderResponseHelper(proto), + }, + ) + helper, ok := GetResponseHelper(ctx) + if !ok { + t.Error("GetResponseHelper expected ok == true") + } + if helper.THeaderResponseHelper == nil { + t.Error("GetResponseHelper expected THeaderResponseHelper to be non-nil") + } + }, + ) +} + +func TestHeaderHelper(t *testing.T) { + t.Run( + "THeaderProtocol", + func(t *testing.T) { + trans := NewTHeaderTransport(NewTMemoryBuffer()) + proto := NewTHeaderProtocol(trans) + helper := NewTHeaderResponseHelper(proto) + + const ( + key = "key" + value = "value" + ) + helper.SetHeader(key, value) + if len(trans.writeHeaders) != 1 { + t.Errorf( + "Expected THeaderTransport.writeHeaders to be with size of 1, got %+v", + trans.writeHeaders, + ) + } + actual := trans.writeHeaders[key] + if actual != value { + t.Errorf( + "Expected THeaderTransport.writeHeaders to have %q:%q, got %+v", + key, + value, + trans.writeHeaders, + ) + } + helper.ClearHeaders() + if len(trans.writeHeaders) != 0 { + t.Errorf( + "Expected THeaderTransport.writeHeaders to be empty after ClearHeaders call, got %+v", + trans.writeHeaders, + ) + } + }, + ) + + t.Run( + "other-protocol", + func(t *testing.T) { + trans := NewTMemoryBuffer() + proto := NewTCompactProtocol(trans) + helper := NewTHeaderResponseHelper(proto) + + // We only need to make sure that functions in helper + // don't panic here. + helper.SetHeader("foo", "bar") + helper.ClearHeaders() + }, + ) + + t.Run( + "zero-value", + func(t *testing.T) { + var helper *THeaderResponseHelper + + // We only need to make sure that functions in helper + // don't panic here. + helper.SetHeader("foo", "bar") + helper.ClearHeaders() + }, + ) +} + +func TestTResponseHelperZeroValue(t *testing.T) { + var helper THeaderResponseHelper + + // We only need to make sure that functions in helper + // don't panic here. + helper.SetHeader("foo", "bar") + helper.ClearHeaders() +} diff --git a/lib/go/thrift/rich_transport.go b/lib/go/thrift/rich_transport.go index 4025bebeaa4..83fdf29f5cb 100644 --- a/lib/go/thrift/rich_transport.go +++ b/lib/go/thrift/rich_transport.go @@ -19,7 +19,10 @@ package thrift -import "io" +import ( + "errors" + "io" +) type RichTransport struct { TTransport @@ -49,7 +52,7 @@ func (r *RichTransport) RemainingBytes() (num_bytes uint64) { func readByte(r io.Reader) (c byte, err error) { v := [1]byte{0} n, err := r.Read(v[0:1]) - if n > 0 && (err == nil || err == io.EOF) { + if n > 0 && (err == nil || errors.Is(err, io.EOF)) { return v[0], nil } if n > 0 && err != nil { diff --git a/lib/go/thrift/serializer.go b/lib/go/thrift/serializer.go index 1ff4d37545a..c44979094c6 100644 --- a/lib/go/thrift/serializer.go +++ b/lib/go/thrift/serializer.go @@ -21,6 +21,7 @@ package thrift import ( "context" + "sync" ) type TSerializer struct { @@ -29,23 +30,24 @@ type TSerializer struct { } type TStruct interface { - Write(p TProtocol) error - Read(p TProtocol) error + Write(ctx context.Context, p TProtocol) error + Read(ctx context.Context, p TProtocol) error } func NewTSerializer() *TSerializer { transport := NewTMemoryBufferLen(1024) - protocol := NewTBinaryProtocolFactoryDefault().GetProtocol(transport) + protocol := NewTBinaryProtocolTransport(transport) return &TSerializer{ - transport, - protocol} + Transport: transport, + Protocol: protocol, + } } func (t *TSerializer) WriteString(ctx context.Context, msg TStruct) (s string, err error) { t.Transport.Reset() - if err = msg.Write(t.Protocol); err != nil { + if err = msg.Write(ctx, t.Protocol); err != nil { return } @@ -62,7 +64,7 @@ func (t *TSerializer) WriteString(ctx context.Context, msg TStruct) (s string, e func (t *TSerializer) Write(ctx context.Context, msg TStruct) (b []byte, err error) { t.Transport.Reset() - if err = msg.Write(t.Protocol); err != nil { + if err = msg.Write(ctx, t.Protocol); err != nil { return } @@ -77,3 +79,58 @@ func (t *TSerializer) Write(ctx context.Context, msg TStruct) (b []byte, err err b = append(b, t.Transport.Bytes()...) return } + +// TSerializerPool is the thread-safe version of TSerializer, it uses resource +// pool of TSerializer under the hood. +// +// It must be initialized with either NewTSerializerPool or +// NewTSerializerPoolSizeFactory. +type TSerializerPool struct { + pool sync.Pool +} + +// NewTSerializerPool creates a new TSerializerPool. +// +// NewTSerializer can be used as the arg here. +func NewTSerializerPool(f func() *TSerializer) *TSerializerPool { + return &TSerializerPool{ + pool: sync.Pool{ + New: func() interface{} { + return f() + }, + }, + } +} + +// NewTSerializerPoolSizeFactory creates a new TSerializerPool with the given +// size and protocol factory. +// +// Note that the size is not the limit. The TMemoryBuffer underneath can grow +// larger than that. It just dictates the initial size. +func NewTSerializerPoolSizeFactory(size int, factory TProtocolFactory) *TSerializerPool { + return &TSerializerPool{ + pool: sync.Pool{ + New: func() interface{} { + transport := NewTMemoryBufferLen(size) + protocol := factory.GetProtocol(transport) + + return &TSerializer{ + Transport: transport, + Protocol: protocol, + } + }, + }, + } +} + +func (t *TSerializerPool) WriteString(ctx context.Context, msg TStruct) (string, error) { + s := t.pool.Get().(*TSerializer) + defer t.pool.Put(s) + return s.WriteString(ctx, msg) +} + +func (t *TSerializerPool) Write(ctx context.Context, msg TStruct) ([]byte, error) { + s := t.pool.Get().(*TSerializer) + defer t.pool.Put(s) + return s.Write(ctx, msg) +} diff --git a/lib/go/thrift/serializer_test.go b/lib/go/thrift/serializer_test.go index 32227ef4922..2e37ea2b195 100644 --- a/lib/go/thrift/serializer_test.go +++ b/lib/go/thrift/serializer_test.go @@ -23,122 +23,193 @@ import ( "context" "errors" "fmt" + "sync" + "sync/atomic" "testing" + "testing/quick" ) type ProtocolFactory interface { GetProtocol(t TTransport) TProtocol } -func compareStructs(m, m1 MyTestStruct) (bool, error) { +func compareStructs(m, m1 MyTestStruct) error { switch { case m.On != m1.On: - return false, errors.New("Boolean not equal") + return errors.New("Boolean not equal") case m.B != m1.B: - return false, errors.New("Byte not equal") + return errors.New("Byte not equal") case m.Int16 != m1.Int16: - return false, errors.New("Int16 not equal") + return errors.New("Int16 not equal") case m.Int32 != m1.Int32: - return false, errors.New("Int32 not equal") + return errors.New("Int32 not equal") case m.Int64 != m1.Int64: - return false, errors.New("Int64 not equal") + return errors.New("Int64 not equal") case m.D != m1.D: - return false, errors.New("Double not equal") + return errors.New("Double not equal") case m.St != m1.St: - return false, errors.New("String not equal") + return errors.New("String not equal") case len(m.Bin) != len(m1.Bin): - return false, errors.New("Binary size not equal") + return errors.New("Binary size not equal") case len(m.Bin) == len(m1.Bin): for i := range m.Bin { if m.Bin[i] != m1.Bin[i] { - return false, errors.New("Binary not equal") + return errors.New("Binary not equal") } } case len(m.StringMap) != len(m1.StringMap): - return false, errors.New("StringMap size not equal") + return errors.New("StringMap size not equal") case len(m.StringList) != len(m1.StringList): - return false, errors.New("StringList size not equal") + return errors.New("StringList size not equal") case len(m.StringSet) != len(m1.StringSet): - return false, errors.New("StringSet size not equal") + return errors.New("StringSet size not equal") case m.E != m1.E: - return false, errors.New("MyTestEnum not equal") + return errors.New("MyTestEnum not equal") default: - return true, nil + return nil } - return true, nil + return nil } -func ProtocolTest1(test *testing.T, pf ProtocolFactory) (bool, error) { +type serializer interface { + WriteString(context.Context, TStruct) (string, error) +} + +type deserializer interface { + ReadString(context.Context, TStruct, string) error +} + +func plainSerializer(pf ProtocolFactory) serializer { t := NewTSerializer() t.Protocol = pf.GetProtocol(t.Transport) - var m = MyTestStruct{} - m.On = true - m.B = int8(0) - m.Int16 = 1 - m.Int32 = 2 - m.Int64 = 3 - m.D = 4.1 - m.St = "Test" - m.Bin = make([]byte, 10) - m.StringMap = make(map[string]string, 5) - m.StringList = make([]string, 5) - m.StringSet = make(map[string]struct{}, 5) - m.E = 2 - - s, err := t.WriteString(context.Background(), &m) - if err != nil { - return false, errors.New(fmt.Sprintf("Unable to Serialize struct\n\t %s", err)) - } + return t +} - t1 := NewTDeserializer() - t1.Protocol = pf.GetProtocol(t1.Transport) - var m1 = MyTestStruct{} - if err = t1.ReadString(&m1, s); err != nil { - return false, errors.New(fmt.Sprintf("Unable to Deserialize struct\n\t %s", err)) +func poolSerializer(pf ProtocolFactory) serializer { + return NewTSerializerPool( + func() *TSerializer { + return plainSerializer(pf).(*TSerializer) + }, + ) +} - } +func plainDeserializer(pf ProtocolFactory) deserializer { + d := NewTDeserializer() + d.Protocol = pf.GetProtocol(d.Transport) + return d +} - return compareStructs(m, m1) +func poolDeserializer(pf ProtocolFactory) deserializer { + return NewTDeserializerPool( + func() *TDeserializer { + return plainDeserializer(pf).(*TDeserializer) + }, + ) +} +type constructors struct { + Label string + Serializer func(pf ProtocolFactory) serializer + Deserializer func(pf ProtocolFactory) deserializer } -func ProtocolTest2(test *testing.T, pf ProtocolFactory) (bool, error) { - t := NewTSerializer() - t.Protocol = pf.GetProtocol(t.Transport) - var m = MyTestStruct{} - m.On = false - m.B = int8(0) - m.Int16 = 1 - m.Int32 = 2 - m.Int64 = 3 - m.D = 4.1 - m.St = "Test" - m.Bin = make([]byte, 10) - m.StringMap = make(map[string]string, 5) - m.StringList = make([]string, 5) - m.StringSet = make(map[string]struct{}, 5) - m.E = 2 - - s, err := t.WriteString(context.Background(), &m) - if err != nil { - return false, errors.New(fmt.Sprintf("Unable to Serialize struct\n\t %s", err)) +var implementations = []constructors{ + { + Label: "plain", + Serializer: plainSerializer, + Deserializer: plainDeserializer, + }, + { + Label: "pool", + Serializer: poolSerializer, + Deserializer: poolDeserializer, + }, +} - } +func ProtocolTest1(t *testing.T, pf ProtocolFactory) { + for _, impl := range implementations { + t.Run( + impl.Label, + func(test *testing.T) { + t := impl.Serializer(pf) + var m = MyTestStruct{} + m.On = true + m.B = int8(0) + m.Int16 = 1 + m.Int32 = 2 + m.Int64 = 3 + m.D = 4.1 + m.St = "Test" + m.Bin = make([]byte, 10) + m.StringMap = make(map[string]string, 5) + m.StringList = make([]string, 5) + m.StringSet = make(map[string]struct{}, 5) + m.E = 2 + + s, err := t.WriteString(context.Background(), &m) + if err != nil { + test.Fatalf("Unable to Serialize struct: %v", err) + + } + + t1 := impl.Deserializer(pf) + var m1 MyTestStruct + if err = t1.ReadString(context.Background(), &m1, s); err != nil { + test.Fatalf("Unable to Deserialize struct: %v", err) - t1 := NewTDeserializer() - t1.Protocol = pf.GetProtocol(t1.Transport) - var m1 = MyTestStruct{} - if err = t1.ReadString(&m1, s); err != nil { - return false, errors.New(fmt.Sprintf("Unable to Deserialize struct\n\t %s", err)) + } + if err := compareStructs(m, m1); err != nil { + test.Error(err) + } + }, + ) } +} + +func ProtocolTest2(t *testing.T, pf ProtocolFactory) { + for _, impl := range implementations { + t.Run( + impl.Label, + func(test *testing.T) { + t := impl.Serializer(pf) + var m = MyTestStruct{} + m.On = false + m.B = int8(0) + m.Int16 = 1 + m.Int32 = 2 + m.Int64 = 3 + m.D = 4.1 + m.St = "Test" + m.Bin = make([]byte, 10) + m.StringMap = make(map[string]string, 5) + m.StringList = make([]string, 5) + m.StringSet = make(map[string]struct{}, 5) + m.E = 2 + + s, err := t.WriteString(context.Background(), &m) + if err != nil { + test.Fatalf("Unable to Serialize struct: %v", err) + + } - return compareStructs(m, m1) + t1 := impl.Deserializer(pf) + var m1 MyTestStruct + if err = t1.ReadString(context.Background(), &m1, s); err != nil { + test.Fatalf("Unable to Deserialize struct: %v", err) + } + + if err := compareStructs(m, m1); err != nil { + test.Error(err) + } + }, + ) + } } func TestSerializer(t *testing.T) { @@ -150,21 +221,123 @@ func TestSerializer(t *testing.T) { //protocol_factories["SimpleJSON"] = NewTSimpleJSONProtocolFactory() - write only, can't be read back by design protocol_factories["JSON"] = NewTJSONProtocolFactory() - var tests map[string]func(*testing.T, ProtocolFactory) (bool, error) - tests = make(map[string]func(*testing.T, ProtocolFactory) (bool, error)) + tests := make(map[string]func(*testing.T, ProtocolFactory)) tests["Test 1"] = ProtocolTest1 tests["Test 2"] = ProtocolTest2 //tests["Test 3"] = ProtocolTest3 // Example of how to add additional tests for name, pf := range protocol_factories { + t.Run( + name, + func(t *testing.T) { + for label, f := range tests { + t.Run( + label, + func(t *testing.T) { + f(t, pf) + }, + ) + } + }, + ) + } + +} - for test, f := range tests { +func TestSerializerPoolAsync(t *testing.T) { + var wg sync.WaitGroup + var counter int64 + s := NewTSerializerPool(NewTSerializer) + d := NewTDeserializerPool(NewTDeserializer) + f := func(i int64) bool { + wg.Add(1) + go func() { + defer wg.Done() + t.Run( + fmt.Sprintf("#%d-%d", atomic.AddInt64(&counter, 1), i), + func(t *testing.T) { + m := MyTestStruct{ + Int64: i, + } + str, err := s.WriteString(context.Background(), &m) + if err != nil { + t.Fatal("serialize:", err) + } + var m1 MyTestStruct + if err = d.ReadString(context.Background(), &m1, str); err != nil { + t.Fatal("deserialize:", err) - if s, err := f(t, pf); !s || err != nil { - t.Errorf("%s Failed for %s protocol\n\t %s", test, name, err) - } + } - } + if err := compareStructs(m, m1); err != nil { + t.Error(err) + } + }, + ) + }() + return true + } + quick.Check(f, nil) + wg.Wait() +} + +func BenchmarkSerializer(b *testing.B) { + sharedSerializer := NewTSerializer() + poolSerializer := NewTSerializerPool(NewTSerializer) + sharedDeserializer := NewTDeserializer() + poolDeserializer := NewTDeserializerPool(NewTDeserializer) + + cases := []struct { + Label string + Serializer func() serializer + Deserializer func() deserializer + }{ + { + // Baseline uses shared plain serializer/deserializer + Label: "baseline", + Serializer: func() serializer { + return sharedSerializer + }, + Deserializer: func() deserializer { + return sharedDeserializer + }, + }, + { + // Plain creates new serializer/deserializer on every run, + // as that's how it's used in real world + Label: "plain", + Serializer: func() serializer { + return NewTSerializer() + }, + Deserializer: func() deserializer { + return NewTDeserializer() + }, + }, + { + // Pool uses the shared pool serializer/deserializer + Label: "pool", + Serializer: func() serializer { + return poolSerializer + }, + Deserializer: func() deserializer { + return poolDeserializer + }, + }, } + for _, c := range cases { + b.Run( + c.Label, + func(b *testing.B) { + for i := 0; i < b.N; i++ { + s := c.Serializer() + m := MyTestStruct{} + str, _ := s.WriteString(context.Background(), &m) + var m1 MyTestStruct + d := c.Deserializer() + d.ReadString(context.Background(), &m1, str) + } + }, + ) + } } diff --git a/lib/go/thrift/serializer_types_test.go b/lib/go/thrift/serializer_types_test.go index ef7cc3a16ad..4d1e992ae42 100644 --- a/lib/go/thrift/serializer_types_test.go +++ b/lib/go/thrift/serializer_types_test.go @@ -19,7 +19,7 @@ package thrift -// Autogenerated by Thrift Compiler (0.12.0) +// Autogenerated by Thrift Compiler (FIXME) // DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING /* THE FOLLOWING THRIFT FILE WAS USED TO CREATE THIS @@ -48,6 +48,7 @@ struct MyTestStruct { */ import ( + "context" "fmt" ) @@ -162,12 +163,12 @@ func (p *MyTestStruct) GetStringSet() map[string]struct{} { func (p *MyTestStruct) GetE() MyTestEnum { return p.E } -func (p *MyTestStruct) Read(iprot TProtocol) error { - if _, err := iprot.ReadStructBegin(); err != nil { +func (p *MyTestStruct) Read(ctx context.Context, iprot TProtocol) error { + if _, err := iprot.ReadStructBegin(ctx); err != nil { return PrependError(fmt.Sprintf("%T read error: ", p), err) } for { - _, fieldTypeId, fieldId, err := iprot.ReadFieldBegin() + _, fieldTypeId, fieldId, err := iprot.ReadFieldBegin(ctx) if err != nil { return PrependError(fmt.Sprintf("%T field %d read error: ", p, fieldId), err) } @@ -176,70 +177,70 @@ func (p *MyTestStruct) Read(iprot TProtocol) error { } switch fieldId { case 1: - if err := p.readField1(iprot); err != nil { + if err := p.readField1(ctx, iprot); err != nil { return err } case 2: - if err := p.readField2(iprot); err != nil { + if err := p.readField2(ctx, iprot); err != nil { return err } case 3: - if err := p.readField3(iprot); err != nil { + if err := p.readField3(ctx, iprot); err != nil { return err } case 4: - if err := p.readField4(iprot); err != nil { + if err := p.readField4(ctx, iprot); err != nil { return err } case 5: - if err := p.readField5(iprot); err != nil { + if err := p.readField5(ctx, iprot); err != nil { return err } case 6: - if err := p.readField6(iprot); err != nil { + if err := p.readField6(ctx, iprot); err != nil { return err } case 7: - if err := p.readField7(iprot); err != nil { + if err := p.readField7(ctx, iprot); err != nil { return err } case 8: - if err := p.readField8(iprot); err != nil { + if err := p.readField8(ctx, iprot); err != nil { return err } case 9: - if err := p.readField9(iprot); err != nil { + if err := p.readField9(ctx, iprot); err != nil { return err } case 10: - if err := p.readField10(iprot); err != nil { + if err := p.readField10(ctx, iprot); err != nil { return err } case 11: - if err := p.readField11(iprot); err != nil { + if err := p.readField11(ctx, iprot); err != nil { return err } case 12: - if err := p.readField12(iprot); err != nil { + if err := p.readField12(ctx, iprot); err != nil { return err } default: - if err := iprot.Skip(fieldTypeId); err != nil { + if err := iprot.Skip(ctx, fieldTypeId); err != nil { return err } } - if err := iprot.ReadFieldEnd(); err != nil { + if err := iprot.ReadFieldEnd(ctx); err != nil { return err } } - if err := iprot.ReadStructEnd(); err != nil { + if err := iprot.ReadStructEnd(ctx); err != nil { return PrependError(fmt.Sprintf("%T read struct end error: ", p), err) } return nil } -func (p *MyTestStruct) readField1(iprot TProtocol) error { - if v, err := iprot.ReadBool(); err != nil { +func (p *MyTestStruct) readField1(ctx context.Context, iprot TProtocol) error { + if v, err := iprot.ReadBool(ctx); err != nil { return PrependError("error reading field 1: ", err) } else { p.On = v @@ -247,8 +248,8 @@ func (p *MyTestStruct) readField1(iprot TProtocol) error { return nil } -func (p *MyTestStruct) readField2(iprot TProtocol) error { - if v, err := iprot.ReadByte(); err != nil { +func (p *MyTestStruct) readField2(ctx context.Context, iprot TProtocol) error { + if v, err := iprot.ReadByte(ctx); err != nil { return PrependError("error reading field 2: ", err) } else { temp := int8(v) @@ -257,8 +258,8 @@ func (p *MyTestStruct) readField2(iprot TProtocol) error { return nil } -func (p *MyTestStruct) readField3(iprot TProtocol) error { - if v, err := iprot.ReadI16(); err != nil { +func (p *MyTestStruct) readField3(ctx context.Context, iprot TProtocol) error { + if v, err := iprot.ReadI16(ctx); err != nil { return PrependError("error reading field 3: ", err) } else { p.Int16 = v @@ -266,8 +267,8 @@ func (p *MyTestStruct) readField3(iprot TProtocol) error { return nil } -func (p *MyTestStruct) readField4(iprot TProtocol) error { - if v, err := iprot.ReadI32(); err != nil { +func (p *MyTestStruct) readField4(ctx context.Context, iprot TProtocol) error { + if v, err := iprot.ReadI32(ctx); err != nil { return PrependError("error reading field 4: ", err) } else { p.Int32 = v @@ -275,8 +276,8 @@ func (p *MyTestStruct) readField4(iprot TProtocol) error { return nil } -func (p *MyTestStruct) readField5(iprot TProtocol) error { - if v, err := iprot.ReadI64(); err != nil { +func (p *MyTestStruct) readField5(ctx context.Context, iprot TProtocol) error { + if v, err := iprot.ReadI64(ctx); err != nil { return PrependError("error reading field 5: ", err) } else { p.Int64 = v @@ -284,8 +285,8 @@ func (p *MyTestStruct) readField5(iprot TProtocol) error { return nil } -func (p *MyTestStruct) readField6(iprot TProtocol) error { - if v, err := iprot.ReadDouble(); err != nil { +func (p *MyTestStruct) readField6(ctx context.Context, iprot TProtocol) error { + if v, err := iprot.ReadDouble(ctx); err != nil { return PrependError("error reading field 6: ", err) } else { p.D = v @@ -293,8 +294,8 @@ func (p *MyTestStruct) readField6(iprot TProtocol) error { return nil } -func (p *MyTestStruct) readField7(iprot TProtocol) error { - if v, err := iprot.ReadString(); err != nil { +func (p *MyTestStruct) readField7(ctx context.Context, iprot TProtocol) error { + if v, err := iprot.ReadString(ctx); err != nil { return PrependError("error reading field 7: ", err) } else { p.St = v @@ -302,8 +303,8 @@ func (p *MyTestStruct) readField7(iprot TProtocol) error { return nil } -func (p *MyTestStruct) readField8(iprot TProtocol) error { - if v, err := iprot.ReadBinary(); err != nil { +func (p *MyTestStruct) readField8(ctx context.Context, iprot TProtocol) error { + if v, err := iprot.ReadBinary(ctx); err != nil { return PrependError("error reading field 8: ", err) } else { p.Bin = v @@ -311,8 +312,8 @@ func (p *MyTestStruct) readField8(iprot TProtocol) error { return nil } -func (p *MyTestStruct) readField9(iprot TProtocol) error { - _, _, size, err := iprot.ReadMapBegin() +func (p *MyTestStruct) readField9(ctx context.Context, iprot TProtocol) error { + _, _, size, err := iprot.ReadMapBegin(ctx) if err != nil { return PrependError("error reading map begin: ", err) } @@ -320,27 +321,27 @@ func (p *MyTestStruct) readField9(iprot TProtocol) error { p.StringMap = tMap for i := 0; i < size; i++ { var _key0 string - if v, err := iprot.ReadString(); err != nil { + if v, err := iprot.ReadString(ctx); err != nil { return PrependError("error reading field 0: ", err) } else { _key0 = v } var _val1 string - if v, err := iprot.ReadString(); err != nil { + if v, err := iprot.ReadString(ctx); err != nil { return PrependError("error reading field 0: ", err) } else { _val1 = v } p.StringMap[_key0] = _val1 } - if err := iprot.ReadMapEnd(); err != nil { + if err := iprot.ReadMapEnd(ctx); err != nil { return PrependError("error reading map end: ", err) } return nil } -func (p *MyTestStruct) readField10(iprot TProtocol) error { - _, size, err := iprot.ReadListBegin() +func (p *MyTestStruct) readField10(ctx context.Context, iprot TProtocol) error { + _, size, err := iprot.ReadListBegin(ctx) if err != nil { return PrependError("error reading list begin: ", err) } @@ -348,21 +349,21 @@ func (p *MyTestStruct) readField10(iprot TProtocol) error { p.StringList = tSlice for i := 0; i < size; i++ { var _elem2 string - if v, err := iprot.ReadString(); err != nil { + if v, err := iprot.ReadString(ctx); err != nil { return PrependError("error reading field 0: ", err) } else { _elem2 = v } p.StringList = append(p.StringList, _elem2) } - if err := iprot.ReadListEnd(); err != nil { + if err := iprot.ReadListEnd(ctx); err != nil { return PrependError("error reading list end: ", err) } return nil } -func (p *MyTestStruct) readField11(iprot TProtocol) error { - _, size, err := iprot.ReadSetBegin() +func (p *MyTestStruct) readField11(ctx context.Context, iprot TProtocol) error { + _, size, err := iprot.ReadSetBegin(ctx) if err != nil { return PrependError("error reading set begin: ", err) } @@ -370,21 +371,21 @@ func (p *MyTestStruct) readField11(iprot TProtocol) error { p.StringSet = tSet for i := 0; i < size; i++ { var _elem3 string - if v, err := iprot.ReadString(); err != nil { + if v, err := iprot.ReadString(ctx); err != nil { return PrependError("error reading field 0: ", err) } else { _elem3 = v } p.StringSet[_elem3] = struct{}{} } - if err := iprot.ReadSetEnd(); err != nil { + if err := iprot.ReadSetEnd(ctx); err != nil { return PrependError("error reading set end: ", err) } return nil } -func (p *MyTestStruct) readField12(iprot TProtocol) error { - if v, err := iprot.ReadI32(); err != nil { +func (p *MyTestStruct) readField12(ctx context.Context, iprot TProtocol) error { + if v, err := iprot.ReadI32(ctx); err != nil { return PrependError("error reading field 12: ", err) } else { temp := MyTestEnum(v) @@ -393,233 +394,233 @@ func (p *MyTestStruct) readField12(iprot TProtocol) error { return nil } -func (p *MyTestStruct) Write(oprot TProtocol) error { - if err := oprot.WriteStructBegin("MyTestStruct"); err != nil { +func (p *MyTestStruct) Write(ctx context.Context, oprot TProtocol) error { + if err := oprot.WriteStructBegin(ctx, "MyTestStruct"); err != nil { return PrependError(fmt.Sprintf("%T write struct begin error: ", p), err) } - if err := p.writeField1(oprot); err != nil { + if err := p.writeField1(ctx, oprot); err != nil { return err } - if err := p.writeField2(oprot); err != nil { + if err := p.writeField2(ctx, oprot); err != nil { return err } - if err := p.writeField3(oprot); err != nil { + if err := p.writeField3(ctx, oprot); err != nil { return err } - if err := p.writeField4(oprot); err != nil { + if err := p.writeField4(ctx, oprot); err != nil { return err } - if err := p.writeField5(oprot); err != nil { + if err := p.writeField5(ctx, oprot); err != nil { return err } - if err := p.writeField6(oprot); err != nil { + if err := p.writeField6(ctx, oprot); err != nil { return err } - if err := p.writeField7(oprot); err != nil { + if err := p.writeField7(ctx, oprot); err != nil { return err } - if err := p.writeField8(oprot); err != nil { + if err := p.writeField8(ctx, oprot); err != nil { return err } - if err := p.writeField9(oprot); err != nil { + if err := p.writeField9(ctx, oprot); err != nil { return err } - if err := p.writeField10(oprot); err != nil { + if err := p.writeField10(ctx, oprot); err != nil { return err } - if err := p.writeField11(oprot); err != nil { + if err := p.writeField11(ctx, oprot); err != nil { return err } - if err := p.writeField12(oprot); err != nil { + if err := p.writeField12(ctx, oprot); err != nil { return err } - if err := oprot.WriteFieldStop(); err != nil { + if err := oprot.WriteFieldStop(ctx); err != nil { return PrependError("write field stop error: ", err) } - if err := oprot.WriteStructEnd(); err != nil { + if err := oprot.WriteStructEnd(ctx); err != nil { return PrependError("write struct stop error: ", err) } return nil } -func (p *MyTestStruct) writeField1(oprot TProtocol) (err error) { - if err := oprot.WriteFieldBegin("on", BOOL, 1); err != nil { +func (p *MyTestStruct) writeField1(ctx context.Context, oprot TProtocol) (err error) { + if err := oprot.WriteFieldBegin(ctx, "on", BOOL, 1); err != nil { return PrependError(fmt.Sprintf("%T write field begin error 1:on: ", p), err) } - if err := oprot.WriteBool(bool(p.On)); err != nil { + if err := oprot.WriteBool(ctx, bool(p.On)); err != nil { return PrependError(fmt.Sprintf("%T.on (1) field write error: ", p), err) } - if err := oprot.WriteFieldEnd(); err != nil { + if err := oprot.WriteFieldEnd(ctx); err != nil { return PrependError(fmt.Sprintf("%T write field end error 1:on: ", p), err) } return err } -func (p *MyTestStruct) writeField2(oprot TProtocol) (err error) { - if err := oprot.WriteFieldBegin("b", BYTE, 2); err != nil { +func (p *MyTestStruct) writeField2(ctx context.Context, oprot TProtocol) (err error) { + if err := oprot.WriteFieldBegin(ctx, "b", BYTE, 2); err != nil { return PrependError(fmt.Sprintf("%T write field begin error 2:b: ", p), err) } - if err := oprot.WriteByte(int8(p.B)); err != nil { + if err := oprot.WriteByte(ctx, int8(p.B)); err != nil { return PrependError(fmt.Sprintf("%T.b (2) field write error: ", p), err) } - if err := oprot.WriteFieldEnd(); err != nil { + if err := oprot.WriteFieldEnd(ctx); err != nil { return PrependError(fmt.Sprintf("%T write field end error 2:b: ", p), err) } return err } -func (p *MyTestStruct) writeField3(oprot TProtocol) (err error) { - if err := oprot.WriteFieldBegin("int16", I16, 3); err != nil { +func (p *MyTestStruct) writeField3(ctx context.Context, oprot TProtocol) (err error) { + if err := oprot.WriteFieldBegin(ctx, "int16", I16, 3); err != nil { return PrependError(fmt.Sprintf("%T write field begin error 3:int16: ", p), err) } - if err := oprot.WriteI16(int16(p.Int16)); err != nil { + if err := oprot.WriteI16(ctx, int16(p.Int16)); err != nil { return PrependError(fmt.Sprintf("%T.int16 (3) field write error: ", p), err) } - if err := oprot.WriteFieldEnd(); err != nil { + if err := oprot.WriteFieldEnd(ctx); err != nil { return PrependError(fmt.Sprintf("%T write field end error 3:int16: ", p), err) } return err } -func (p *MyTestStruct) writeField4(oprot TProtocol) (err error) { - if err := oprot.WriteFieldBegin("int32", I32, 4); err != nil { +func (p *MyTestStruct) writeField4(ctx context.Context, oprot TProtocol) (err error) { + if err := oprot.WriteFieldBegin(ctx, "int32", I32, 4); err != nil { return PrependError(fmt.Sprintf("%T write field begin error 4:int32: ", p), err) } - if err := oprot.WriteI32(int32(p.Int32)); err != nil { + if err := oprot.WriteI32(ctx, int32(p.Int32)); err != nil { return PrependError(fmt.Sprintf("%T.int32 (4) field write error: ", p), err) } - if err := oprot.WriteFieldEnd(); err != nil { + if err := oprot.WriteFieldEnd(ctx); err != nil { return PrependError(fmt.Sprintf("%T write field end error 4:int32: ", p), err) } return err } -func (p *MyTestStruct) writeField5(oprot TProtocol) (err error) { - if err := oprot.WriteFieldBegin("int64", I64, 5); err != nil { +func (p *MyTestStruct) writeField5(ctx context.Context, oprot TProtocol) (err error) { + if err := oprot.WriteFieldBegin(ctx, "int64", I64, 5); err != nil { return PrependError(fmt.Sprintf("%T write field begin error 5:int64: ", p), err) } - if err := oprot.WriteI64(int64(p.Int64)); err != nil { + if err := oprot.WriteI64(ctx, int64(p.Int64)); err != nil { return PrependError(fmt.Sprintf("%T.int64 (5) field write error: ", p), err) } - if err := oprot.WriteFieldEnd(); err != nil { + if err := oprot.WriteFieldEnd(ctx); err != nil { return PrependError(fmt.Sprintf("%T write field end error 5:int64: ", p), err) } return err } -func (p *MyTestStruct) writeField6(oprot TProtocol) (err error) { - if err := oprot.WriteFieldBegin("d", DOUBLE, 6); err != nil { +func (p *MyTestStruct) writeField6(ctx context.Context, oprot TProtocol) (err error) { + if err := oprot.WriteFieldBegin(ctx, "d", DOUBLE, 6); err != nil { return PrependError(fmt.Sprintf("%T write field begin error 6:d: ", p), err) } - if err := oprot.WriteDouble(float64(p.D)); err != nil { + if err := oprot.WriteDouble(ctx, float64(p.D)); err != nil { return PrependError(fmt.Sprintf("%T.d (6) field write error: ", p), err) } - if err := oprot.WriteFieldEnd(); err != nil { + if err := oprot.WriteFieldEnd(ctx); err != nil { return PrependError(fmt.Sprintf("%T write field end error 6:d: ", p), err) } return err } -func (p *MyTestStruct) writeField7(oprot TProtocol) (err error) { - if err := oprot.WriteFieldBegin("st", STRING, 7); err != nil { +func (p *MyTestStruct) writeField7(ctx context.Context, oprot TProtocol) (err error) { + if err := oprot.WriteFieldBegin(ctx, "st", STRING, 7); err != nil { return PrependError(fmt.Sprintf("%T write field begin error 7:st: ", p), err) } - if err := oprot.WriteString(string(p.St)); err != nil { + if err := oprot.WriteString(ctx, string(p.St)); err != nil { return PrependError(fmt.Sprintf("%T.st (7) field write error: ", p), err) } - if err := oprot.WriteFieldEnd(); err != nil { + if err := oprot.WriteFieldEnd(ctx); err != nil { return PrependError(fmt.Sprintf("%T write field end error 7:st: ", p), err) } return err } -func (p *MyTestStruct) writeField8(oprot TProtocol) (err error) { - if err := oprot.WriteFieldBegin("bin", STRING, 8); err != nil { +func (p *MyTestStruct) writeField8(ctx context.Context, oprot TProtocol) (err error) { + if err := oprot.WriteFieldBegin(ctx, "bin", STRING, 8); err != nil { return PrependError(fmt.Sprintf("%T write field begin error 8:bin: ", p), err) } - if err := oprot.WriteBinary(p.Bin); err != nil { + if err := oprot.WriteBinary(ctx, p.Bin); err != nil { return PrependError(fmt.Sprintf("%T.bin (8) field write error: ", p), err) } - if err := oprot.WriteFieldEnd(); err != nil { + if err := oprot.WriteFieldEnd(ctx); err != nil { return PrependError(fmt.Sprintf("%T write field end error 8:bin: ", p), err) } return err } -func (p *MyTestStruct) writeField9(oprot TProtocol) (err error) { - if err := oprot.WriteFieldBegin("stringMap", MAP, 9); err != nil { +func (p *MyTestStruct) writeField9(ctx context.Context, oprot TProtocol) (err error) { + if err := oprot.WriteFieldBegin(ctx, "stringMap", MAP, 9); err != nil { return PrependError(fmt.Sprintf("%T write field begin error 9:stringMap: ", p), err) } - if err := oprot.WriteMapBegin(STRING, STRING, len(p.StringMap)); err != nil { + if err := oprot.WriteMapBegin(ctx, STRING, STRING, len(p.StringMap)); err != nil { return PrependError("error writing map begin: ", err) } for k, v := range p.StringMap { - if err := oprot.WriteString(string(k)); err != nil { + if err := oprot.WriteString(ctx, string(k)); err != nil { return PrependError(fmt.Sprintf("%T. (0) field write error: ", p), err) } - if err := oprot.WriteString(string(v)); err != nil { + if err := oprot.WriteString(ctx, string(v)); err != nil { return PrependError(fmt.Sprintf("%T. (0) field write error: ", p), err) } } - if err := oprot.WriteMapEnd(); err != nil { + if err := oprot.WriteMapEnd(ctx); err != nil { return PrependError("error writing map end: ", err) } - if err := oprot.WriteFieldEnd(); err != nil { + if err := oprot.WriteFieldEnd(ctx); err != nil { return PrependError(fmt.Sprintf("%T write field end error 9:stringMap: ", p), err) } return err } -func (p *MyTestStruct) writeField10(oprot TProtocol) (err error) { - if err := oprot.WriteFieldBegin("stringList", LIST, 10); err != nil { +func (p *MyTestStruct) writeField10(ctx context.Context, oprot TProtocol) (err error) { + if err := oprot.WriteFieldBegin(ctx, "stringList", LIST, 10); err != nil { return PrependError(fmt.Sprintf("%T write field begin error 10:stringList: ", p), err) } - if err := oprot.WriteListBegin(STRING, len(p.StringList)); err != nil { + if err := oprot.WriteListBegin(ctx, STRING, len(p.StringList)); err != nil { return PrependError("error writing list begin: ", err) } for _, v := range p.StringList { - if err := oprot.WriteString(string(v)); err != nil { + if err := oprot.WriteString(ctx, string(v)); err != nil { return PrependError(fmt.Sprintf("%T. (0) field write error: ", p), err) } } - if err := oprot.WriteListEnd(); err != nil { + if err := oprot.WriteListEnd(ctx); err != nil { return PrependError("error writing list end: ", err) } - if err := oprot.WriteFieldEnd(); err != nil { + if err := oprot.WriteFieldEnd(ctx); err != nil { return PrependError(fmt.Sprintf("%T write field end error 10:stringList: ", p), err) } return err } -func (p *MyTestStruct) writeField11(oprot TProtocol) (err error) { - if err := oprot.WriteFieldBegin("stringSet", SET, 11); err != nil { +func (p *MyTestStruct) writeField11(ctx context.Context, oprot TProtocol) (err error) { + if err := oprot.WriteFieldBegin(ctx, "stringSet", SET, 11); err != nil { return PrependError(fmt.Sprintf("%T write field begin error 11:stringSet: ", p), err) } - if err := oprot.WriteSetBegin(STRING, len(p.StringSet)); err != nil { + if err := oprot.WriteSetBegin(ctx, STRING, len(p.StringSet)); err != nil { return PrependError("error writing set begin: ", err) } for v := range p.StringSet { - if err := oprot.WriteString(string(v)); err != nil { + if err := oprot.WriteString(ctx, string(v)); err != nil { return PrependError(fmt.Sprintf("%T. (0) field write error: ", p), err) } } - if err := oprot.WriteSetEnd(); err != nil { + if err := oprot.WriteSetEnd(ctx); err != nil { return PrependError("error writing set end: ", err) } - if err := oprot.WriteFieldEnd(); err != nil { + if err := oprot.WriteFieldEnd(ctx); err != nil { return PrependError(fmt.Sprintf("%T write field end error 11:stringSet: ", p), err) } return err } -func (p *MyTestStruct) writeField12(oprot TProtocol) (err error) { - if err := oprot.WriteFieldBegin("e", I32, 12); err != nil { +func (p *MyTestStruct) writeField12(ctx context.Context, oprot TProtocol) (err error) { + if err := oprot.WriteFieldBegin(ctx, "e", I32, 12); err != nil { return PrependError(fmt.Sprintf("%T write field begin error 12:e: ", p), err) } - if err := oprot.WriteI32(int32(p.E)); err != nil { + if err := oprot.WriteI32(ctx, int32(p.E)); err != nil { return PrependError(fmt.Sprintf("%T.e (12) field write error: ", p), err) } - if err := oprot.WriteFieldEnd(); err != nil { + if err := oprot.WriteFieldEnd(ctx); err != nil { return PrependError(fmt.Sprintf("%T write field end error 12:e: ", p), err) } return err diff --git a/lib/go/thrift/simple_json_protocol.go b/lib/go/thrift/simple_json_protocol.go index 2e8a71112a4..d1a8154532d 100644 --- a/lib/go/thrift/simple_json_protocol.go +++ b/lib/go/thrift/simple_json_protocol.go @@ -25,6 +25,7 @@ import ( "context" "encoding/base64" "encoding/json" + "errors" "fmt" "io" "math" @@ -34,12 +35,13 @@ import ( type _ParseContext int const ( - _CONTEXT_IN_TOPLEVEL _ParseContext = 1 - _CONTEXT_IN_LIST_FIRST _ParseContext = 2 - _CONTEXT_IN_LIST _ParseContext = 3 - _CONTEXT_IN_OBJECT_FIRST _ParseContext = 4 - _CONTEXT_IN_OBJECT_NEXT_KEY _ParseContext = 5 - _CONTEXT_IN_OBJECT_NEXT_VALUE _ParseContext = 6 + _CONTEXT_INVALID _ParseContext = iota + _CONTEXT_IN_TOPLEVEL // 1 + _CONTEXT_IN_LIST_FIRST // 2 + _CONTEXT_IN_LIST // 3 + _CONTEXT_IN_OBJECT_FIRST // 4 + _CONTEXT_IN_OBJECT_NEXT_KEY // 5 + _CONTEXT_IN_OBJECT_NEXT_VALUE // 6 ) func (p _ParseContext) String() string { @@ -60,7 +62,33 @@ func (p _ParseContext) String() string { return "UNKNOWN-PARSE-CONTEXT" } -// JSON protocol implementation for thrift. +type jsonContextStack []_ParseContext + +func (s *jsonContextStack) push(v _ParseContext) { + *s = append(*s, v) +} + +func (s jsonContextStack) peek() (v _ParseContext, ok bool) { + l := len(s) + if l <= 0 { + return + } + return s[l-1], true +} + +func (s *jsonContextStack) pop() (v _ParseContext, ok bool) { + l := len(*s) + if l <= 0 { + return + } + v = (*s)[l-1] + *s = (*s)[0 : l-1] + return v, true +} + +var errEmptyJSONContextStack = NewTProtocolExceptionWithType(INVALID_DATA, errors.New("Unexpected empty json protocol context stack")) + +// Simple JSON protocol implementation for thrift. // // This protocol produces/consumes a simple output format // suitable for parsing by scripting languages. It should not be @@ -69,8 +97,8 @@ func (p _ParseContext) String() string { type TSimpleJSONProtocol struct { trans TTransport - parseContextStack []int - dumpContext []int + parseContextStack jsonContextStack + dumpContext jsonContextStack writer *bufio.Writer reader *bufio.Reader @@ -82,8 +110,8 @@ func NewTSimpleJSONProtocol(t TTransport) *TSimpleJSONProtocol { writer: bufio.NewWriter(t), reader: bufio.NewReader(t), } - v.parseContextStack = append(v.parseContextStack, int(_CONTEXT_IN_TOPLEVEL)) - v.dumpContext = append(v.dumpContext, int(_CONTEXT_IN_TOPLEVEL)) + v.parseContextStack.push(_CONTEXT_IN_TOPLEVEL) + v.dumpContext.push(_CONTEXT_IN_TOPLEVEL) return v } @@ -156,114 +184,113 @@ func mismatch(expected, actual string) error { return fmt.Errorf("Expected '%s' but found '%s' while parsing JSON.", expected, actual) } -func (p *TSimpleJSONProtocol) WriteMessageBegin(name string, typeId TMessageType, seqId int32) error { +func (p *TSimpleJSONProtocol) WriteMessageBegin(ctx context.Context, name string, typeId TMessageType, seqId int32) error { p.resetContextStack() // THRIFT-3735 if e := p.OutputListBegin(); e != nil { return e } - if e := p.WriteString(name); e != nil { + if e := p.WriteString(ctx, name); e != nil { return e } - if e := p.WriteByte(int8(typeId)); e != nil { + if e := p.WriteByte(ctx, int8(typeId)); e != nil { return e } - if e := p.WriteI32(seqId); e != nil { + if e := p.WriteI32(ctx, seqId); e != nil { return e } return nil } -func (p *TSimpleJSONProtocol) WriteMessageEnd() error { +func (p *TSimpleJSONProtocol) WriteMessageEnd(ctx context.Context) error { return p.OutputListEnd() } -func (p *TSimpleJSONProtocol) WriteStructBegin(name string) error { +func (p *TSimpleJSONProtocol) WriteStructBegin(ctx context.Context, name string) error { if e := p.OutputObjectBegin(); e != nil { return e } return nil } -func (p *TSimpleJSONProtocol) WriteStructEnd() error { +func (p *TSimpleJSONProtocol) WriteStructEnd(ctx context.Context) error { return p.OutputObjectEnd() } -func (p *TSimpleJSONProtocol) WriteFieldBegin(name string, typeId TType, id int16) error { - if e := p.WriteString(name); e != nil { +func (p *TSimpleJSONProtocol) WriteFieldBegin(ctx context.Context, name string, typeId TType, id int16) error { + if e := p.WriteString(ctx, name); e != nil { return e } return nil } -func (p *TSimpleJSONProtocol) WriteFieldEnd() error { - //return p.OutputListEnd() +func (p *TSimpleJSONProtocol) WriteFieldEnd(ctx context.Context) error { return nil } -func (p *TSimpleJSONProtocol) WriteFieldStop() error { return nil } +func (p *TSimpleJSONProtocol) WriteFieldStop(ctx context.Context) error { return nil } -func (p *TSimpleJSONProtocol) WriteMapBegin(keyType TType, valueType TType, size int) error { +func (p *TSimpleJSONProtocol) WriteMapBegin(ctx context.Context, keyType TType, valueType TType, size int) error { if e := p.OutputListBegin(); e != nil { return e } - if e := p.WriteByte(int8(keyType)); e != nil { + if e := p.WriteByte(ctx, int8(keyType)); e != nil { return e } - if e := p.WriteByte(int8(valueType)); e != nil { + if e := p.WriteByte(ctx, int8(valueType)); e != nil { return e } - return p.WriteI32(int32(size)) + return p.WriteI32(ctx, int32(size)) } -func (p *TSimpleJSONProtocol) WriteMapEnd() error { +func (p *TSimpleJSONProtocol) WriteMapEnd(ctx context.Context) error { return p.OutputListEnd() } -func (p *TSimpleJSONProtocol) WriteListBegin(elemType TType, size int) error { +func (p *TSimpleJSONProtocol) WriteListBegin(ctx context.Context, elemType TType, size int) error { return p.OutputElemListBegin(elemType, size) } -func (p *TSimpleJSONProtocol) WriteListEnd() error { +func (p *TSimpleJSONProtocol) WriteListEnd(ctx context.Context) error { return p.OutputListEnd() } -func (p *TSimpleJSONProtocol) WriteSetBegin(elemType TType, size int) error { +func (p *TSimpleJSONProtocol) WriteSetBegin(ctx context.Context, elemType TType, size int) error { return p.OutputElemListBegin(elemType, size) } -func (p *TSimpleJSONProtocol) WriteSetEnd() error { +func (p *TSimpleJSONProtocol) WriteSetEnd(ctx context.Context) error { return p.OutputListEnd() } -func (p *TSimpleJSONProtocol) WriteBool(b bool) error { +func (p *TSimpleJSONProtocol) WriteBool(ctx context.Context, b bool) error { return p.OutputBool(b) } -func (p *TSimpleJSONProtocol) WriteByte(b int8) error { - return p.WriteI32(int32(b)) +func (p *TSimpleJSONProtocol) WriteByte(ctx context.Context, b int8) error { + return p.WriteI32(ctx, int32(b)) } -func (p *TSimpleJSONProtocol) WriteI16(v int16) error { - return p.WriteI32(int32(v)) +func (p *TSimpleJSONProtocol) WriteI16(ctx context.Context, v int16) error { + return p.WriteI32(ctx, int32(v)) } -func (p *TSimpleJSONProtocol) WriteI32(v int32) error { +func (p *TSimpleJSONProtocol) WriteI32(ctx context.Context, v int32) error { return p.OutputI64(int64(v)) } -func (p *TSimpleJSONProtocol) WriteI64(v int64) error { +func (p *TSimpleJSONProtocol) WriteI64(ctx context.Context, v int64) error { return p.OutputI64(int64(v)) } -func (p *TSimpleJSONProtocol) WriteDouble(v float64) error { +func (p *TSimpleJSONProtocol) WriteDouble(ctx context.Context, v float64) error { return p.OutputF64(v) } -func (p *TSimpleJSONProtocol) WriteString(v string) error { +func (p *TSimpleJSONProtocol) WriteString(ctx context.Context, v string) error { return p.OutputString(v) } -func (p *TSimpleJSONProtocol) WriteBinary(v []byte) error { +func (p *TSimpleJSONProtocol) WriteBinary(ctx context.Context, v []byte) error { // JSON library only takes in a string, // not an arbitrary byte array, to ensure bytes are transmitted // efficiently we must convert this into a valid JSON string @@ -289,39 +316,39 @@ func (p *TSimpleJSONProtocol) WriteBinary(v []byte) error { } // Reading methods. -func (p *TSimpleJSONProtocol) ReadMessageBegin() (name string, typeId TMessageType, seqId int32, err error) { +func (p *TSimpleJSONProtocol) ReadMessageBegin(ctx context.Context) (name string, typeId TMessageType, seqId int32, err error) { p.resetContextStack() // THRIFT-3735 if isNull, err := p.ParseListBegin(); isNull || err != nil { return name, typeId, seqId, err } - if name, err = p.ReadString(); err != nil { + if name, err = p.ReadString(ctx); err != nil { return name, typeId, seqId, err } - bTypeId, err := p.ReadByte() + bTypeId, err := p.ReadByte(ctx) typeId = TMessageType(bTypeId) if err != nil { return name, typeId, seqId, err } - if seqId, err = p.ReadI32(); err != nil { + if seqId, err = p.ReadI32(ctx); err != nil { return name, typeId, seqId, err } return name, typeId, seqId, nil } -func (p *TSimpleJSONProtocol) ReadMessageEnd() error { +func (p *TSimpleJSONProtocol) ReadMessageEnd(ctx context.Context) error { return p.ParseListEnd() } -func (p *TSimpleJSONProtocol) ReadStructBegin() (name string, err error) { +func (p *TSimpleJSONProtocol) ReadStructBegin(ctx context.Context) (name string, err error) { _, err = p.ParseObjectStart() return "", err } -func (p *TSimpleJSONProtocol) ReadStructEnd() error { +func (p *TSimpleJSONProtocol) ReadStructEnd(ctx context.Context) error { return p.ParseObjectEnd() } -func (p *TSimpleJSONProtocol) ReadFieldBegin() (string, TType, int16, error) { +func (p *TSimpleJSONProtocol) ReadFieldBegin(ctx context.Context) (string, TType, int16, error) { if err := p.ParsePreValue(); err != nil { return "", STOP, 0, err } @@ -340,21 +367,6 @@ func (p *TSimpleJSONProtocol) ReadFieldBegin() (string, TType, int16, error) { return name, STOP, 0, err } return name, STOP, -1, p.ParsePostValue() - /* - if err = p.ParsePostValue(); err != nil { - return name, STOP, 0, err - } - if isNull, err := p.ParseListBegin(); isNull || err != nil { - return name, STOP, 0, err - } - bType, err := p.ReadByte() - thetype := TType(bType) - if err != nil { - return name, thetype, 0, err - } - id, err := p.ReadI16() - return name, thetype, id, err - */ } e := fmt.Errorf("Expected \"}\" or '\"', but found: '%s'", string(b)) return "", STOP, 0, NewTProtocolExceptionWithType(INVALID_DATA, e) @@ -362,57 +374,56 @@ func (p *TSimpleJSONProtocol) ReadFieldBegin() (string, TType, int16, error) { return "", STOP, 0, NewTProtocolException(io.EOF) } -func (p *TSimpleJSONProtocol) ReadFieldEnd() error { +func (p *TSimpleJSONProtocol) ReadFieldEnd(ctx context.Context) error { return nil - //return p.ParseListEnd() } -func (p *TSimpleJSONProtocol) ReadMapBegin() (keyType TType, valueType TType, size int, e error) { +func (p *TSimpleJSONProtocol) ReadMapBegin(ctx context.Context) (keyType TType, valueType TType, size int, e error) { if isNull, e := p.ParseListBegin(); isNull || e != nil { return VOID, VOID, 0, e } // read keyType - bKeyType, e := p.ReadByte() + bKeyType, e := p.ReadByte(ctx) keyType = TType(bKeyType) if e != nil { return keyType, valueType, size, e } // read valueType - bValueType, e := p.ReadByte() + bValueType, e := p.ReadByte(ctx) valueType = TType(bValueType) if e != nil { return keyType, valueType, size, e } // read size - iSize, err := p.ReadI64() + iSize, err := p.ReadI64(ctx) size = int(iSize) return keyType, valueType, size, err } -func (p *TSimpleJSONProtocol) ReadMapEnd() error { +func (p *TSimpleJSONProtocol) ReadMapEnd(ctx context.Context) error { return p.ParseListEnd() } -func (p *TSimpleJSONProtocol) ReadListBegin() (elemType TType, size int, e error) { +func (p *TSimpleJSONProtocol) ReadListBegin(ctx context.Context) (elemType TType, size int, e error) { return p.ParseElemListBegin() } -func (p *TSimpleJSONProtocol) ReadListEnd() error { +func (p *TSimpleJSONProtocol) ReadListEnd(ctx context.Context) error { return p.ParseListEnd() } -func (p *TSimpleJSONProtocol) ReadSetBegin() (elemType TType, size int, e error) { +func (p *TSimpleJSONProtocol) ReadSetBegin(ctx context.Context) (elemType TType, size int, e error) { return p.ParseElemListBegin() } -func (p *TSimpleJSONProtocol) ReadSetEnd() error { +func (p *TSimpleJSONProtocol) ReadSetEnd(ctx context.Context) error { return p.ParseListEnd() } -func (p *TSimpleJSONProtocol) ReadBool() (bool, error) { +func (p *TSimpleJSONProtocol) ReadBool(ctx context.Context) (bool, error) { var value bool if err := p.ParsePreValue(); err != nil { @@ -467,32 +478,32 @@ func (p *TSimpleJSONProtocol) ReadBool() (bool, error) { return value, p.ParsePostValue() } -func (p *TSimpleJSONProtocol) ReadByte() (int8, error) { - v, err := p.ReadI64() +func (p *TSimpleJSONProtocol) ReadByte(ctx context.Context) (int8, error) { + v, err := p.ReadI64(ctx) return int8(v), err } -func (p *TSimpleJSONProtocol) ReadI16() (int16, error) { - v, err := p.ReadI64() +func (p *TSimpleJSONProtocol) ReadI16(ctx context.Context) (int16, error) { + v, err := p.ReadI64(ctx) return int16(v), err } -func (p *TSimpleJSONProtocol) ReadI32() (int32, error) { - v, err := p.ReadI64() +func (p *TSimpleJSONProtocol) ReadI32(ctx context.Context) (int32, error) { + v, err := p.ReadI64(ctx) return int32(v), err } -func (p *TSimpleJSONProtocol) ReadI64() (int64, error) { +func (p *TSimpleJSONProtocol) ReadI64(ctx context.Context) (int64, error) { v, _, err := p.ParseI64() return v, err } -func (p *TSimpleJSONProtocol) ReadDouble() (float64, error) { +func (p *TSimpleJSONProtocol) ReadDouble(ctx context.Context) (float64, error) { v, _, err := p.ParseF64() return v, err } -func (p *TSimpleJSONProtocol) ReadString() (string, error) { +func (p *TSimpleJSONProtocol) ReadString(ctx context.Context) (string, error) { var v string if err := p.ParsePreValue(); err != nil { return v, err @@ -522,7 +533,7 @@ func (p *TSimpleJSONProtocol) ReadString() (string, error) { return v, p.ParsePostValue() } -func (p *TSimpleJSONProtocol) ReadBinary() ([]byte, error) { +func (p *TSimpleJSONProtocol) ReadBinary(ctx context.Context) ([]byte, error) { var v []byte if err := p.ParsePreValue(); err != nil { return nil, err @@ -557,8 +568,8 @@ func (p *TSimpleJSONProtocol) Flush(ctx context.Context) (err error) { return NewTProtocolException(p.writer.Flush()) } -func (p *TSimpleJSONProtocol) Skip(fieldType TType) (err error) { - return SkipDefaultDepth(p, fieldType) +func (p *TSimpleJSONProtocol) Skip(ctx context.Context, fieldType TType) (err error) { + return SkipDefaultDepth(ctx, p, fieldType) } func (p *TSimpleJSONProtocol) Transport() TTransport { @@ -566,41 +577,41 @@ func (p *TSimpleJSONProtocol) Transport() TTransport { } func (p *TSimpleJSONProtocol) OutputPreValue() error { - cxt := _ParseContext(p.dumpContext[len(p.dumpContext)-1]) + cxt, ok := p.dumpContext.peek() + if !ok { + return errEmptyJSONContextStack + } switch cxt { case _CONTEXT_IN_LIST, _CONTEXT_IN_OBJECT_NEXT_KEY: if _, e := p.write(JSON_COMMA); e != nil { return NewTProtocolException(e) } - break case _CONTEXT_IN_OBJECT_NEXT_VALUE: if _, e := p.write(JSON_COLON); e != nil { return NewTProtocolException(e) } - break } return nil } func (p *TSimpleJSONProtocol) OutputPostValue() error { - cxt := _ParseContext(p.dumpContext[len(p.dumpContext)-1]) + cxt, ok := p.dumpContext.peek() + if !ok { + return errEmptyJSONContextStack + } switch cxt { case _CONTEXT_IN_LIST_FIRST: - p.dumpContext = p.dumpContext[:len(p.dumpContext)-1] - p.dumpContext = append(p.dumpContext, int(_CONTEXT_IN_LIST)) - break + p.dumpContext.pop() + p.dumpContext.push(_CONTEXT_IN_LIST) case _CONTEXT_IN_OBJECT_FIRST: - p.dumpContext = p.dumpContext[:len(p.dumpContext)-1] - p.dumpContext = append(p.dumpContext, int(_CONTEXT_IN_OBJECT_NEXT_VALUE)) - break + p.dumpContext.pop() + p.dumpContext.push(_CONTEXT_IN_OBJECT_NEXT_VALUE) case _CONTEXT_IN_OBJECT_NEXT_KEY: - p.dumpContext = p.dumpContext[:len(p.dumpContext)-1] - p.dumpContext = append(p.dumpContext, int(_CONTEXT_IN_OBJECT_NEXT_VALUE)) - break + p.dumpContext.pop() + p.dumpContext.push(_CONTEXT_IN_OBJECT_NEXT_VALUE) case _CONTEXT_IN_OBJECT_NEXT_VALUE: - p.dumpContext = p.dumpContext[:len(p.dumpContext)-1] - p.dumpContext = append(p.dumpContext, int(_CONTEXT_IN_OBJECT_NEXT_KEY)) - break + p.dumpContext.pop() + p.dumpContext.push(_CONTEXT_IN_OBJECT_NEXT_KEY) } return nil } @@ -615,10 +626,13 @@ func (p *TSimpleJSONProtocol) OutputBool(value bool) error { } else { v = string(JSON_FALSE) } - switch _ParseContext(p.dumpContext[len(p.dumpContext)-1]) { + cxt, ok := p.dumpContext.peek() + if !ok { + return errEmptyJSONContextStack + } + switch cxt { case _CONTEXT_IN_OBJECT_FIRST, _CONTEXT_IN_OBJECT_NEXT_KEY: v = jsonQuote(v) - default: } if e := p.OutputStringData(v); e != nil { return e @@ -648,11 +662,14 @@ func (p *TSimpleJSONProtocol) OutputF64(value float64) error { } else if math.IsInf(value, -1) { v = string(JSON_QUOTE) + JSON_NEGATIVE_INFINITY + string(JSON_QUOTE) } else { + cxt, ok := p.dumpContext.peek() + if !ok { + return errEmptyJSONContextStack + } v = strconv.FormatFloat(value, 'g', -1, 64) - switch _ParseContext(p.dumpContext[len(p.dumpContext)-1]) { + switch cxt { case _CONTEXT_IN_OBJECT_FIRST, _CONTEXT_IN_OBJECT_NEXT_KEY: v = string(JSON_QUOTE) + v + string(JSON_QUOTE) - default: } } if e := p.OutputStringData(v); e != nil { @@ -665,11 +682,14 @@ func (p *TSimpleJSONProtocol) OutputI64(value int64) error { if e := p.OutputPreValue(); e != nil { return e } + cxt, ok := p.dumpContext.peek() + if !ok { + return errEmptyJSONContextStack + } v := strconv.FormatInt(value, 10) - switch _ParseContext(p.dumpContext[len(p.dumpContext)-1]) { + switch cxt { case _CONTEXT_IN_OBJECT_FIRST, _CONTEXT_IN_OBJECT_NEXT_KEY: v = jsonQuote(v) - default: } if e := p.OutputStringData(v); e != nil { return e @@ -699,7 +719,7 @@ func (p *TSimpleJSONProtocol) OutputObjectBegin() error { if _, e := p.write(JSON_LBRACE); e != nil { return NewTProtocolException(e) } - p.dumpContext = append(p.dumpContext, int(_CONTEXT_IN_OBJECT_FIRST)) + p.dumpContext.push(_CONTEXT_IN_OBJECT_FIRST) return nil } @@ -707,7 +727,10 @@ func (p *TSimpleJSONProtocol) OutputObjectEnd() error { if _, e := p.write(JSON_RBRACE); e != nil { return NewTProtocolException(e) } - p.dumpContext = p.dumpContext[:len(p.dumpContext)-1] + _, ok := p.dumpContext.pop() + if !ok { + return errEmptyJSONContextStack + } if e := p.OutputPostValue(); e != nil { return e } @@ -721,7 +744,7 @@ func (p *TSimpleJSONProtocol) OutputListBegin() error { if _, e := p.write(JSON_LBRACKET); e != nil { return NewTProtocolException(e) } - p.dumpContext = append(p.dumpContext, int(_CONTEXT_IN_LIST_FIRST)) + p.dumpContext.push(_CONTEXT_IN_LIST_FIRST) return nil } @@ -729,7 +752,10 @@ func (p *TSimpleJSONProtocol) OutputListEnd() error { if _, e := p.write(JSON_RBRACKET); e != nil { return NewTProtocolException(e) } - p.dumpContext = p.dumpContext[:len(p.dumpContext)-1] + _, ok := p.dumpContext.pop() + if !ok { + return errEmptyJSONContextStack + } if e := p.OutputPostValue(); e != nil { return e } @@ -740,10 +766,10 @@ func (p *TSimpleJSONProtocol) OutputElemListBegin(elemType TType, size int) erro if e := p.OutputListBegin(); e != nil { return e } - if e := p.WriteByte(int8(elemType)); e != nil { + if e := p.OutputI64(int64(elemType)); e != nil { return e } - if e := p.WriteI64(int64(size)); e != nil { + if e := p.OutputI64(int64(size)); e != nil { return e } return nil @@ -753,7 +779,10 @@ func (p *TSimpleJSONProtocol) ParsePreValue() error { if e := p.readNonSignificantWhitespace(); e != nil { return NewTProtocolException(e) } - cxt := _ParseContext(p.parseContextStack[len(p.parseContextStack)-1]) + cxt, ok := p.parseContextStack.peek() + if !ok { + return errEmptyJSONContextStack + } b, _ := p.reader.Peek(1) switch cxt { case _CONTEXT_IN_LIST: @@ -772,7 +801,6 @@ func (p *TSimpleJSONProtocol) ParsePreValue() error { return NewTProtocolExceptionWithType(INVALID_DATA, e) } } - break case _CONTEXT_IN_OBJECT_NEXT_KEY: if len(b) > 0 { switch b[0] { @@ -789,7 +817,6 @@ func (p *TSimpleJSONProtocol) ParsePreValue() error { return NewTProtocolExceptionWithType(INVALID_DATA, e) } } - break case _CONTEXT_IN_OBJECT_NEXT_VALUE: if len(b) > 0 { switch b[0] { @@ -804,7 +831,6 @@ func (p *TSimpleJSONProtocol) ParsePreValue() error { return NewTProtocolExceptionWithType(INVALID_DATA, e) } } - break } return nil } @@ -813,20 +839,20 @@ func (p *TSimpleJSONProtocol) ParsePostValue() error { if e := p.readNonSignificantWhitespace(); e != nil { return NewTProtocolException(e) } - cxt := _ParseContext(p.parseContextStack[len(p.parseContextStack)-1]) + cxt, ok := p.parseContextStack.peek() + if !ok { + return errEmptyJSONContextStack + } switch cxt { case _CONTEXT_IN_LIST_FIRST: - p.parseContextStack = p.parseContextStack[:len(p.parseContextStack)-1] - p.parseContextStack = append(p.parseContextStack, int(_CONTEXT_IN_LIST)) - break + p.parseContextStack.pop() + p.parseContextStack.push(_CONTEXT_IN_LIST) case _CONTEXT_IN_OBJECT_FIRST, _CONTEXT_IN_OBJECT_NEXT_KEY: - p.parseContextStack = p.parseContextStack[:len(p.parseContextStack)-1] - p.parseContextStack = append(p.parseContextStack, int(_CONTEXT_IN_OBJECT_NEXT_VALUE)) - break + p.parseContextStack.pop() + p.parseContextStack.push(_CONTEXT_IN_OBJECT_NEXT_VALUE) case _CONTEXT_IN_OBJECT_NEXT_VALUE: - p.parseContextStack = p.parseContextStack[:len(p.parseContextStack)-1] - p.parseContextStack = append(p.parseContextStack, int(_CONTEXT_IN_OBJECT_NEXT_KEY)) - break + p.parseContextStack.pop() + p.parseContextStack.push(_CONTEXT_IN_OBJECT_NEXT_KEY) } return nil } @@ -979,7 +1005,7 @@ func (p *TSimpleJSONProtocol) ParseObjectStart() (bool, error) { } if len(b) > 0 && b[0] == JSON_LBRACE[0] { p.reader.ReadByte() - p.parseContextStack = append(p.parseContextStack, int(_CONTEXT_IN_OBJECT_FIRST)) + p.parseContextStack.push(_CONTEXT_IN_OBJECT_FIRST) return false, nil } else if p.safePeekContains(JSON_NULL) { return true, nil @@ -992,7 +1018,7 @@ func (p *TSimpleJSONProtocol) ParseObjectEnd() error { if isNull, err := p.readIfNull(); isNull || err != nil { return err } - cxt := _ParseContext(p.parseContextStack[len(p.parseContextStack)-1]) + cxt, _ := p.parseContextStack.peek() if (cxt != _CONTEXT_IN_OBJECT_FIRST) && (cxt != _CONTEXT_IN_OBJECT_NEXT_KEY) { e := fmt.Errorf("Expected to be in the Object Context, but not in Object Context (%d)", cxt) return NewTProtocolExceptionWithType(INVALID_DATA, e) @@ -1010,7 +1036,7 @@ func (p *TSimpleJSONProtocol) ParseObjectEnd() error { break } } - p.parseContextStack = p.parseContextStack[:len(p.parseContextStack)-1] + p.parseContextStack.pop() return p.ParsePostValue() } @@ -1024,7 +1050,7 @@ func (p *TSimpleJSONProtocol) ParseListBegin() (isNull bool, err error) { return false, err } if len(b) >= 1 && b[0] == JSON_LBRACKET[0] { - p.parseContextStack = append(p.parseContextStack, int(_CONTEXT_IN_LIST_FIRST)) + p.parseContextStack.push(_CONTEXT_IN_LIST_FIRST) p.reader.ReadByte() isNull = false } else if p.safePeekContains(JSON_NULL) { @@ -1039,12 +1065,12 @@ func (p *TSimpleJSONProtocol) ParseElemListBegin() (elemType TType, size int, e if isNull, e := p.ParseListBegin(); isNull || e != nil { return VOID, 0, e } - bElemType, err := p.ReadByte() + bElemType, _, err := p.ParseI64() elemType = TType(bElemType) if err != nil { return elemType, size, err } - nSize, err2 := p.ReadI64() + nSize, _, err2 := p.ParseI64() size = int(nSize) return elemType, size, err2 } @@ -1053,7 +1079,7 @@ func (p *TSimpleJSONProtocol) ParseListEnd() error { if isNull, err := p.readIfNull(); isNull || err != nil { return err } - cxt := _ParseContext(p.parseContextStack[len(p.parseContextStack)-1]) + cxt, _ := p.parseContextStack.peek() if cxt != _CONTEXT_IN_LIST { e := fmt.Errorf("Expected to be in the List Context, but not in List Context (%d)", cxt) return NewTProtocolExceptionWithType(INVALID_DATA, e) @@ -1071,8 +1097,10 @@ func (p *TSimpleJSONProtocol) ParseListEnd() error { break } } - p.parseContextStack = p.parseContextStack[:len(p.parseContextStack)-1] - if _ParseContext(p.parseContextStack[len(p.parseContextStack)-1]) == _CONTEXT_IN_TOPLEVEL { + p.parseContextStack.pop() + if cxt, ok := p.parseContextStack.peek(); !ok { + return errEmptyJSONContextStack + } else if cxt == _CONTEXT_IN_TOPLEVEL { return nil } return p.ParsePostValue() @@ -1316,7 +1344,7 @@ func (p *TSimpleJSONProtocol) readNumeric() (Numeric, error) { func (p *TSimpleJSONProtocol) safePeekContains(b []byte) bool { for i := 0; i < len(b); i++ { a, _ := p.reader.Peek(i + 1) - if len(a) == 0 || a[i] != b[i] { + if len(a) < (i+1) || a[i] != b[i] { return false } } @@ -1325,8 +1353,8 @@ func (p *TSimpleJSONProtocol) safePeekContains(b []byte) bool { // Reset the context stack to its initial state. func (p *TSimpleJSONProtocol) resetContextStack() { - p.parseContextStack = []int{int(_CONTEXT_IN_TOPLEVEL)} - p.dumpContext = []int{int(_CONTEXT_IN_TOPLEVEL)} + p.parseContextStack = jsonContextStack{_CONTEXT_IN_TOPLEVEL} + p.dumpContext = jsonContextStack{_CONTEXT_IN_TOPLEVEL} } func (p *TSimpleJSONProtocol) write(b []byte) (int, error) { @@ -1336,3 +1364,10 @@ func (p *TSimpleJSONProtocol) write(b []byte) (int, error) { } return n, err } + +// SetTConfiguration implements TConfigurationSetter for propagation. +func (p *TSimpleJSONProtocol) SetTConfiguration(conf *TConfiguration) { + PropagateTConfiguration(p.trans, conf) +} + +var _ TConfigurationSetter = (*TSimpleJSONProtocol)(nil) diff --git a/lib/go/thrift/simple_json_protocol_test.go b/lib/go/thrift/simple_json_protocol_test.go index 7b98082a4ee..89753c6148a 100644 --- a/lib/go/thrift/simple_json_protocol_test.go +++ b/lib/go/thrift/simple_json_protocol_test.go @@ -35,7 +35,7 @@ func TestWriteSimpleJSONProtocolBool(t *testing.T) { trans := NewTMemoryBuffer() p := NewTSimpleJSONProtocol(trans) for _, value := range BOOL_VALUES { - if e := p.WriteBool(value); e != nil { + if e := p.WriteBool(context.Background(), value); e != nil { t.Fatalf("Unable to write %s value %v due to error: %s", thetype, value, e.Error()) } if e := p.Flush(context.Background()); e != nil { @@ -66,7 +66,7 @@ func TestReadSimpleJSONProtocolBool(t *testing.T) { } trans.Flush(context.Background()) s := trans.String() - v, e := p.ReadBool() + v, e := p.ReadBool(context.Background()) if e != nil { t.Fatalf("Unable to read %s value %v due to error: %s", thetype, value, e.Error()) } @@ -86,7 +86,7 @@ func TestWriteSimpleJSONProtocolByte(t *testing.T) { trans := NewTMemoryBuffer() p := NewTSimpleJSONProtocol(trans) for _, value := range BYTE_VALUES { - if e := p.WriteByte(value); e != nil { + if e := p.WriteByte(context.Background(), value); e != nil { t.Fatalf("Unable to write %s value %v due to error: %s", thetype, value, e.Error()) } if e := p.Flush(context.Background()); e != nil { @@ -113,7 +113,7 @@ func TestReadSimpleJSONProtocolByte(t *testing.T) { trans.WriteString(strconv.Itoa(int(value))) trans.Flush(context.Background()) s := trans.String() - v, e := p.ReadByte() + v, e := p.ReadByte(context.Background()) if e != nil { t.Fatalf("Unable to read %s value %v due to error: %s", thetype, value, e.Error()) } @@ -133,7 +133,7 @@ func TestWriteSimpleJSONProtocolI16(t *testing.T) { trans := NewTMemoryBuffer() p := NewTSimpleJSONProtocol(trans) for _, value := range INT16_VALUES { - if e := p.WriteI16(value); e != nil { + if e := p.WriteI16(context.Background(), value); e != nil { t.Fatalf("Unable to write %s value %v due to error: %s", thetype, value, e.Error()) } if e := p.Flush(context.Background()); e != nil { @@ -160,7 +160,7 @@ func TestReadSimpleJSONProtocolI16(t *testing.T) { trans.WriteString(strconv.Itoa(int(value))) trans.Flush(context.Background()) s := trans.String() - v, e := p.ReadI16() + v, e := p.ReadI16(context.Background()) if e != nil { t.Fatalf("Unable to read %s value %v due to error: %s", thetype, value, e.Error()) } @@ -180,7 +180,7 @@ func TestWriteSimpleJSONProtocolI32(t *testing.T) { trans := NewTMemoryBuffer() p := NewTSimpleJSONProtocol(trans) for _, value := range INT32_VALUES { - if e := p.WriteI32(value); e != nil { + if e := p.WriteI32(context.Background(), value); e != nil { t.Fatalf("Unable to write %s value %v due to error: %s", thetype, value, e.Error()) } if e := p.Flush(context.Background()); e != nil { @@ -207,7 +207,7 @@ func TestReadSimpleJSONProtocolI32(t *testing.T) { trans.WriteString(strconv.Itoa(int(value))) trans.Flush(context.Background()) s := trans.String() - v, e := p.ReadI32() + v, e := p.ReadI32(context.Background()) if e != nil { t.Fatalf("Unable to read %s value %v due to error: %s", thetype, value, e.Error()) } @@ -231,7 +231,7 @@ func TestReadSimpleJSONProtocolI32Null(t *testing.T) { trans.WriteString(value) trans.Flush(context.Background()) s := trans.String() - v, e := p.ReadI32() + v, e := p.ReadI32(context.Background()) if e != nil { t.Fatalf("Unable to read %s value %v due to error: %s", thetype, value, e.Error()) @@ -248,7 +248,7 @@ func TestWriteSimpleJSONProtocolI64(t *testing.T) { trans := NewTMemoryBuffer() p := NewTSimpleJSONProtocol(trans) for _, value := range INT64_VALUES { - if e := p.WriteI64(value); e != nil { + if e := p.WriteI64(context.Background(), value); e != nil { t.Fatalf("Unable to write %s value %v due to error: %s", thetype, value, e.Error()) } if e := p.Flush(context.Background()); e != nil { @@ -275,7 +275,7 @@ func TestReadSimpleJSONProtocolI64(t *testing.T) { trans.WriteString(strconv.FormatInt(value, 10)) trans.Flush(context.Background()) s := trans.String() - v, e := p.ReadI64() + v, e := p.ReadI64(context.Background()) if e != nil { t.Fatalf("Unable to read %s value %v due to error: %s", thetype, value, e.Error()) } @@ -299,7 +299,7 @@ func TestReadSimpleJSONProtocolI64Null(t *testing.T) { trans.WriteString(value) trans.Flush(context.Background()) s := trans.String() - v, e := p.ReadI64() + v, e := p.ReadI64(context.Background()) if e != nil { t.Fatalf("Unable to read %s value %v due to error: %s", thetype, value, e.Error()) @@ -316,7 +316,7 @@ func TestWriteSimpleJSONProtocolDouble(t *testing.T) { trans := NewTMemoryBuffer() p := NewTSimpleJSONProtocol(trans) for _, value := range DOUBLE_VALUES { - if e := p.WriteDouble(value); e != nil { + if e := p.WriteDouble(context.Background(), value); e != nil { t.Fatalf("Unable to write %s value %v due to error: %s", thetype, value, e.Error()) } if e := p.Flush(context.Background()); e != nil { @@ -358,7 +358,7 @@ func TestReadSimpleJSONProtocolDouble(t *testing.T) { trans.WriteString(n.String()) trans.Flush(context.Background()) s := trans.String() - v, e := p.ReadDouble() + v, e := p.ReadDouble(context.Background()) if e != nil { t.Fatalf("Unable to read %s value %v due to error: %s", thetype, value, e.Error()) } @@ -392,7 +392,7 @@ func TestWriteSimpleJSONProtocolString(t *testing.T) { trans := NewTMemoryBuffer() p := NewTSimpleJSONProtocol(trans) for _, value := range STRING_VALUES { - if e := p.WriteString(value); e != nil { + if e := p.WriteString(context.Background(), value); e != nil { t.Fatalf("Unable to write %s value %v due to error: %s", thetype, value, e.Error()) } if e := p.Flush(context.Background()); e != nil { @@ -419,7 +419,7 @@ func TestReadSimpleJSONProtocolString(t *testing.T) { trans.WriteString(jsonQuote(value)) trans.Flush(context.Background()) s := trans.String() - v, e := p.ReadString() + v, e := p.ReadString(context.Background()) if e != nil { t.Fatalf("Unable to read %s value %v due to error: %s", thetype, value, e.Error()) } @@ -443,7 +443,7 @@ func TestReadSimpleJSONProtocolStringNull(t *testing.T) { trans.WriteString(value) trans.Flush(context.Background()) s := trans.String() - v, e := p.ReadString() + v, e := p.ReadString(context.Background()) if e != nil { t.Fatalf("Unable to read %s value %v due to error: %s", thetype, value, e.Error()) } @@ -462,7 +462,7 @@ func TestWriteSimpleJSONProtocolBinary(t *testing.T) { b64String := string(b64value) trans := NewTMemoryBuffer() p := NewTSimpleJSONProtocol(trans) - if e := p.WriteBinary(value); e != nil { + if e := p.WriteBinary(context.Background(), value); e != nil { t.Fatalf("Unable to write %s value %v due to error: %s", thetype, value, e.Error()) } if e := p.Flush(context.Background()); e != nil { @@ -490,7 +490,7 @@ func TestReadSimpleJSONProtocolBinary(t *testing.T) { trans.WriteString(jsonQuote(b64String)) trans.Flush(context.Background()) s := trans.String() - v, e := p.ReadBinary() + v, e := p.ReadBinary(context.Background()) if e != nil { t.Fatalf("Unable to read %s value %v due to error: %s", thetype, value, e.Error()) } @@ -519,7 +519,7 @@ func TestReadSimpleJSONProtocolBinaryNull(t *testing.T) { trans.WriteString(value) trans.Flush(context.Background()) s := trans.String() - b, e := p.ReadBinary() + b, e := p.ReadBinary(context.Background()) v := string(b) if e != nil { @@ -536,13 +536,13 @@ func TestWriteSimpleJSONProtocolList(t *testing.T) { thetype := "list" trans := NewTMemoryBuffer() p := NewTSimpleJSONProtocol(trans) - p.WriteListBegin(TType(DOUBLE), len(DOUBLE_VALUES)) + p.WriteListBegin(context.Background(), TType(DOUBLE), len(DOUBLE_VALUES)) for _, value := range DOUBLE_VALUES { - if e := p.WriteDouble(value); e != nil { + if e := p.WriteDouble(context.Background(), value); e != nil { t.Fatalf("Unable to write %s value %v due to error: %s", thetype, value, e.Error()) } } - p.WriteListEnd() + p.WriteListEnd(context.Background()) if e := p.Flush(context.Background()); e != nil { t.Fatalf("Unable to write %s due to error flushing: %s", thetype, e.Error()) } @@ -590,13 +590,13 @@ func TestWriteSimpleJSONProtocolSet(t *testing.T) { thetype := "set" trans := NewTMemoryBuffer() p := NewTSimpleJSONProtocol(trans) - p.WriteSetBegin(TType(DOUBLE), len(DOUBLE_VALUES)) + p.WriteSetBegin(context.Background(), TType(DOUBLE), len(DOUBLE_VALUES)) for _, value := range DOUBLE_VALUES { - if e := p.WriteDouble(value); e != nil { + if e := p.WriteDouble(context.Background(), value); e != nil { t.Fatalf("Unable to write %s value %v due to error: %s", thetype, value, e.Error()) } } - p.WriteSetEnd() + p.WriteSetEnd(context.Background()) if e := p.Flush(context.Background()); e != nil { t.Fatalf("Unable to write %s due to error flushing: %s", thetype, e.Error()) } @@ -644,16 +644,16 @@ func TestWriteSimpleJSONProtocolMap(t *testing.T) { thetype := "map" trans := NewTMemoryBuffer() p := NewTSimpleJSONProtocol(trans) - p.WriteMapBegin(TType(I32), TType(DOUBLE), len(DOUBLE_VALUES)) + p.WriteMapBegin(context.Background(), TType(I32), TType(DOUBLE), len(DOUBLE_VALUES)) for k, value := range DOUBLE_VALUES { - if e := p.WriteI32(int32(k)); e != nil { + if e := p.WriteI32(context.Background(), int32(k)); e != nil { t.Fatalf("Unable to write %s key int32 value %v due to error: %s", thetype, k, e.Error()) } - if e := p.WriteDouble(value); e != nil { + if e := p.WriteDouble(context.Background(), value); e != nil { t.Fatalf("Unable to write %s value float64 value %v due to error: %s", thetype, value, e.Error()) } } - p.WriteMapEnd() + p.WriteMapEnd(context.Background()) if e := p.Flush(context.Background()); e != nil { t.Fatalf("Unable to write %s due to error flushing: %s", thetype, e.Error()) } @@ -682,7 +682,7 @@ func TestWriteSimpleJSONProtocolMap(t *testing.T) { strv := l[k*2+4] ik, err := strconv.Atoi(strk) if err != nil { - t.Fatalf("Bad value for %s index %v, wrote: %v, expected: %v, error: %s", thetype, k, strk, string(k), err.Error()) + t.Fatalf("Bad value for %s index %v, wrote: %v, expected: %v, error: %s", thetype, k, strk, k, err.Error()) } if ik != k { t.Fatalf("Bad value for %s index %v, wrote: %v, expected: %v", thetype, k, strk, k) @@ -714,3 +714,80 @@ func TestWriteSimpleJSONProtocolMap(t *testing.T) { } trans.Close() } + +func TestWriteSimpleJSONProtocolSafePeek(t *testing.T) { + trans := NewTMemoryBuffer() + p := NewTSimpleJSONProtocol(trans) + trans.Write([]byte{'a', 'b'}) + trans.Flush(context.Background()) + + test1 := p.safePeekContains([]byte{'a', 'b'}) + if !test1 { + t.Fatalf("Should match at test 1") + } + + test2 := p.safePeekContains([]byte{'a', 'b', 'c', 'd'}) + if test2 { + t.Fatalf("Should not match at test 2") + } + + test3 := p.safePeekContains([]byte{'x', 'y'}) + if test3 { + t.Fatalf("Should not match at test 3") + } +} + +func TestJSONContextStack(t *testing.T) { + var stack jsonContextStack + t.Run("empty-peek", func(t *testing.T) { + v, ok := stack.peek() + if ok { + t.Error("peek() on empty should return ok: false") + } + expected := _CONTEXT_INVALID + if v != expected { + t.Errorf("Expected value from peek() to be %v(%d), got %v(%d)", expected, expected, v, v) + } + }) + t.Run("empty-pop", func(t *testing.T) { + v, ok := stack.pop() + if ok { + t.Error("pop() on empty should return ok: false") + } + expected := _CONTEXT_INVALID + if v != expected { + t.Errorf("Expected value from pop() to be %v(%d), got %v(%d)", expected, expected, v, v) + } + }) + t.Run("push-peek-pop", func(t *testing.T) { + expected := _CONTEXT_INVALID + stack.push(expected) + if len(stack) != 1 { + t.Errorf("Expected stack to be as size 1 after push, got %#v", stack) + } + v, ok := stack.peek() + if !ok { + t.Error("peek() on non-empty should return ok: true") + } + if v != expected { + t.Errorf("Expected value from peek() to be %v(%d), got %v(%d)", expected, expected, v, v) + } + if len(stack) != 1 { + t.Errorf("Expected peek() to be read-only, got %#v", stack) + } + v, ok = stack.pop() + if !ok { + t.Error("pop() on non-empty should return ok: true") + } + if v != expected { + t.Errorf("Expected value from pop() to be %v(%d), got %v(%d)", expected, expected, v, v) + } + if len(stack) != 0 { + t.Errorf("Expected pop() to empty the stack, got %#v", stack) + } + }) +} + +func TestTSimpleJSONProtocolUnmatchedBeginEnd(t *testing.T) { + UnmatchedBeginEndProtocolTest(t, NewTSimpleJSONProtocolFactory()) +} diff --git a/lib/go/thrift/simple_server.go b/lib/go/thrift/simple_server.go index 60358025160..563cbfc694a 100644 --- a/lib/go/thrift/simple_server.go +++ b/lib/go/thrift/simple_server.go @@ -20,12 +20,34 @@ package thrift import ( - "log" - "runtime/debug" + "errors" + "fmt" + "io" "sync" "sync/atomic" + "time" ) +// ErrAbandonRequest is a special error server handler implementations can +// return to indicate that the request has been abandoned. +// +// TSimpleServer will check for this error, and close the client connection +// instead of writing the response/error back to the client. +// +// It shall only be used when the server handler implementation know that the +// client already abandoned the request (by checking that the passed in context +// is already canceled, for example). +var ErrAbandonRequest = errors.New("request abandoned") + +// ServerConnectivityCheckInterval defines the ticker interval used by +// connectivity check in thrift compiled TProcessorFunc implementations. +// +// It's defined as a variable instead of constant, so that thrift server +// implementations can change its value to control the behavior. +// +// If it's changed to <=0, the feature will be disabled. +var ServerConnectivityCheckInterval = time.Millisecond * 5 + /* * This is not a typical TSimpleServer as it is not blocked after accept a socket. * It is more like a TThreadedServer that can handle different connections in different goroutines. @@ -42,6 +64,11 @@ type TSimpleServer struct { outputTransportFactory TTransportFactory inputProtocolFactory TProtocolFactory outputProtocolFactory TProtocolFactory + + // Headers to auto forward in THeaderProtocol + forwardHeaders []string + + logger Logger } func NewTSimpleServer2(processor TProcessor, serverTransport TServerTransport) *TSimpleServer { @@ -125,6 +152,34 @@ func (p *TSimpleServer) Listen() error { return p.serverTransport.Listen() } +// SetForwardHeaders sets the list of header keys that will be auto forwarded +// while using THeaderProtocol. +// +// "forward" means that when the server is also a client to other upstream +// thrift servers, the context object user gets in the processor functions will +// have both read and write headers set, with write headers being forwarded. +// Users can always override the write headers by calling SetWriteHeaderList +// before calling thrift client functions. +func (p *TSimpleServer) SetForwardHeaders(headers []string) { + size := len(headers) + if size == 0 { + p.forwardHeaders = nil + return + } + + keys := make([]string, size) + copy(keys, headers) + p.forwardHeaders = keys +} + +// SetLogger sets the logger used by this TSimpleServer. +// +// If no logger was set before Serve is called, a default logger using standard +// log library will be used. +func (p *TSimpleServer) SetLogger(logger Logger) { + p.logger = logger +} + func (p *TSimpleServer) innerAccept() (int32, error) { client, err := p.serverTransport.Accept() p.mu.Lock() @@ -141,7 +196,7 @@ func (p *TSimpleServer) innerAccept() (int32, error) { go func() { defer p.wg.Done() if err := p.processRequests(client); err != nil { - log.Println("error processing request:", err) + p.logger(fmt.Sprintf("error processing request: %v", err)) } }() } @@ -161,6 +216,8 @@ func (p *TSimpleServer) AcceptLoop() error { } func (p *TSimpleServer) Serve() error { + p.logger = fallbackLogger(p.logger) + err := p.Listen() if err != nil { return err @@ -181,23 +238,49 @@ func (p *TSimpleServer) Stop() error { return nil } -func (p *TSimpleServer) processRequests(client TTransport) error { +// If err is actually EOF, return nil, otherwise return err as-is. +func treatEOFErrorsAsNil(err error) error { + if err == nil { + return nil + } + if errors.Is(err, io.EOF) { + return nil + } + var te TTransportException + if errors.As(err, &te) && te.TypeId() == END_OF_FILE { + return nil + } + return err +} + +func (p *TSimpleServer) processRequests(client TTransport) (err error) { + defer func() { + err = treatEOFErrorsAsNil(err) + }() + processor := p.processorFactory.GetProcessor(client) inputTransport, err := p.inputTransportFactory.GetTransport(client) if err != nil { return err } - outputTransport, err := p.outputTransportFactory.GetTransport(client) - if err != nil { - return err - } inputProtocol := p.inputProtocolFactory.GetProtocol(inputTransport) - outputProtocol := p.outputProtocolFactory.GetProtocol(outputTransport) - defer func() { - if e := recover(); e != nil { - log.Printf("panic in processor: %s: %s", e, debug.Stack()) + var outputTransport TTransport + var outputProtocol TProtocol + + // for THeaderProtocol, we must use the same protocol instance for + // input and output so that the response is in the same dialect that + // the server detected the request was in. + headerProtocol, ok := inputProtocol.(*THeaderProtocol) + if ok { + outputProtocol = inputProtocol + } else { + oTrans, err := p.outputTransportFactory.GetTransport(client) + if err != nil { + return err } - }() + outputTransport = oTrans + outputProtocol = p.outputProtocolFactory.GetProtocol(outputTransport) + } if inputTransport != nil { defer inputTransport.Close() @@ -210,13 +293,35 @@ func (p *TSimpleServer) processRequests(client TTransport) error { return nil } - ok, err := processor.Process(defaultCtx, inputProtocol, outputProtocol) - if err, ok := err.(TTransportException); ok && err.TypeId() == END_OF_FILE { - return nil - } else if err != nil { + ctx := SetResponseHelper( + defaultCtx, + TResponseHelper{ + THeaderResponseHelper: NewTHeaderResponseHelper(outputProtocol), + }, + ) + if headerProtocol != nil { + // We need to call ReadFrame here, otherwise we won't + // get any headers on the AddReadTHeaderToContext call. + // + // ReadFrame is safe to be called multiple times so it + // won't break when it's called again later when we + // actually start to read the message. + if err := headerProtocol.ReadFrame(ctx); err != nil { + return err + } + ctx = AddReadTHeaderToContext(ctx, headerProtocol.GetReadHeaders()) + ctx = SetWriteHeaderList(ctx, p.forwardHeaders) + } + + ok, err := processor.Process(ctx, inputProtocol, outputProtocol) + if errors.Is(err, ErrAbandonRequest) { + return client.Close() + } + if errors.As(err, new(TTransportException)) && err != nil { return err } - if err, ok := err.(TApplicationException); ok && err.TypeId() == UNKNOWN_METHOD { + var tae TApplicationException + if errors.As(err, &tae) && tae.TypeId() == UNKNOWN_METHOD { continue } if !ok { diff --git a/lib/go/thrift/socket.go b/lib/go/thrift/socket.go index 8854279651f..e911bf16681 100644 --- a/lib/go/thrift/socket.go +++ b/lib/go/thrift/socket.go @@ -26,50 +26,116 @@ import ( ) type TSocket struct { - conn net.Conn - addr net.Addr - timeout time.Duration + conn *socketConn + addr net.Addr + cfg *TConfiguration + + connectTimeout time.Duration + socketTimeout time.Duration } -// NewTSocket creates a net.Conn-backed TTransport, given a host and port -// -// Example: -// trans, err := thrift.NewTSocket("localhost:9090") +// Deprecated: Use NewTSocketConf instead. func NewTSocket(hostPort string) (*TSocket, error) { - return NewTSocketTimeout(hostPort, 0) + return NewTSocketConf(hostPort, &TConfiguration{ + noPropagation: true, + }) } -// NewTSocketTimeout creates a net.Conn-backed TTransport, given a host and port -// it also accepts a timeout as a time.Duration -func NewTSocketTimeout(hostPort string, timeout time.Duration) (*TSocket, error) { - //conn, err := net.DialTimeout(network, address, timeout) +// NewTSocketConf creates a net.Conn-backed TTransport, given a host and port. +// +// Example: +// +// trans, err := thrift.NewTSocketConf("localhost:9090", &TConfiguration{ +// ConnectTimeout: time.Second, // Use 0 for no timeout +// SocketTimeout: time.Second, // Use 0 for no timeout +// }) +func NewTSocketConf(hostPort string, conf *TConfiguration) (*TSocket, error) { addr, err := net.ResolveTCPAddr("tcp", hostPort) if err != nil { return nil, err } - return NewTSocketFromAddrTimeout(addr, timeout), nil + return NewTSocketFromAddrConf(addr, conf), nil } -// Creates a TSocket from a net.Addr -func NewTSocketFromAddrTimeout(addr net.Addr, timeout time.Duration) *TSocket { - return &TSocket{addr: addr, timeout: timeout} +// Deprecated: Use NewTSocketConf instead. +func NewTSocketTimeout(hostPort string, connTimeout time.Duration, soTimeout time.Duration) (*TSocket, error) { + return NewTSocketConf(hostPort, &TConfiguration{ + ConnectTimeout: connTimeout, + SocketTimeout: soTimeout, + + noPropagation: true, + }) } -// Creates a TSocket from an existing net.Conn -func NewTSocketFromConnTimeout(conn net.Conn, timeout time.Duration) *TSocket { - return &TSocket{conn: conn, addr: conn.RemoteAddr(), timeout: timeout} +// NewTSocketFromAddrConf creates a TSocket from a net.Addr +func NewTSocketFromAddrConf(addr net.Addr, conf *TConfiguration) *TSocket { + return &TSocket{ + addr: addr, + cfg: conf, + } +} + +// Deprecated: Use NewTSocketFromAddrConf instead. +func NewTSocketFromAddrTimeout(addr net.Addr, connTimeout time.Duration, soTimeout time.Duration) *TSocket { + return NewTSocketFromAddrConf(addr, &TConfiguration{ + ConnectTimeout: connTimeout, + SocketTimeout: soTimeout, + + noPropagation: true, + }) +} + +// NewTSocketFromConnConf creates a TSocket from an existing net.Conn. +func NewTSocketFromConnConf(conn net.Conn, conf *TConfiguration) *TSocket { + return &TSocket{ + conn: wrapSocketConn(conn), + addr: conn.RemoteAddr(), + cfg: conf, + } +} + +// Deprecated: Use NewTSocketFromConnConf instead. +func NewTSocketFromConnTimeout(conn net.Conn, socketTimeout time.Duration) *TSocket { + return NewTSocketFromConnConf(conn, &TConfiguration{ + SocketTimeout: socketTimeout, + + noPropagation: true, + }) +} + +// SetTConfiguration implements TConfigurationSetter. +// +// It can be used to set connect and socket timeouts. +func (p *TSocket) SetTConfiguration(conf *TConfiguration) { + p.cfg = conf +} + +// Sets the connect timeout +func (p *TSocket) SetConnTimeout(timeout time.Duration) error { + if p.cfg == nil { + p.cfg = &TConfiguration{ + noPropagation: true, + } + } + p.cfg.ConnectTimeout = timeout + return nil } // Sets the socket timeout -func (p *TSocket) SetTimeout(timeout time.Duration) error { - p.timeout = timeout +func (p *TSocket) SetSocketTimeout(timeout time.Duration) error { + if p.cfg == nil { + p.cfg = &TConfiguration{ + noPropagation: true, + } + } + p.cfg.SocketTimeout = timeout return nil } func (p *TSocket) pushDeadline(read, write bool) { var t time.Time - if p.timeout > 0 { - t = time.Now().Add(time.Duration(p.timeout)) + if timeout := p.cfg.GetSocketTimeout(); timeout > 0 { + t = time.Now().Add(time.Duration(timeout)) } if read && write { p.conn.SetDeadline(t) @@ -82,7 +148,7 @@ func (p *TSocket) pushDeadline(read, write bool) { // Connects the socket, creating a new socket object if necessary. func (p *TSocket) Open() error { - if p.IsOpen() { + if p.conn.isValid() { return NewTTransportException(ALREADY_OPEN, "Socket already connected.") } if p.addr == nil { @@ -95,7 +161,11 @@ func (p *TSocket) Open() error { return NewTTransportException(NOT_OPEN, "Cannot open bad address.") } var err error - if p.conn, err = net.DialTimeout(p.addr.Network(), p.addr.String(), p.timeout); err != nil { + if p.conn, err = createSocketConnFromReturn(net.DialTimeout( + p.addr.Network(), + p.addr.String(), + p.cfg.GetConnectTimeout(), + )); err != nil { return NewTTransportException(NOT_OPEN, err.Error()) } return nil @@ -108,10 +178,7 @@ func (p *TSocket) Conn() net.Conn { // Returns true if the connection is open func (p *TSocket) IsOpen() bool { - if p.conn == nil { - return false - } - return true + return p.conn.IsOpen() } // Closes the socket. @@ -133,16 +200,19 @@ func (p *TSocket) Addr() net.Addr { } func (p *TSocket) Read(buf []byte) (int, error) { - if !p.IsOpen() { + if !p.conn.isValid() { return 0, NewTTransportException(NOT_OPEN, "Connection not open") } p.pushDeadline(true, false) + // NOTE: Calling any of p.IsOpen, p.conn.read0, or p.conn.IsOpen between + // p.pushDeadline and p.conn.Read could cause the deadline set inside + // p.pushDeadline being reset, thus need to be avoided. n, err := p.conn.Read(buf) return n, NewTTransportExceptionFromError(err) } func (p *TSocket) Write(buf []byte) (int, error) { - if !p.IsOpen() { + if !p.conn.isValid() { return 0, NewTTransportException(NOT_OPEN, "Connection not open") } p.pushDeadline(false, true) @@ -154,7 +224,7 @@ func (p *TSocket) Flush(ctx context.Context) error { } func (p *TSocket) Interrupt() error { - if !p.IsOpen() { + if !p.conn.isValid() { return nil } return p.conn.Close() @@ -162,5 +232,7 @@ func (p *TSocket) Interrupt() error { func (p *TSocket) RemainingBytes() (num_bytes uint64) { const maxSize = ^uint64(0) - return maxSize // the thruth is, we just don't know unless framed is used + return maxSize // the truth is, we just don't know unless framed is used } + +var _ TConfigurationSetter = (*TSocket)(nil) diff --git a/lib/go/thrift/socket_conn.go b/lib/go/thrift/socket_conn.go new file mode 100644 index 00000000000..c1cc30c6cc5 --- /dev/null +++ b/lib/go/thrift/socket_conn.go @@ -0,0 +1,102 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package thrift + +import ( + "net" +) + +// socketConn is a wrapped net.Conn that tries to do connectivity check. +type socketConn struct { + net.Conn + + buffer [1]byte +} + +var _ net.Conn = (*socketConn)(nil) + +// createSocketConnFromReturn is a language sugar to help create socketConn from +// return values of functions like net.Dial, tls.Dial, net.Listener.Accept, etc. +func createSocketConnFromReturn(conn net.Conn, err error) (*socketConn, error) { + if err != nil { + return nil, err + } + return &socketConn{ + Conn: conn, + }, nil +} + +// wrapSocketConn wraps an existing net.Conn into *socketConn. +func wrapSocketConn(conn net.Conn) *socketConn { + // In case conn is already wrapped, + // return it as-is and avoid double wrapping. + if sc, ok := conn.(*socketConn); ok { + return sc + } + + return &socketConn{ + Conn: conn, + } +} + +// isValid checks whether there's a valid connection. +// +// It's nil safe, and returns false if sc itself is nil, or if the underlying +// connection is nil. +// +// It's the same as the previous implementation of TSocket.IsOpen and +// TSSLSocket.IsOpen before we added connectivity check. +func (sc *socketConn) isValid() bool { + return sc != nil && sc.Conn != nil +} + +// IsOpen checks whether the connection is open. +// +// It's nil safe, and returns false if sc itself is nil, or if the underlying +// connection is nil. +// +// Otherwise, it tries to do a connectivity check and returns the result. +// +// It also has the side effect of resetting the previously set read deadline on +// the socket. As a result, it shouldn't be called between setting read deadline +// and doing actual read. +func (sc *socketConn) IsOpen() bool { + if !sc.isValid() { + return false + } + return sc.checkConn() == nil +} + +// Read implements io.Reader. +// +// On Windows, it behaves the same as the underlying net.Conn.Read. +// +// On non-Windows, it treats len(p) == 0 as a connectivity check instead of +// readability check, which means instead of blocking until there's something to +// read (readability check), or always return (0, nil) (the default behavior of +// go's stdlib implementation on non-Windows), it never blocks, and will return +// an error if the connection is lost. +func (sc *socketConn) Read(p []byte) (n int, err error) { + if len(p) == 0 { + return 0, sc.read0() + } + + return sc.Conn.Read(p) +} diff --git a/lib/go/thrift/socket_conn_test.go b/lib/go/thrift/socket_conn_test.go new file mode 100644 index 00000000000..ab924620c78 --- /dev/null +++ b/lib/go/thrift/socket_conn_test.go @@ -0,0 +1,125 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package thrift + +import ( + "io" + "net" + "strings" + "testing" + "time" +) + +type serverSocketConnCallback func(testing.TB, *socketConn) + +func serverSocketConn(tb testing.TB, f serverSocketConnCallback) (net.Listener, error) { + tb.Helper() + + ln, err := net.Listen("tcp", "localhost:0") + if err != nil { + return nil, err + } + go func() { + for { + sc, err := createSocketConnFromReturn(ln.Accept()) + if err != nil { + // This is usually caused by Listener being + // closed, not really an error. + return + } + go f(tb, sc) + } + }() + return ln, nil +} + +func writeFully(tb testing.TB, w io.Writer, s string) bool { + tb.Helper() + + n, err := io.Copy(w, strings.NewReader(s)) + if err != nil { + tb.Errorf("Failed to write %q: %v", s, err) + return false + } + if int(n) < len(s) { + tb.Errorf("Only wrote %d out of %q", n, s) + return false + } + return true +} + +func TestSocketConn(t *testing.T) { + const ( + interval = time.Millisecond * 10 + first = "hello" + second = "world" + ) + + ln, err := serverSocketConn( + t, + func(tb testing.TB, sc *socketConn) { + defer sc.Close() + + if !writeFully(tb, sc, first) { + return + } + time.Sleep(interval) + writeFully(tb, sc, second) + }, + ) + if err != nil { + t.Fatal(err) + } + defer ln.Close() + + sc, err := createSocketConnFromReturn(net.Dial("tcp", ln.Addr().String())) + if err != nil { + t.Fatal(err) + } + buf := make([]byte, 1024) + + n, err := sc.Read(buf) + if err != nil { + t.Fatal(err) + } + read := string(buf[:n]) + if read != first { + t.Errorf("Expected read %q, got %q", first, read) + } + + n, err = sc.Read(buf) + if err != nil { + t.Fatal(err) + } + read = string(buf[:n]) + if read != second { + t.Errorf("Expected read %q, got %q", second, read) + } +} + +func TestSocketConnNilSafe(t *testing.T) { + sc := (*socketConn)(nil) + if sc.isValid() { + t.Error("Expected false for nil.isValid(), got true") + } + if sc.IsOpen() { + t.Error("Expected false for nil.IsOpen(), got true") + } +} diff --git a/lib/go/thrift/socket_unix_conn.go b/lib/go/thrift/socket_unix_conn.go new file mode 100644 index 00000000000..f5fab3ab653 --- /dev/null +++ b/lib/go/thrift/socket_unix_conn.go @@ -0,0 +1,83 @@ +// +build !windows + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package thrift + +import ( + "errors" + "io" + "syscall" + "time" +) + +// We rely on this variable to be the zero time, +// but define it as global variable to avoid repetitive allocations. +// Please DO NOT mutate this variable in any way. +var zeroTime time.Time + +func (sc *socketConn) read0() error { + return sc.checkConn() +} + +func (sc *socketConn) checkConn() error { + syscallConn, ok := sc.Conn.(syscall.Conn) + if !ok { + // No way to check, return nil + return nil + } + + // The reading about to be done here is non-blocking so we don't really + // need a read deadline. We just need to clear the previously set read + // deadline, if any. + sc.Conn.SetReadDeadline(zeroTime) + + rc, err := syscallConn.SyscallConn() + if err != nil { + return err + } + + var n int + + if readErr := rc.Read(func(fd uintptr) bool { + n, _, err = syscall.Recvfrom(int(fd), sc.buffer[:], syscall.MSG_PEEK|syscall.MSG_DONTWAIT) + return true + }); readErr != nil { + return readErr + } + + if n > 0 { + // We got something, which means we are good + return nil + } + + if errors.Is(err, syscall.EAGAIN) || errors.Is(err, syscall.EWOULDBLOCK) { + // This means the connection is still open but we don't have + // anything to read right now. + return nil + } + + if err != nil { + return err + } + + // At this point, it means the other side already closed the connection. + return io.EOF +} diff --git a/lib/go/thrift/socket_unix_conn_test.go b/lib/go/thrift/socket_unix_conn_test.go new file mode 100644 index 00000000000..1d4c806cf08 --- /dev/null +++ b/lib/go/thrift/socket_unix_conn_test.go @@ -0,0 +1,102 @@ +// +build !windows + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package thrift + +import ( + "io" + "net" + "testing" + "time" +) + +func TestSocketConnUnix(t *testing.T) { + const ( + interval = time.Millisecond * 10 + first = "hello" + second = "world" + ) + + ln, err := serverSocketConn( + t, + func(tb testing.TB, sc *socketConn) { + defer sc.Close() + + time.Sleep(interval) + if !writeFully(tb, sc, first) { + return + } + time.Sleep(interval) + writeFully(tb, sc, second) + }, + ) + if err != nil { + t.Fatal(err) + } + defer ln.Close() + + sc, err := createSocketConnFromReturn(net.Dial("tcp", ln.Addr().String())) + if err != nil { + t.Fatal(err) + } + buf := make([]byte, 1024) + + if !sc.IsOpen() { + t.Error("Expected sc to report open, got false") + } + n, err := sc.Read(buf) + if err != nil { + t.Fatal(err) + } + read := string(buf[:n]) + if read != first { + t.Errorf("Expected read %q, got %q", first, read) + } + + if !sc.IsOpen() { + t.Error("Expected sc to report open, got false") + } + // Do connection check again twice after server already wrote new data, + // make sure we don't cause any data loss with the check. + time.Sleep(interval * 10) + if !sc.IsOpen() { + t.Error("Expected sc to report open, got false") + } + if !sc.IsOpen() { + t.Error("Expected sc to report open, got false") + } + n, err = sc.Read(buf) + if err != nil { + t.Fatal(err) + } + read = string(buf[:n]) + if read != second { + t.Errorf("Expected read %q, got %q", second, read) + } + + // Now it's supposed to be closed on the server side + if err := sc.read0(); err != io.EOF { + t.Errorf("Expected to get EOF on read0, got %v", err) + } + if sc.IsOpen() { + t.Error("Expected sc to report not open, got true") + } +} diff --git a/lib/cocoa/src/TBaseClient.h b/lib/go/thrift/socket_windows_conn.go similarity index 72% rename from lib/cocoa/src/TBaseClient.h rename to lib/go/thrift/socket_windows_conn.go index 0f73aa027ca..679838c3b64 100644 --- a/lib/cocoa/src/TBaseClient.h +++ b/lib/go/thrift/socket_windows_conn.go @@ -1,3 +1,5 @@ +// +build windows + /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file @@ -17,11 +19,16 @@ * under the License. */ -#import "TProtocol.h" -#import "TApplicationError.h" - -@interface TBaseClient : NSObject +package thrift --(NSError *) checkIncomingMessageException:(id)protocol; +func (sc *socketConn) read0() error { + // On windows, we fallback to the default behavior of reading 0 bytes. + var p []byte + _, err := sc.Conn.Read(p) + return err +} -@end +func (sc *socketConn) checkConn() error { + // On windows, we always return nil for this check. + return nil +} diff --git a/lib/go/thrift/ssl_socket.go b/lib/go/thrift/ssl_socket.go index ba63377263a..6359a74ceb2 100644 --- a/lib/go/thrift/ssl_socket.go +++ b/lib/go/thrift/ssl_socket.go @@ -27,54 +27,122 @@ import ( ) type TSSLSocket struct { - conn net.Conn + conn *socketConn // hostPort contains host:port (e.g. "asdf.com:12345"). The field is // only valid if addr is nil. hostPort string // addr is nil when hostPort is not "", and is only used when the // TSSLSocket is constructed from a net.Addr. - addr net.Addr - timeout time.Duration - cfg *tls.Config + addr net.Addr + + cfg *TConfiguration } -// NewTSSLSocket creates a net.Conn-backed TTransport, given a host and port and tls Configuration +// NewTSSLSocketConf creates a net.Conn-backed TTransport, given a host and port. // // Example: -// trans, err := thrift.NewTSSLSocket("localhost:9090", nil) +// +// trans, err := thrift.NewTSSLSocketConf("localhost:9090", nil, &TConfiguration{ +// ConnectTimeout: time.Second, // Use 0 for no timeout +// SocketTimeout: time.Second, // Use 0 for no timeout +// }) +func NewTSSLSocketConf(hostPort string, conf *TConfiguration) (*TSSLSocket, error) { + if cfg := conf.GetTLSConfig(); cfg != nil && cfg.MinVersion == 0 { + cfg.MinVersion = tls.VersionTLS10 + } + return &TSSLSocket{ + hostPort: hostPort, + cfg: conf, + }, nil +} + +// Deprecated: Use NewTSSLSocketConf instead. func NewTSSLSocket(hostPort string, cfg *tls.Config) (*TSSLSocket, error) { - return NewTSSLSocketTimeout(hostPort, cfg, 0) + return NewTSSLSocketConf(hostPort, &TConfiguration{ + TLSConfig: cfg, + + noPropagation: true, + }) } -// NewTSSLSocketTimeout creates a net.Conn-backed TTransport, given a host and port -// it also accepts a tls Configuration and a timeout as a time.Duration -func NewTSSLSocketTimeout(hostPort string, cfg *tls.Config, timeout time.Duration) (*TSSLSocket, error) { - if cfg.MinVersion == 0 { - cfg.MinVersion = tls.VersionTLS10 +// Deprecated: Use NewTSSLSocketConf instead. +func NewTSSLSocketTimeout(hostPort string, cfg *tls.Config, connectTimeout, socketTimeout time.Duration) (*TSSLSocket, error) { + return NewTSSLSocketConf(hostPort, &TConfiguration{ + ConnectTimeout: connectTimeout, + SocketTimeout: socketTimeout, + TLSConfig: cfg, + + noPropagation: true, + }) +} + +// NewTSSLSocketFromAddrConf creates a TSSLSocket from a net.Addr. +func NewTSSLSocketFromAddrConf(addr net.Addr, conf *TConfiguration) *TSSLSocket { + return &TSSLSocket{ + addr: addr, + cfg: conf, + } +} + +// Deprecated: Use NewTSSLSocketFromAddrConf instead. +func NewTSSLSocketFromAddrTimeout(addr net.Addr, cfg *tls.Config, connectTimeout, socketTimeout time.Duration) *TSSLSocket { + return NewTSSLSocketFromAddrConf(addr, &TConfiguration{ + ConnectTimeout: connectTimeout, + SocketTimeout: socketTimeout, + TLSConfig: cfg, + + noPropagation: true, + }) +} + +// NewTSSLSocketFromConnConf creates a TSSLSocket from an existing net.Conn. +func NewTSSLSocketFromConnConf(conn net.Conn, conf *TConfiguration) *TSSLSocket { + return &TSSLSocket{ + conn: wrapSocketConn(conn), + addr: conn.RemoteAddr(), + cfg: conf, } - return &TSSLSocket{hostPort: hostPort, timeout: timeout, cfg: cfg}, nil } -// Creates a TSSLSocket from a net.Addr -func NewTSSLSocketFromAddrTimeout(addr net.Addr, cfg *tls.Config, timeout time.Duration) *TSSLSocket { - return &TSSLSocket{addr: addr, timeout: timeout, cfg: cfg} +// Deprecated: Use NewTSSLSocketFromConnConf instead. +func NewTSSLSocketFromConnTimeout(conn net.Conn, cfg *tls.Config, socketTimeout time.Duration) *TSSLSocket { + return NewTSSLSocketFromConnConf(conn, &TConfiguration{ + SocketTimeout: socketTimeout, + TLSConfig: cfg, + + noPropagation: true, + }) } -// Creates a TSSLSocket from an existing net.Conn -func NewTSSLSocketFromConnTimeout(conn net.Conn, cfg *tls.Config, timeout time.Duration) *TSSLSocket { - return &TSSLSocket{conn: conn, addr: conn.RemoteAddr(), timeout: timeout, cfg: cfg} +// SetTConfiguration implements TConfigurationSetter. +// +// It can be used to change connect and socket timeouts. +func (p *TSSLSocket) SetTConfiguration(conf *TConfiguration) { + p.cfg = conf +} + +// Sets the connect timeout +func (p *TSSLSocket) SetConnTimeout(timeout time.Duration) error { + if p.cfg == nil { + p.cfg = &TConfiguration{} + } + p.cfg.ConnectTimeout = timeout + return nil } // Sets the socket timeout -func (p *TSSLSocket) SetTimeout(timeout time.Duration) error { - p.timeout = timeout +func (p *TSSLSocket) SetSocketTimeout(timeout time.Duration) error { + if p.cfg == nil { + p.cfg = &TConfiguration{} + } + p.cfg.SocketTimeout = timeout return nil } func (p *TSSLSocket) pushDeadline(read, write bool) { var t time.Time - if p.timeout > 0 { - t = time.Now().Add(time.Duration(p.timeout)) + if timeout := p.cfg.GetSocketTimeout(); timeout > 0 { + t = time.Now().Add(time.Duration(timeout)) } if read && write { p.conn.SetDeadline(t) @@ -91,12 +159,18 @@ func (p *TSSLSocket) Open() error { // If we have a hostname, we need to pass the hostname to tls.Dial for // certificate hostname checks. if p.hostPort != "" { - if p.conn, err = tls.DialWithDialer(&net.Dialer{ - Timeout: p.timeout}, "tcp", p.hostPort, p.cfg); err != nil { + if p.conn, err = createSocketConnFromReturn(tls.DialWithDialer( + &net.Dialer{ + Timeout: p.cfg.GetConnectTimeout(), + }, + "tcp", + p.hostPort, + p.cfg.GetTLSConfig(), + )); err != nil { return NewTTransportException(NOT_OPEN, err.Error()) } } else { - if p.IsOpen() { + if p.conn.isValid() { return NewTTransportException(ALREADY_OPEN, "Socket already connected.") } if p.addr == nil { @@ -108,8 +182,14 @@ func (p *TSSLSocket) Open() error { if len(p.addr.String()) == 0 { return NewTTransportException(NOT_OPEN, "Cannot open bad address.") } - if p.conn, err = tls.DialWithDialer(&net.Dialer{ - Timeout: p.timeout}, p.addr.Network(), p.addr.String(), p.cfg); err != nil { + if p.conn, err = createSocketConnFromReturn(tls.DialWithDialer( + &net.Dialer{ + Timeout: p.cfg.GetConnectTimeout(), + }, + p.addr.Network(), + p.addr.String(), + p.cfg.GetTLSConfig(), + )); err != nil { return NewTTransportException(NOT_OPEN, err.Error()) } } @@ -123,10 +203,7 @@ func (p *TSSLSocket) Conn() net.Conn { // Returns true if the connection is open func (p *TSSLSocket) IsOpen() bool { - if p.conn == nil { - return false - } - return true + return p.conn.IsOpen() } // Closes the socket. @@ -143,16 +220,19 @@ func (p *TSSLSocket) Close() error { } func (p *TSSLSocket) Read(buf []byte) (int, error) { - if !p.IsOpen() { + if !p.conn.isValid() { return 0, NewTTransportException(NOT_OPEN, "Connection not open") } p.pushDeadline(true, false) + // NOTE: Calling any of p.IsOpen, p.conn.read0, or p.conn.IsOpen between + // p.pushDeadline and p.conn.Read could cause the deadline set inside + // p.pushDeadline being reset, thus need to be avoided. n, err := p.conn.Read(buf) return n, NewTTransportExceptionFromError(err) } func (p *TSSLSocket) Write(buf []byte) (int, error) { - if !p.IsOpen() { + if !p.conn.isValid() { return 0, NewTTransportException(NOT_OPEN, "Connection not open") } p.pushDeadline(false, true) @@ -164,7 +244,7 @@ func (p *TSSLSocket) Flush(ctx context.Context) error { } func (p *TSSLSocket) Interrupt() error { - if !p.IsOpen() { + if !p.conn.isValid() { return nil } return p.conn.Close() @@ -172,5 +252,7 @@ func (p *TSSLSocket) Interrupt() error { func (p *TSSLSocket) RemainingBytes() (num_bytes uint64) { const maxSize = ^uint64(0) - return maxSize // the thruth is, we just don't know unless framed is used + return maxSize // the truth is, we just don't know unless framed is used } + +var _ TConfigurationSetter = (*TSSLSocket)(nil) diff --git a/lib/go/thrift/transport_exception.go b/lib/go/thrift/transport_exception.go index 9505b44612d..0a3f07646d3 100644 --- a/lib/go/thrift/transport_exception.go +++ b/lib/go/thrift/transport_exception.go @@ -46,6 +46,13 @@ const ( type tTransportException struct { typeId int err error + msg string +} + +var _ TTransportException = (*tTransportException)(nil) + +func (tTransportException) TExceptionType() TExceptionType { + return TExceptionTypeTransport } func (p *tTransportException) TypeId() int { @@ -53,15 +60,27 @@ func (p *tTransportException) TypeId() int { } func (p *tTransportException) Error() string { - return p.err.Error() + return p.msg } func (p *tTransportException) Err() error { return p.err } +func (p *tTransportException) Unwrap() error { + return p.err +} + +func (p *tTransportException) Timeout() bool { + return p.typeId == TIMED_OUT +} + func NewTTransportException(t int, e string) TTransportException { - return &tTransportException{typeId: t, err: errors.New(e)} + return &tTransportException{ + typeId: t, + err: errors.New(e), + msg: e, + } } func NewTTransportExceptionFromError(e error) TTransportException { @@ -73,18 +92,40 @@ func NewTTransportExceptionFromError(e error) TTransportException { return t } - switch v := e.(type) { - case TTransportException: - return v - case timeoutable: - if v.Timeout() { - return &tTransportException{typeId: TIMED_OUT, err: e} - } + te := &tTransportException{ + typeId: UNKNOWN_TRANSPORT_EXCEPTION, + err: e, + msg: e.Error(), } - if e == io.EOF { - return &tTransportException{typeId: END_OF_FILE, err: e} + if isTimeoutError(e) { + te.typeId = TIMED_OUT + return te } - return &tTransportException{typeId: UNKNOWN_TRANSPORT_EXCEPTION, err: e} + if errors.Is(e, io.EOF) { + te.typeId = END_OF_FILE + return te + } + + return te +} + +func prependTTransportException(prepend string, e TTransportException) TTransportException { + return &tTransportException{ + typeId: e.TypeId(), + err: e, + msg: prepend + e.Error(), + } +} + +// isTimeoutError returns true when err is an error caused by timeout. +// +// Note that this also includes TTransportException wrapped timeout errors. +func isTimeoutError(err error) bool { + var t timeoutable + if errors.As(err, &t) { + return t.Timeout() + } + return false } diff --git a/lib/go/thrift/transport_exception_test.go b/lib/go/thrift/transport_exception_test.go index b44314f490d..0d79409e727 100644 --- a/lib/go/thrift/transport_exception_test.go +++ b/lib/go/thrift/transport_exception_test.go @@ -20,9 +20,9 @@ package thrift import ( + "errors" "fmt" "io" - "testing" ) @@ -40,21 +40,58 @@ func TestTExceptionTimeout(t *testing.T) { timeout := &timeout{true} exception := NewTTransportExceptionFromError(timeout) if timeout.Error() != exception.Error() { - t.Fatalf("Error did not match: expected %q, got %q", timeout.Error(), exception.Error()) + t.Errorf("Error did not match: expected %q, got %q", timeout.Error(), exception.Error()) } if exception.TypeId() != TIMED_OUT { - t.Fatalf("TypeId was not TIMED_OUT: expected %v, got %v", TIMED_OUT, exception.TypeId()) + t.Errorf("TypeId was not TIMED_OUT: expected %v, got %v", TIMED_OUT, exception.TypeId()) + } + + if unwrapped := errors.Unwrap(exception); unwrapped != timeout { + t.Errorf("Unwrapped exception did not match: expected %v, got %v", timeout, unwrapped) } } func TestTExceptionEOF(t *testing.T) { exception := NewTTransportExceptionFromError(io.EOF) if io.EOF.Error() != exception.Error() { - t.Fatalf("Error did not match: expected %q, got %q", io.EOF.Error(), exception.Error()) + t.Errorf("Error did not match: expected %q, got %q", io.EOF.Error(), exception.Error()) } if exception.TypeId() != END_OF_FILE { - t.Fatalf("TypeId was not END_OF_FILE: expected %v, got %v", END_OF_FILE, exception.TypeId()) + t.Errorf("TypeId was not END_OF_FILE: expected %v, got %v", END_OF_FILE, exception.TypeId()) + } + + if unwrapped := errors.Unwrap(exception); unwrapped != io.EOF { + t.Errorf("Unwrapped exception did not match: expected %v, got %v", io.EOF, unwrapped) + } +} + +func TestIsTimeoutError(t *testing.T) { + te := &timeout{true} + if !isTimeoutError(te) { + t.Error("isTimeoutError expected true, got false") + } + e := NewTTransportExceptionFromError(te) + if !isTimeoutError(e) { + t.Error("isTimeoutError on wrapped TTransportException expected true, got false") + } + + te = &timeout{false} + if isTimeoutError(te) { + t.Error("isTimeoutError expected false, got true") + } + e = NewTTransportExceptionFromError(te) + if isTimeoutError(e) { + t.Error("isTimeoutError on wrapped TTransportException expected false, got true") + } + + err := errors.New("foo") + if isTimeoutError(err) { + t.Error("isTimeoutError expected false, got true") + } + e = NewTTransportExceptionFromError(err) + if isTimeoutError(e) { + t.Error("isTimeoutError on wrapped TTransportException expected false, got true") } } diff --git a/lib/go/thrift/zlib_transport.go b/lib/go/thrift/zlib_transport.go index f3d42673af9..259943a627c 100644 --- a/lib/go/thrift/zlib_transport.go +++ b/lib/go/thrift/zlib_transport.go @@ -23,7 +23,6 @@ import ( "compress/zlib" "context" "io" - "log" ) // TZlibTransportFactory is a factory for TZlibTransport instances @@ -67,7 +66,6 @@ func NewTZlibTransportFactoryWithFactory(level int, factory TTransportFactory) * func NewTZlibTransport(trans TTransport, level int) (*TZlibTransport, error) { w, err := zlib.NewWriterLevel(trans, level) if err != nil { - log.Println(err) return nil, err } @@ -130,3 +128,10 @@ func (z *TZlibTransport) RemainingBytes() uint64 { func (z *TZlibTransport) Write(p []byte) (int, error) { return z.writer.Write(p) } + +// SetTConfiguration implements TConfigurationSetter for propagation. +func (z *TZlibTransport) SetTConfiguration(conf *TConfiguration) { + PropagateTConfiguration(z.transport, conf) +} + +var _ TConfigurationSetter = (*TZlibTransport)(nil) diff --git a/lib/haxe/README.md b/lib/haxe/README.md index 02e6919df79..c9f74b578d6 100644 --- a/lib/haxe/README.md +++ b/lib/haxe/README.md @@ -69,14 +69,12 @@ Thrift Haxe bindings Thrift Haxe bindings can be set up via the `haxelib` tool either from the official ASF repo, or via the github mirror. -- To set up any **stable version**, choose the appropriate branch (e.g. `0.10.0`): +- To set up any **stable version**, choose the appropriate branch (e.g. `0.12.0`): - - `haxelib git thrift https://git.apache.org/thrift.git 0.12.0 lib/haxe` - `haxelib git thrift https://github.com/apache/thrift.git 0.12.0 lib/haxe` - To set up the current **development version**, use the `master` branch: - - `haxelib git thrift https://git.apache.org/thrift.git master lib/haxe` - `haxelib git thrift https://github.com/apache/thrift.git master lib/haxe` As usual, the installed library can be updated using `haxelib upgrade` diff --git a/lib/haxe/haxelib.json b/lib/haxe/haxelib.json index 8d1bb11944b..d0e437ba396 100644 --- a/lib/haxe/haxelib.json +++ b/lib/haxe/haxelib.json @@ -4,7 +4,7 @@ "license": "Apache", "tags": ["thrift", "rpc", "serialization", "cross", "framework"], "description": "Haxe bindings for the Apache Thrift RPC and serialization framework", - "version": "0.12.0", + "version": "0.14.0", "releasenote": "Licensed under Apache License, Version 2.0. The Apache Thrift compiler needs to be installed separately.", "contributors": ["Apache Software Foundation (ASF)"], "dependencies": { }, diff --git a/lib/hs/CMakeLists.txt b/lib/hs/CMakeLists.txt index a20a319e277..c477c9b56ed 100644 --- a/lib/hs/CMakeLists.txt +++ b/lib/hs/CMakeLists.txt @@ -37,7 +37,7 @@ set(haskell_sources ) if(BUILD_TESTING) - list(APPEND haskell_soruces + list(APPEND haskell_sources test/Spec.hs test/BinarySpec.hs test/CompactSpec.hs @@ -60,7 +60,7 @@ endforeach() if(CMAKE_BUILD_TYPE STREQUAL "Debug") set(hs_optimize -O0) -elseif(CMAKE_BUILD_TYPE STREQUAL "Release") +else() set(hs_optimize -O1) endif() diff --git a/lib/hs/Makefile.am b/lib/hs/Makefile.am index 3cd8b57d615..ba156a13044 100644 --- a/lib/hs/Makefile.am +++ b/lib/hs/Makefile.am @@ -39,6 +39,9 @@ install-exec-hook: clean-local: $(CABAL) clean +dist-local: + $(CABAL) sdist + maintainer-clean-local: $(CABAL) clean diff --git a/lib/hs/README.md b/lib/hs/README.md index eea0a73821c..10bdeff1e77 100644 --- a/lib/hs/README.md +++ b/lib/hs/README.md @@ -97,3 +97,17 @@ Processor Just a function that takes a handler label, protocols. It calls the superclasses process if there is a superclass. + +Releasing to Hackage +==================== + +Using the [Docker Container for Ubuntu Bionic](../../build/docker/README.md), run: + + root@e941f5311545:/thrift/src# ./bootstrap.sh && ./configure + root@e941f5311545:/thrift/src# cd lib/hs && make dist-local + +This will produce a `lib/hs/dist/thrift-.tar.gz` file. Take this +file and upload it as a Haskell Hackage +[package candidate](https://hackage.haskell.org/upload#candidates) and +check to make sure all the information is correct. Assuming all is satisfactory, +you can upload the package as official using the link at the top of the page. diff --git a/lib/hs/src/Thrift/Transport/Framed.hs b/lib/hs/src/Thrift/Transport/Framed.hs index 42fc43f395b..ad553aed8cf 100644 --- a/lib/hs/src/Thrift/Transport/Framed.hs +++ b/lib/hs/src/Thrift/Transport/Framed.hs @@ -87,11 +87,11 @@ instance Transport t => Transport (FramedTransport t) where readFrame :: Transport t => FramedTransport t -> IO Int readFrame trans = do -- Read and decode the frame size. - szBs <- tRead (wrappedTrans trans) 4 + szBs <- tReadAll (wrappedTrans trans) 4 let sz = fromIntegral (B.decode szBs :: Int32) -- Read the frame and stuff it into the read buffer. - bs <- tRead (wrappedTrans trans) sz + bs <- tReadAll (wrappedTrans trans) sz fillBuf (readBuffer trans) bs -- Return the frame size so that the caller knows whether to expect diff --git a/lib/hs/src/Thrift/Transport/Handle.hs b/lib/hs/src/Thrift/Transport/Handle.hs index ff6295b6779..528a0279315 100644 --- a/lib/hs/src/Thrift/Transport/Handle.hs +++ b/lib/hs/src/Thrift/Transport/Handle.hs @@ -28,6 +28,7 @@ module Thrift.Transport.Handle ) where import Control.Exception ( catch, throw ) +import Control.Monad ( when ) import Data.ByteString.Internal (c2w) import Data.Functor @@ -50,7 +51,13 @@ instance Transport Handle where hLookAhead h LBS.hGetNonBlocking h n tReadAll _ 0 = return mempty - tReadAll h n = LBS.hGet h n `Control.Exception.catch` throwTransportExn + tReadAll h n = do + result <- LBS.hGet h n `Control.Exception.catch` throwTransportExn + let rlen = fromIntegral $ LBS.length result + when (rlen == 0) (throw $ TransportExn "Cannot read. Remote side has closed." TE_UNKNOWN) + if n <= rlen + then return result + else (result `mappend`) <$> tReadAll h (n - rlen) tPeek h = (Just . c2w <$> hLookAhead h) `Control.Exception.catch` handleEOF Nothing tWrite = LBS.hPut tFlush = hFlush diff --git a/lib/hs/thrift.cabal b/lib/hs/thrift.cabal index c136480afa9..c60effbe891 100644 --- a/lib/hs/thrift.cabal +++ b/lib/hs/thrift.cabal @@ -18,9 +18,9 @@ -- Name: thrift -Version: 0.12.0 +Version: 0.14.0 Cabal-Version: 1.24 -License: OtherLicense +License: Apache Category: Foreign Build-Type: Simple Synopsis: Haskell bindings for the Apache Thrift RPC system @@ -42,7 +42,7 @@ Library Build-Depends: base >= 4, base < 5, containers, ghc-prim, attoparsec, binary, bytestring >= 0.10, base64-bytestring, hashable, HTTP, text, hspec-core > 2.4.0, unordered-containers >= 0.2.6, vector >= 0.10.12.2, QuickCheck >= 2.8.2, split if flag(network-uri) - build-depends: network-uri >= 2.6, network >= 2.6 + build-depends: network-uri >= 2.6, network >= 2.6 && < 3.0 else build-depends: network < 2.6 Exposed-Modules: diff --git a/lib/java/CMakeLists.txt b/lib/java/CMakeLists.txt index 46064e6008a..28158c00776 100644 --- a/lib/java/CMakeLists.txt +++ b/lib/java/CMakeLists.txt @@ -18,6 +18,7 @@ # if(ANDROID) + set(THRIFT_AAR outputs/aar/thrift-debug.aar outputs/aar/thrift-release.aar) add_custom_command( OUTPUT ${THRIFT_AAR} @@ -27,12 +28,13 @@ if(ANDROID) ) add_custom_target(thrift_aar ALL DEPENDS ${THRIFT_AAR}) -else(ANDROID) - - if(IS_ABSOLUTE "${LIB_INSTALL_DIR}") - set(JAVA_INSTALL_DIR "${LIB_INSTALL_DIR}/java") - else() - set(JAVA_INSTALL_DIR "${CMAKE_INSTALL_PREFIX}/${LIB_INSTALL_DIR}/java") +else() + if(NOT JAVA_INSTALL_DIR) + if(IS_ABSOLUTE "${LIB_INSTALL_DIR}") + set(JAVA_INSTALL_DIR "${LIB_INSTALL_DIR}/java") + else() + set(JAVA_INSTALL_DIR "${CMAKE_INSTALL_PREFIX}/${LIB_INSTALL_DIR}/java") + endif() endif() if(IS_ABSOLUTE "${DOC_INSTALL_DIR}") @@ -41,11 +43,16 @@ else(ANDROID) set(JAVA_DOC_INSTALL_DIR "${CMAKE_INSTALL_PREFIX}/${DOC_INSTALL_DIR}/java") endif() + set(PRELEASE "true") + if (CMAKE_BUILD_TYPE MATCHES DEBUG) + set(PRELEASE "false") + endif () + add_custom_target(ThriftJava ALL COMMENT "Building Java library using Gradle Wrapper" COMMAND ${GRADLEW_EXECUTABLE} ${GRADLE_OPTS} assemble --console=plain --no-daemon - -Prelease=true + -Prelease=${PRELEASE} -Pthrift.version=${thrift_VERSION} "-Pbuild.dir=${CMAKE_CURRENT_BINARY_DIR}/build" WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} @@ -56,7 +63,7 @@ else(ANDROID) COMMENT "Publishing Java Library to Apache Maven staging" COMMAND ${GRADLEW_EXECUTABLE} ${GRADLE_OPTS} clean uploadArchives --console=plain --no-daemon - -Prelease=true + -Prelease=${PRELEASE} -Pthrift.version=${thrift_VERSION} "-Pbuild.dir=${CMAKE_CURRENT_BINARY_DIR}/build" WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} @@ -78,10 +85,11 @@ else(ANDROID) add_test(NAME JavaTest COMMAND ${GRADLEW_EXECUTABLE} ${GRADLE_OPTS} test --console=plain --no-daemon - -Prelease=true + -Prelease=${PRELEASE} -Pthrift.version=${thrift_VERSION} "-Pbuild.dir=${CMAKE_CURRENT_BINARY_DIR}/build" "-Pthrift.compiler=${THRIFT_COMPILER}" WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}) endif() -endif(ANDROID) + +endif() diff --git a/lib/java/README.md b/lib/java/README.md index 1e4aed22a23..c8ec1c37c62 100644 --- a/lib/java/README.md +++ b/lib/java/README.md @@ -138,11 +138,15 @@ properties to it. mavenUser=meMyselfAndI mavenPassword=MySuperAwesomeSecretPassword +NOTE: If you do not have a secring.gpg file, see the +[gradle signing docs](https://docs.gradle.org/current/userguide/signing_plugin.html) +for instructions on how to generate it. + It is also possible to manually publish using the Gradle build directly. With the key information and credentials in place the following will generate if needed the build artifacts and proceed to publish the results. - ./gradlew -Prelease=true -Pthrift.version=0.11.0 uploadArchives + ./gradlew -Prelease=true uploadArchives It is also possible to override the target repository for the Maven Publication by using a Gradle property, for example you can publish signed JAR files to your @@ -161,3 +165,24 @@ Dependencies Gradle http://gradle.org/ + +# Breaking Changes + +## 0.13.0 + +* The signature of the 'process' method in TAsyncProcessor and TProcessor has +changed to remove the boolean return type and instead rely on Exceptions. + +* Per THRIFT-4805, TSaslTransportException has been removed. The same condition +is now covered by TTansportException, where `TTransportException.getType() == END_OF_FILE`. + +## 0.12.0 + +The access modifier of the AutoExpandingBuffer class has been changed from +public to default (package) and will no longer be accessible by third-party +libraries. + +The access modifier of the ShortStack class has been changed from +public to default (package) and will no longer be accessible by third-party +libraries. + diff --git a/lib/java/build.gradle b/lib/java/build.gradle index 4302f779db4..57a8e1e26f5 100644 --- a/lib/java/build.gradle +++ b/lib/java/build.gradle @@ -19,14 +19,20 @@ // Using the legacy plugin classpath for Clover so it can be loaded optionally buildscript { + // strictly enforce the minimum version of Java required to build and fail fast + if (JavaVersion.current() < JavaVersion.VERSION_1_8) { + throw new GradleException("The java version used is ${JavaVersion.current()}, but must be at least ${JavaVersion.VERSION_1_8}") + } + repositories { + mavenCentral() google() jcenter() gradlePluginPortal() } dependencies { - classpath 'com.bmuschko:gradle-clover-plugin:2.2.0' + classpath 'com.bmuschko:gradle-clover-plugin:2.2.1' } } @@ -34,7 +40,13 @@ plugins { id 'java' id 'maven' id 'signing' - id 'com.github.johnrengelman.shadow' version '2.0.2' + id 'com.github.johnrengelman.shadow' version '4.0.4' + id "com.github.spotbugs" version "2.0.0" +} + +ext { + // https://github.com/spotbugs/spotbugs-gradle-plugin/issues/32#issuecomment-409951172 + SpotBugsTask = com.github.spotbugs.SpotBugsTask } description = 'Apache Thrift Java Library' @@ -44,12 +56,10 @@ defaultTasks 'build' // Version components for this project group = property('thrift.groupid') -// Drop the -dev suffix, we use the SNAPSHOT suffix for non-release versions -def parsedVersion = property('thrift.version').toString().replace('-dev', '') if (Boolean.parseBoolean(project.release)) { - version = parsedVersion + version = property('thrift.version') } else { - version = parsedVersion + '-SNAPSHOT' + version = property('thrift.version') + '-SNAPSHOT' } // Keeping the rest of the build logic in functional named scripts for clarity diff --git a/lib/java/gradle.properties b/lib/java/gradle.properties index 4955b4cdb3e..8a7acb10dc7 100644 --- a/lib/java/gradle.properties +++ b/lib/java/gradle.properties @@ -1,9 +1,9 @@ # This file is shared currently between this Gradle build and the # Ant builds for fd303 and JavaScript. Keep the dotted notation for # the properties to minimize the changes in the dependencies. -thrift.version=0.12.0 +thrift.version=0.14.0 thrift.groupid=org.apache.thrift -release=true +release=false # Local Install paths install.path=/usr/local/lib @@ -16,18 +16,21 @@ testPort=9090 cloverEnabled=false # Maven dependency download locations -mvn.repo=http://repo1.maven.org/maven2 +mvn.repo=https://repo1.maven.org/maven2 apache.repo=https://repository.apache.org/content/repositories/releases # Apache Maven publish -license=http://www.apache.org/licenses/LICENSE-2.0.txt +license=https://www.apache.org/licenses/LICENSE-2.0.txt maven-repository-url=https://repository.apache.org/service/local/staging/deploy/maven2 maven-repository-id=apache.releases.https # Dependency versions -httpclient.version=4.4.1 -httpcore.version=4.4.1 -slf4j.version=1.7.12 -servlet.version=2.5 +httpclient.version=4.5.10 +httpcore.version=4.4.12 +slf4j.version=1.7.28 +#servlet.version=2.5 +#It contains servlet3 +tomcat.embed.version=8.5.46 junit.version=4.12 -mockito.version=1.9.5 +mockito.version=1.10.19 +javax.annotation.version=1.3.2 diff --git a/lib/java/gradle/codeQualityChecks.gradle b/lib/java/gradle/codeQualityChecks.gradle index 9572ca1779f..b0662875900 100644 --- a/lib/java/gradle/codeQualityChecks.gradle +++ b/lib/java/gradle/codeQualityChecks.gradle @@ -3,18 +3,23 @@ // Configure the Gradle code quality plugins here. // -apply plugin: 'findbugs' +dependencies { + spotbugs configurations.spotbugsPlugins.dependencies + spotbugsPlugins 'com.h3xstream.findsecbugs:findsecbugs-plugin:1.8.0' +} + +apply plugin: 'com.github.spotbugs' -findbugs { +spotbugs{ ignoreFailures = true - toolVersion = '3.0.1' + toolVersion = '3.1.12' sourceSets = [ sourceSets.main ] effort = 'max' reportLevel = 'low' excludeFilter = file('code_quality_tools/findbugs-filter.xml') } -tasks.withType(FindBugs) { +tasks.withType(SpotBugsTask) { reports { text.enabled = false html.enabled = true @@ -28,7 +33,6 @@ pmd { ignoreFailures = true toolVersion = '6.0.0' sourceSets = [ sourceSets.main ] - targetJdk = sourceCompatibility ruleSets = [ 'java-basic' ] } diff --git a/lib/java/gradle/environment.gradle b/lib/java/gradle/environment.gradle index 9b7eb1ed625..965a9088177 100644 --- a/lib/java/gradle/environment.gradle +++ b/lib/java/gradle/environment.gradle @@ -44,10 +44,12 @@ ext.mavenRepositoryUrl = property('maven-repository-url') // Versions used in this project ext.httpclientVersion = property('httpclient.version') ext.httpcoreVersion = property('httpcore.version') -ext.servletVersion = property('servlet.version') +//ext.servletVersion = property('servlet.version') +ext.tomcatEmbedVersion = property('tomcat.embed.version') ext.slf4jVersion = property('slf4j.version') ext.junitVersion = property('junit.version') ext.mockitoVersion = property('mockito.version') +ext.javaxAnnotationVersion = property('javax.annotation.version') // In this section you declare where to find the dependencies of your project repositories { @@ -65,7 +67,9 @@ dependencies { compile "org.slf4j:slf4j-api:${slf4jVersion}" compile "org.apache.httpcomponents:httpclient:${httpclientVersion}" compile "org.apache.httpcomponents:httpcore:${httpcoreVersion}" - compile "javax.servlet:servlet-api:${servletVersion}" + //compile "javax.servlet:servlet-api:${servletVersion}" + compile "org.apache.tomcat.embed:tomcat-embed-core:${tomcatEmbedVersion}" + compile "javax.annotation:javax.annotation-api:${javaxAnnotationVersion}" testCompile "junit:junit:${junitVersion}" testCompile "org.mockito:mockito-all:${mockitoVersion}" diff --git a/lib/java/gradle/functionalTests.gradle b/lib/java/gradle/functionalTests.gradle index c420d122c44..6a388a697de 100644 --- a/lib/java/gradle/functionalTests.gradle +++ b/lib/java/gradle/functionalTests.gradle @@ -34,6 +34,7 @@ sourceSets { include '**/test/TestClient.java' include '**/test/TestServer.java' include '**/test/TestNonblockingServer.java' + include '**/test/TestTServletServer.java' } } } @@ -55,7 +56,7 @@ dependencies { shadowJar { description = 'Assemble a test JAR file for cross-check execution' // make sure the runners are created when this runs - dependsOn 'generateRunnerScriptForClient', 'generateRunnerScriptForServer', 'generateRunnerScriptForNonblockingServer' + dependsOn 'generateRunnerScriptForClient', 'generateRunnerScriptForServer', 'generateRunnerScriptForNonblockingServer', 'generateRunnerScriptForTServletServer' baseName = 'functionalTest' destinationDir = file("$buildDir/functionalTestJar") @@ -153,3 +154,24 @@ ${scriptHead} serverFile.setExecutable(true, false) } } + +task generateRunnerScriptForTServletServer(group: 'Build') { + description = 'Generate a runner script for cross-check tests with TestTServletServer' + + def serverFile = file("$buildDir/runservletserver${scriptExt}") + + def runServerText = """\ +${scriptHead} + +"${javaExe}" -cp "$jarPath" "-Djavax.net.ssl.keyStore=$keyStore" -Djavax.net.ssl.keyStorePassword=thrift org.apache.thrift.test.TestTServletServer $args +""" + + inputs.property 'runServerText', runServerText + outputs.file serverFile + + doLast { + serverFile.parentFile.mkdirs() + serverFile.text = runServerText + serverFile.setExecutable(true, false) + } +} diff --git a/lib/java/gradle/generateTestThrift.gradle b/lib/java/gradle/generateTestThrift.gradle index 2b53739979e..121bf537d8f 100644 --- a/lib/java/gradle/generateTestThrift.gradle +++ b/lib/java/gradle/generateTestThrift.gradle @@ -80,6 +80,7 @@ task generateJava(group: 'Build') { thriftCompile(it, 'ManyOptionals.thrift') thriftCompile(it, 'JavaDeepCopyTest.thrift') thriftCompile(it, 'EnumContainersTest.thrift') + thriftCompile(it, 'JavaBinaryDefault.thrift') } task generateBeanJava(group: 'Build') { diff --git a/lib/java/gradle/publishing.gradle b/lib/java/gradle/publishing.gradle index 961d58f7820..029bff93d77 100644 --- a/lib/java/gradle/publishing.gradle +++ b/lib/java/gradle/publishing.gradle @@ -61,9 +61,9 @@ def configurePom(pom) { url 'http://thrift.apache.org' scm { - url 'https://git-wip-us.apache.org/repos/asf?p=thrift.git' - connection 'scm:git:https://git-wip-us.apache.org/repos/asf/thrift.git' - developerConnection 'scm:git:https://git-wip-us.apache.org/repos/asf/thrift.git' + url 'https://github.com/apache/thrift' + connection 'scm:git:https://github.com/apache/thrift.git' + developerConnection 'scm:git:git@github.com:apache/thrift.git' } licenses { diff --git a/lib/java/gradle/sourceConfiguration.gradle b/lib/java/gradle/sourceConfiguration.gradle index decc6a27597..d15c1179f30 100644 --- a/lib/java/gradle/sourceConfiguration.gradle +++ b/lib/java/gradle/sourceConfiguration.gradle @@ -34,6 +34,7 @@ sourceSets { exclude '**/test/TestClient.java' exclude '**/test/TestServer.java' exclude '**/test/TestNonblockingServer.java' + exclude '**/test/TestTServletServer.java' } resources { srcDir 'test' @@ -45,13 +46,20 @@ sourceSets { // ---------------------------------------------------------------------------- // Compiler configuration details -sourceCompatibility = '1.6' -targetCompatibility = '1.6' +// These two properties are still needed on JDK8, and possibly used directly by +// plugins. However, the '--release' option added below makes these two +// properties redundant when building with JDK9 or later. +sourceCompatibility = '1.8' +targetCompatibility = '1.8' tasks.withType(JavaCompile) { options.encoding = 'UTF-8' options.debug = true options.deprecation = true + // the following is to build with Java 8 specifications, even when building with JDK9 or later + if (JavaVersion.current() > JavaVersion.VERSION_1_8) { + options.compilerArgs.addAll(['--release', '8']) + } // options.compilerArgs.addAll('-Xlint:unchecked') } @@ -70,6 +78,7 @@ jar { manifest { attributes([ "Implementation-Version": "${project.version}", + "Automatic-Module-Name": "${project.group}", "Bundle-ManifestVersion": "2", "Bundle-SymbolicName": "${project.group}", "Bundle-Name": "Apache Thrift", diff --git a/lib/java/gradle/wrapper/gradle-wrapper.jar b/lib/java/gradle/wrapper/gradle-wrapper.jar index 99340b4ad18..5c2d1cf016b 100644 Binary files a/lib/java/gradle/wrapper/gradle-wrapper.jar and b/lib/java/gradle/wrapper/gradle-wrapper.jar differ diff --git a/lib/java/gradle/wrapper/gradle-wrapper.properties b/lib/java/gradle/wrapper/gradle-wrapper.properties index 2c2bbe5f9a8..5028f28f8e4 100644 --- a/lib/java/gradle/wrapper/gradle-wrapper.properties +++ b/lib/java/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.4-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-4.4.1-bin.zip diff --git a/lib/java/gradlew b/lib/java/gradlew index cccdd3d517f..83f2acfdc31 100755 --- a/lib/java/gradlew +++ b/lib/java/gradlew @@ -1,5 +1,21 @@ #!/usr/bin/env sh +# +# Copyright 2015 the original author or authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + ############################################################################## ## ## Gradle start up script for UN*X @@ -28,7 +44,7 @@ APP_NAME="Gradle" APP_BASE_NAME=`basename "$0"` # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -DEFAULT_JVM_OPTS="" +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD="maximum" @@ -109,8 +125,8 @@ if $darwin; then GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" fi -# For Cygwin, switch paths to Windows format before running java -if $cygwin ; then +# For Cygwin or MSYS, switch paths to Windows format before running java +if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then APP_HOME=`cygpath --path --mixed "$APP_HOME"` CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` JAVACMD=`cygpath --unix "$JAVACMD"` diff --git a/lib/java/gradlew.bat b/lib/java/gradlew.bat index f9553162f12..9618d8d9607 100644 --- a/lib/java/gradlew.bat +++ b/lib/java/gradlew.bat @@ -1,3 +1,19 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + @if "%DEBUG%" == "" @echo off @rem ########################################################################## @rem @@ -14,7 +30,7 @@ set APP_BASE_NAME=%~n0 set APP_HOME=%DIRNAME% @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -set DEFAULT_JVM_OPTS= +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" @rem Find java.exe if defined JAVA_HOME goto findJavaFromJavaHome diff --git a/lib/java/src/org/apache/thrift/Option.java b/lib/java/src/org/apache/thrift/Option.java index db25ec51314..d5cd309e89a 100644 --- a/lib/java/src/org/apache/thrift/Option.java +++ b/lib/java/src/org/apache/thrift/Option.java @@ -24,6 +24,9 @@ */ public abstract class Option { + @SuppressWarnings("rawtypes") + private static final Option NONE = new None(); + /** * Whether the Option is defined or not * @return @@ -87,7 +90,7 @@ public T get() { } public String toString() { - return "Some("+value.toString()+")"; + return "Some(" + value + ")"; } } @@ -99,9 +102,9 @@ public String toString() { */ public static Option fromNullable(T value) { if (value != null) { - return new Some(value); + return some(value); } else { - return new None(); + return none(); } } @@ -115,7 +118,8 @@ public static Some some(T value) { return new Some(value); } + @SuppressWarnings("unchecked") public static None none() { - return new None(); + return (None) NONE; } } \ No newline at end of file diff --git a/lib/java/src/org/apache/thrift/TAsyncProcessor.java b/lib/java/src/org/apache/thrift/TAsyncProcessor.java index 533e74d86c7..5e287d5c911 100644 --- a/lib/java/src/org/apache/thrift/TAsyncProcessor.java +++ b/lib/java/src/org/apache/thrift/TAsyncProcessor.java @@ -21,8 +21,13 @@ import org.apache.thrift.server.AbstractNonblockingServer.AsyncFrameBuffer; public interface TAsyncProcessor { - /** - * Implementations must call fb.responseReady() once processing is complete - */ - public boolean process(final AsyncFrameBuffer fb) throws TException; + /** + * Process a single frame. + + * Note: Implementations must call fb.responseReady() once processing + * is complete + * + * @throws TException if the frame cannot be processed + */ + public void process(final AsyncFrameBuffer fb) throws TException; } diff --git a/lib/java/src/org/apache/thrift/TBaseAsyncProcessor.java b/lib/java/src/org/apache/thrift/TBaseAsyncProcessor.java index 9459c1ae418..f13f068ef46 100644 --- a/lib/java/src/org/apache/thrift/TBaseAsyncProcessor.java +++ b/lib/java/src/org/apache/thrift/TBaseAsyncProcessor.java @@ -43,7 +43,7 @@ public TBaseAsyncProcessor(I iface, Map>> 32); - return high * 127 + low; + return Long.hashCode(value); } public static int hashCode(double value) { - return hashCode(Double.doubleToRawLongBits(value)); + return Double.hashCode(value); } } diff --git a/lib/java/src/org/apache/thrift/TBaseProcessor.java b/lib/java/src/org/apache/thrift/TBaseProcessor.java index f9a9a9e371e..55a0f15d34f 100644 --- a/lib/java/src/org/apache/thrift/TBaseProcessor.java +++ b/lib/java/src/org/apache/thrift/TBaseProcessor.java @@ -23,7 +23,7 @@ protected TBaseProcessor(I iface, Map * * - * @throws TException If the message type is not CALL or ONEWAY, if + * @throws TProtocolException If the message type is not CALL or ONEWAY, if * the service name was not found in the message, or if the service * name was not found in the service map. You called {@link #registerProcessor(String, TProcessor) registerProcessor} * during initialization, right? :) */ - public boolean process(TProtocol iprot, TProtocol oprot) throws TException { + public void process(TProtocol iprot, TProtocol oprot) throws TException { /* Use the actual underlying protocol (e.g. TBinaryProtocol) to read the message header. This pulls the message "off the wire", which we'll @@ -101,7 +101,8 @@ Use the actual underlying protocol (e.g. TBinaryProtocol) to read the TMessage message = iprot.readMessageBegin(); if (message.type != TMessageType.CALL && message.type != TMessageType.ONEWAY) { - throw new TException("This should not have happened!?"); + throw new TProtocolException(TProtocolException.NOT_IMPLEMENTED, + "This should not have happened!?"); } // Extract the service name @@ -109,18 +110,21 @@ Use the actual underlying protocol (e.g. TBinaryProtocol) to read the if (index < 0) { if (defaultProcessor != null) { // Dispatch processing to the stored processor - return defaultProcessor.process(new StoredMessageProtocol(iprot, message), oprot); + defaultProcessor.process(new StoredMessageProtocol(iprot, message), oprot); + return; } - throw new TException("Service name not found in message name: " + message.name + ". Did you " + - "forget to use a TMultiplexProtocol in your client?"); + throw new TProtocolException(TProtocolException.NOT_IMPLEMENTED, + "Service name not found in message name: " + message.name + ". Did you " + + "forget to use a TMultiplexProtocol in your client?"); } // Create a new TMessage, something that can be consumed by any TProtocol String serviceName = message.name.substring(0, index); TProcessor actualProcessor = SERVICE_PROCESSOR_MAP.get(serviceName); if (actualProcessor == null) { - throw new TException("Service name not found: " + serviceName + ". Did you forget " + - "to call registerProcessor()?"); + throw new TProtocolException(TProtocolException.NOT_IMPLEMENTED, + "Service name not found: " + serviceName + ". Did you forget " + + "to call registerProcessor()?"); } // Create a new TMessage, removing the service name @@ -131,7 +135,7 @@ Use the actual underlying protocol (e.g. TBinaryProtocol) to read the ); // Dispatch processing to the stored processor - return actualProcessor.process(new StoredMessageProtocol(iprot, standardMessage), oprot); + actualProcessor.process(new StoredMessageProtocol(iprot, standardMessage), oprot); } /** diff --git a/lib/java/src/org/apache/thrift/TNonblockingMultiFetchClient.java b/lib/java/src/org/apache/thrift/TNonblockingMultiFetchClient.java index 382d978db44..78f3a571b75 100644 --- a/lib/java/src/org/apache/thrift/TNonblockingMultiFetchClient.java +++ b/lib/java/src/org/apache/thrift/TNonblockingMultiFetchClient.java @@ -75,10 +75,9 @@ * */ public class TNonblockingMultiFetchClient { - + private static final Logger LOGGER = LoggerFactory.getLogger( - TNonblockingMultiFetchClient.class.getName() - ); + TNonblockingMultiFetchClient.class); // if the size of the response msg exceeds this limit (in byte), we will // not read the msg @@ -87,7 +86,7 @@ public class TNonblockingMultiFetchClient { // time limit for fetching data from all servers (in second) private int fetchTimeoutSeconds; - // store request that will be sent to servers + // store request that will be sent to servers private ByteBuffer requestBuf; private ByteBuffer requestBufDuplication; @@ -105,7 +104,7 @@ public TNonblockingMultiFetchClient(int maxRecvBufBytesPerServer, this.fetchTimeoutSeconds = fetchTimeoutSeconds; this.requestBuf = requestBuf; this.servers = servers; - + stats = new TNonblockingMultiFetchStats(); recvBuf = null; } @@ -129,7 +128,7 @@ public synchronized ByteBuffer getRequestBuf() { if (requestBufDuplication == null) { requestBufDuplication = requestBuf.duplicate(); } - return requestBufDuplication; + return requestBufDuplication; } } @@ -163,18 +162,18 @@ public synchronized ByteBuffer[] fetch() { executor.execute(task); try { task.get(fetchTimeoutSeconds, TimeUnit.SECONDS); - } catch(InterruptedException ie) { + } catch (InterruptedException ie) { // attempt to cancel execution of the task. task.cancel(true); - LOGGER.error("interrupted during fetch: "+ie.toString()); - } catch(ExecutionException ee) { + LOGGER.error("Interrupted during fetch", ie); + } catch (ExecutionException ee) { // attempt to cancel execution of the task. task.cancel(true); - LOGGER.error("exception during fetch: "+ee.toString()); - } catch(TimeoutException te) { - // attempt to cancel execution of the task. + LOGGER.error("Exception during fetch", ee); + } catch (TimeoutException te) { + // attempt to cancel execution of the task. task.cancel(true); - LOGGER.error("timeout for fetch: "+te.toString()); + LOGGER.error("Timeout for fetch", te); } executor.shutdownNow(); @@ -208,15 +207,15 @@ public void run() { // buffer for receiving response from servers recvBuf = new ByteBuffer[numTotalServers]; // buffer for sending request - ByteBuffer sendBuf[] = new ByteBuffer[numTotalServers]; - long numBytesRead[] = new long[numTotalServers]; - int frameSize[] = new int[numTotalServers]; - boolean hasReadFrameSize[] = new boolean[numTotalServers]; + ByteBuffer[] sendBuf = new ByteBuffer[numTotalServers]; + long[] numBytesRead = new long[numTotalServers]; + int[] frameSize = new int[numTotalServers]; + boolean[] hasReadFrameSize = new boolean[numTotalServers]; try { selector = Selector.open(); - } catch (IOException e) { - LOGGER.error("selector opens error: "+e.toString()); + } catch (IOException ioe) { + LOGGER.error("Selector opens error", ioe); return; } @@ -239,14 +238,13 @@ public void run() { // attach index of the key key.attach(i); } catch (Exception e) { - stats.incNumConnectErrorServers(); - String err = String.format("set up socket to server %s error: %s", - server.toString(), e.toString()); - LOGGER.error(err); + stats.incNumConnectErrorServers(); + LOGGER.error("Set up socket to server {} error", server, e); + // free resource if (s != null) { try {s.close();} catch (Exception ex) {} - } + } if (key != null) { key.cancel(); } @@ -256,7 +254,7 @@ public void run() { // wait for events while (stats.getNumReadCompletedServers() + stats.getNumConnectErrorServers() < stats.getNumTotalServers()) { - // if the thread is interrupted (e.g., task is cancelled) + // if the thread is interrupted (e.g., task is cancelled) if (Thread.currentThread().isInterrupted()) { return; } @@ -264,7 +262,7 @@ public void run() { try{ selector.select(); } catch (Exception e) { - LOGGER.error("selector selects error: "+e.toString()); + LOGGER.error("Selector selects error", e); continue; } @@ -284,10 +282,7 @@ public void run() { sChannel.finishConnect(); } catch (Exception e) { stats.incNumConnectErrorServers(); - String err = String.format("socket %d connects to server %s " + - "error: %s", - index, servers.get(index).toString(), e.toString()); - LOGGER.error(err); + LOGGER.error("Socket {} connects to server {} error", index, servers.get(index), e); } } @@ -299,10 +294,7 @@ public void run() { SocketChannel sChannel = (SocketChannel)selKey.channel(); sChannel.write(sendBuf[index]); } catch (Exception e) { - String err = String.format("socket %d writes to server %s " + - "error: %s", - index, servers.get(index).toString(), e.toString()); - LOGGER.error(err); + LOGGER.error("Socket {} writes to server {} error", index, servers.get(index), e); } } } @@ -325,10 +317,8 @@ public void run() { if (frameSize[index] <= 0) { stats.incNumInvalidFrameSize(); - String err = String.format("Read an invalid frame size %d" - + " from %s. Does the server use TFramedTransport? ", - frameSize[index], servers.get(index).toString()); - LOGGER.error(err); + LOGGER.error("Read an invalid frame size {} from {}. Does the server use TFramedTransport?", + frameSize[index], servers.get(index)); sChannel.close(); continue; } @@ -339,11 +329,8 @@ public void run() { if (frameSize[index] + 4 > maxRecvBufBytesPerServer) { stats.incNumOverflowedRecvBuf(); - String err = String.format("Read frame size %d from %s," - + " total buffer size would exceed limit %d", - frameSize[index], servers.get(index).toString(), - maxRecvBufBytesPerServer); - LOGGER.error(err); + LOGGER.error("Read frame size {} from {}, total buffer size would exceed limit {}", + frameSize[index], servers.get(index), maxRecvBufBytesPerServer); sChannel.close(); continue; } @@ -366,10 +353,8 @@ public void run() { } } } catch (Exception e) { - String err = String.format("socket %d reads from server %s " + - "error: %s", - index, servers.get(index).toString(), e.toString()); - LOGGER.error(err); + LOGGER.error("Socket {} reads from server {} error", + index, servers.get(index), e); } } } @@ -392,8 +377,8 @@ public void close() { selector.close(); } } catch (IOException e) { - LOGGER.error("free resource error: "+e.toString()); + LOGGER.error("Free resource error", e); } } } -} \ No newline at end of file +} diff --git a/lib/java/src/org/apache/thrift/TProcessor.java b/lib/java/src/org/apache/thrift/TProcessor.java index d79522c3e51..15ba9c0fe72 100644 --- a/lib/java/src/org/apache/thrift/TProcessor.java +++ b/lib/java/src/org/apache/thrift/TProcessor.java @@ -24,9 +24,7 @@ /** * A processor is a generic object which operates upon an input stream and * writes to some output stream. - * */ public interface TProcessor { - public boolean process(TProtocol in, TProtocol out) - throws TException; + public void process(TProtocol in, TProtocol out) throws TException; } diff --git a/lib/java/src/org/apache/thrift/TSerializer.java b/lib/java/src/org/apache/thrift/TSerializer.java index 4e1ce612930..90cc0398f9e 100644 --- a/lib/java/src/org/apache/thrift/TSerializer.java +++ b/lib/java/src/org/apache/thrift/TSerializer.java @@ -20,12 +20,12 @@ package org.apache.thrift; import java.io.ByteArrayOutputStream; -import java.io.UnsupportedEncodingException; import org.apache.thrift.protocol.TBinaryProtocol; import org.apache.thrift.protocol.TProtocol; import org.apache.thrift.protocol.TProtocolFactory; import org.apache.thrift.transport.TIOStreamTransport; +import org.apache.thrift.transport.TTransportException; /** * Generic utility for easily serializing objects into a byte array or Java @@ -42,7 +42,7 @@ public class TSerializer { /** * This transport wraps that byte array */ - private final TIOStreamTransport transport_ = new TIOStreamTransport(baos_); + private final TIOStreamTransport transport_; /** * Internal protocol used for serializing objects. @@ -52,7 +52,7 @@ public class TSerializer { /** * Create a new TSerializer that uses the TBinaryProtocol by default. */ - public TSerializer() { + public TSerializer() throws TTransportException { this(new TBinaryProtocol.Factory()); } @@ -62,7 +62,8 @@ public TSerializer() { * * @param protocolFactory Factory to create a protocol */ - public TSerializer(TProtocolFactory protocolFactory) { + public TSerializer(TProtocolFactory protocolFactory) throws TTransportException { + transport_ = new TIOStreamTransport(new TConfiguration(), baos_); protocol_ = protocolFactory.getProtocol(transport_); } @@ -80,22 +81,6 @@ public byte[] serialize(TBase base) throws TException { return baos_.toByteArray(); } - /** - * Serialize the Thrift object into a Java string, using a specified - * character set for encoding. - * - * @param base The object to serialize - * @param charset Valid JVM charset - * @return Serialized object as a String - */ - public String toString(TBase base, String charset) throws TException { - try { - return new String(serialize(base), charset); - } catch (UnsupportedEncodingException uex) { - throw new TException("JVM DOES NOT SUPPORT ENCODING: " + charset); - } - } - /** * Serialize the Thrift object into a Java string, using the default JVM * charset encoding. diff --git a/lib/java/src/org/apache/thrift/TUnion.java b/lib/java/src/org/apache/thrift/TUnion.java index 13f9c6774ef..1ef11df49a9 100644 --- a/lib/java/src/org/apache/thrift/TUnion.java +++ b/lib/java/src/org/apache/thrift/TUnion.java @@ -79,7 +79,7 @@ private static Object deepCopyObject(Object o) { } private static Map deepCopyMap(Map map) { - Map copy = new HashMap(); + Map copy = new HashMap(map.size()); for (Map.Entry entry : map.entrySet()) { copy.put(deepCopyObject(entry.getKey()), deepCopyObject(entry.getValue())); } @@ -87,7 +87,7 @@ private static Map deepCopyMap(Map map) { } private static Set deepCopySet(Set set) { - Set copy = new HashSet(); + Set copy = new HashSet(set.size()); for (Object o : set) { copy.add(deepCopyObject(o)); } diff --git a/lib/java/src/org/apache/thrift/async/TAsyncMethodCall.java b/lib/java/src/org/apache/thrift/async/TAsyncMethodCall.java index 3bf1747f4bd..a119f23e0bc 100644 --- a/lib/java/src/org/apache/thrift/async/TAsyncMethodCall.java +++ b/lib/java/src/org/apache/thrift/async/TAsyncMethodCall.java @@ -27,7 +27,7 @@ import org.apache.thrift.TException; import org.apache.thrift.protocol.TProtocol; import org.apache.thrift.protocol.TProtocolFactory; -import org.apache.thrift.transport.TFramedTransport; +import org.apache.thrift.transport.layered.TFramedTransport; import org.apache.thrift.transport.TMemoryBuffer; import org.apache.thrift.transport.TNonblockingTransport; import org.apache.thrift.transport.TTransportException; @@ -98,7 +98,7 @@ protected boolean isFinished() { protected long getStartTime() { return startTime; } - + protected long getSequenceId() { return sequenceId; } @@ -106,11 +106,11 @@ protected long getSequenceId() { public TAsyncClient getClient() { return client; } - + public boolean hasTimeout() { return timeout > 0; } - + public long getTimeoutTimestamp() { return timeout + startTime; } @@ -217,9 +217,9 @@ protected void onError(Exception e) { state = State.ERROR; } - private void doReadingResponseBody(SelectionKey key) throws IOException { + private void doReadingResponseBody(SelectionKey key) throws TTransportException { if (transport.read(frameBuffer) < 0) { - throw new IOException("Read call frame failed"); + throw new TTransportException(TTransportException.END_OF_FILE, "Read call frame failed"); } if (frameBuffer.remaining() == 0) { cleanUpAndFireCallback(key); @@ -241,9 +241,9 @@ private void cleanUpAndFireCallback(SelectionKey key) { } } - private void doReadingResponseSize() throws IOException { + private void doReadingResponseSize() throws TTransportException { if (transport.read(sizeBuffer) < 0) { - throw new IOException("Read call frame size failed"); + throw new TTransportException(TTransportException.END_OF_FILE, "Read call frame size failed"); } if (sizeBuffer.remaining() == 0) { state = State.READING_RESPONSE_BODY; @@ -251,9 +251,9 @@ private void doReadingResponseSize() throws IOException { } } - private void doWritingRequestBody(SelectionKey key) throws IOException { + private void doWritingRequestBody(SelectionKey key) throws TTransportException { if (transport.write(frameBuffer) < 0) { - throw new IOException("Write call frame failed"); + throw new TTransportException(TTransportException.END_OF_FILE, "Write call frame failed"); } if (frameBuffer.remaining() == 0) { if (isOneway) { @@ -266,9 +266,9 @@ private void doWritingRequestBody(SelectionKey key) throws IOException { } } - private void doWritingRequestSize() throws IOException { + private void doWritingRequestSize() throws TTransportException { if (transport.write(sizeBuffer) < 0) { - throw new IOException("Write call frame size failed"); + throw new TTransportException(TTransportException.END_OF_FILE, "Write call frame size failed"); } if (sizeBuffer.remaining() == 0) { state = State.WRITING_REQUEST_BODY; diff --git a/lib/java/src/org/apache/thrift/ShortStack.java b/lib/java/src/org/apache/thrift/protocol/ShortStack.java similarity index 74% rename from lib/java/src/org/apache/thrift/ShortStack.java rename to lib/java/src/org/apache/thrift/protocol/ShortStack.java index 4957d1c77b5..9e659307459 100644 --- a/lib/java/src/org/apache/thrift/ShortStack.java +++ b/lib/java/src/org/apache/thrift/protocol/ShortStack.java @@ -16,45 +16,43 @@ * specific language governing permissions and limitations * under the License. */ -package org.apache.thrift; +package org.apache.thrift.protocol; + +import java.util.Arrays; /** * ShortStack is a short-specific Stack implementation written for the express * purpose of very fast operations on TCompactProtocol's field id stack. This * implementation performs at least 10x faster than java.util.Stack. */ -public class ShortStack { +class ShortStack { private short[] vector; - private int top = -1; + + /** Always points to the next location */ + private int top = 0; public ShortStack(int initialCapacity) { vector = new short[initialCapacity]; } public short pop() { - return vector[top--]; + return vector[--top]; } public void push(short pushed) { - if (vector.length == top + 1) { + if (vector.length == top) { grow(); } - vector[++top] = pushed; + vector[top++] = pushed; } private void grow() { - short[] newVector = new short[vector.length * 2]; - System.arraycopy(vector, 0, newVector, 0, vector.length); - vector = newVector; - } - - public short peek() { - return vector[top]; + vector = Arrays.copyOf(vector, vector.length << 1); } public void clear() { - top = -1; + top = 0; } @Override @@ -62,18 +60,15 @@ public String toString() { StringBuilder sb = new StringBuilder(); sb.append(">"); - } - - sb.append(vector[i]); - - if (i == top) { - sb.append("<<"); + if (isTop) { + sb.append(">>").append(value).append("<<"); + } else { + sb.append(value); } } sb.append("]>"); diff --git a/lib/java/src/org/apache/thrift/protocol/TBinaryProtocol.java b/lib/java/src/org/apache/thrift/protocol/TBinaryProtocol.java index aaa1fd8c7e4..fc46f7c6f89 100644 --- a/lib/java/src/org/apache/thrift/protocol/TBinaryProtocol.java +++ b/lib/java/src/org/apache/thrift/protocol/TBinaryProtocol.java @@ -19,11 +19,12 @@ package org.apache.thrift.protocol; -import java.io.UnsupportedEncodingException; import java.nio.ByteBuffer; +import java.nio.charset.StandardCharsets; import org.apache.thrift.TException; import org.apache.thrift.transport.TTransport; +import org.apache.thrift.transport.TTransportException; /** * Binary protocol implementation for thrift. @@ -110,6 +111,7 @@ public TBinaryProtocol(TTransport trans, long stringLengthLimit, long containerL strictWrite_ = strictWrite; } + @Override public void writeMessageBegin(TMessage message) throws TException { if (strictWrite_) { int version = VERSION_1 | message.type; @@ -123,60 +125,76 @@ public void writeMessageBegin(TMessage message) throws TException { } } - public void writeMessageEnd() {} + @Override + public void writeMessageEnd() throws TException {} - public void writeStructBegin(TStruct struct) {} + @Override + public void writeStructBegin(TStruct struct) throws TException {} - public void writeStructEnd() {} + @Override + public void writeStructEnd() throws TException {} + @Override public void writeFieldBegin(TField field) throws TException { writeByte(field.type); writeI16(field.id); } - public void writeFieldEnd() {} + @Override + public void writeFieldEnd() throws TException {} + @Override public void writeFieldStop() throws TException { writeByte(TType.STOP); } + @Override public void writeMapBegin(TMap map) throws TException { writeByte(map.keyType); writeByte(map.valueType); writeI32(map.size); } - public void writeMapEnd() {} + @Override + public void writeMapEnd() throws TException {} + @Override public void writeListBegin(TList list) throws TException { writeByte(list.elemType); writeI32(list.size); } - public void writeListEnd() {} + @Override + public void writeListEnd() throws TException {} + @Override public void writeSetBegin(TSet set) throws TException { writeByte(set.elemType); writeI32(set.size); } - public void writeSetEnd() {} + @Override + public void writeSetEnd() throws TException {} + @Override public void writeBool(boolean b) throws TException { writeByte(b ? (byte)1 : (byte)0); } + @Override public void writeByte(byte b) throws TException { inoutTemp[0] = b; trans_.write(inoutTemp, 0, 1); } + @Override public void writeI16(short i16) throws TException { inoutTemp[0] = (byte)(0xff & (i16 >> 8)); inoutTemp[1] = (byte)(0xff & (i16)); trans_.write(inoutTemp, 0, 2); } + @Override public void writeI32(int i32) throws TException { inoutTemp[0] = (byte)(0xff & (i32 >> 24)); inoutTemp[1] = (byte)(0xff & (i32 >> 16)); @@ -185,6 +203,7 @@ public void writeI32(int i32) throws TException { trans_.write(inoutTemp, 0, 4); } + @Override public void writeI64(long i64) throws TException { inoutTemp[0] = (byte)(0xff & (i64 >> 56)); inoutTemp[1] = (byte)(0xff & (i64 >> 48)); @@ -197,20 +216,19 @@ public void writeI64(long i64) throws TException { trans_.write(inoutTemp, 0, 8); } + @Override public void writeDouble(double dub) throws TException { writeI64(Double.doubleToLongBits(dub)); } + @Override public void writeString(String str) throws TException { - try { - byte[] dat = str.getBytes("UTF-8"); - writeI32(dat.length); - trans_.write(dat, 0, dat.length); - } catch (UnsupportedEncodingException uex) { - throw new TException("JVM DOES NOT SUPPORT UTF-8"); - } + byte[] dat = str.getBytes(StandardCharsets.UTF_8); + writeI32(dat.length); + trans_.write(dat, 0, dat.length); } + @Override public void writeBinary(ByteBuffer bin) throws TException { int length = bin.limit() - bin.position(); writeI32(length); @@ -221,6 +239,7 @@ public void writeBinary(ByteBuffer bin) throws TException { * Reading methods. */ + @Override public TMessage readMessageBegin() throws TException { int size = readI32(); if (size < 0) { @@ -237,50 +256,69 @@ public TMessage readMessageBegin() throws TException { } } - public void readMessageEnd() {} + @Override + public void readMessageEnd() throws TException {} - public TStruct readStructBegin() { + @Override + public TStruct readStructBegin() throws TException { return ANONYMOUS_STRUCT; } - public void readStructEnd() {} + @Override + public void readStructEnd() throws TException {} + @Override public TField readFieldBegin() throws TException { byte type = readByte(); short id = type == TType.STOP ? 0 : readI16(); return new TField("", type, id); } - public void readFieldEnd() {} + @Override + public void readFieldEnd() throws TException {} + @Override public TMap readMapBegin() throws TException { TMap map = new TMap(readByte(), readByte(), readI32()); + + checkReadBytesAvailable(map); checkContainerReadLength(map.size); return map; } - public void readMapEnd() {} + @Override + public void readMapEnd() throws TException {} + @Override public TList readListBegin() throws TException { TList list = new TList(readByte(), readI32()); + + checkReadBytesAvailable(list); checkContainerReadLength(list.size); return list; } - public void readListEnd() {} + @Override + public void readListEnd() throws TException {} + @Override public TSet readSetBegin() throws TException { TSet set = new TSet(readByte(), readI32()); + + checkReadBytesAvailable(set); checkContainerReadLength(set.size); return set; } - public void readSetEnd() {} + @Override + public void readSetEnd() throws TException {} + @Override public boolean readBool() throws TException { return (readByte() == 1); } + @Override public byte readByte() throws TException { if (trans_.getBytesRemainingInBuffer() >= 1) { byte b = trans_.getBuffer()[trans_.getBufferPosition()]; @@ -291,6 +329,7 @@ public byte readByte() throws TException { return inoutTemp[0]; } + @Override public short readI16() throws TException { byte[] buf = inoutTemp; int off = 0; @@ -309,6 +348,7 @@ public short readI16() throws TException { ((buf[off+1] & 0xff))); } + @Override public int readI32() throws TException { byte[] buf = inoutTemp; int off = 0; @@ -327,6 +367,7 @@ public int readI32() throws TException { ((buf[off+3] & 0xff)); } + @Override public long readI64() throws TException { byte[] buf = inoutTemp; int off = 0; @@ -350,23 +391,20 @@ public long readI64() throws TException { ((long)(buf[off+7] & 0xff)); } + @Override public double readDouble() throws TException { return Double.longBitsToDouble(readI64()); } + @Override public String readString() throws TException { int size = readI32(); - checkStringReadLength(size); - if (trans_.getBytesRemainingInBuffer() >= size) { - try { - String s = new String(trans_.getBuffer(), trans_.getBufferPosition(), size, "UTF-8"); - trans_.consumeBuffer(size); - return s; - } catch (UnsupportedEncodingException e) { - throw new TException("JVM DOES NOT SUPPORT UTF-8"); - } + String s = new String(trans_.getBuffer(), trans_.getBufferPosition(), + size, StandardCharsets.UTF_8); + trans_.consumeBuffer(size); + return s; } return readStringBody(size); @@ -374,15 +412,12 @@ public String readString() throws TException { public String readStringBody(int size) throws TException { checkStringReadLength(size); - try { - byte[] buf = new byte[size]; - trans_.readAll(buf, 0, size); - return new String(buf, "UTF-8"); - } catch (UnsupportedEncodingException uex) { - throw new TException("JVM DOES NOT SUPPORT UTF-8"); - } + byte[] buf = new byte[size]; + trans_.readAll(buf, 0, size); + return new String(buf, StandardCharsets.UTF_8); } + @Override public ByteBuffer readBinary() throws TException { int size = readI32(); @@ -399,11 +434,14 @@ public ByteBuffer readBinary() throws TException { return ByteBuffer.wrap(buf); } - private void checkStringReadLength(int length) throws TProtocolException { + private void checkStringReadLength(int length) throws TException { if (length < 0) { throw new TProtocolException(TProtocolException.NEGATIVE_SIZE, "Negative length: " + length); } + + getTransport().checkReadBytesAvailable(length); + if (stringLengthLimit_ != NO_LENGTH_LIMIT && length > stringLengthLimit_) { throw new TProtocolException(TProtocolException.SIZE_LIMIT, "Length exceeded max allowed: " + length); @@ -424,4 +462,28 @@ private void checkContainerReadLength(int length) throws TProtocolException { private int readAll(byte[] buf, int off, int len) throws TException { return trans_.readAll(buf, off, len); } + + /** + * + * Return the minimum number of bytes a type will consume on the wire + */ + public int getMinSerializedSize(byte type) throws TTransportException { + switch (type) + { + case 0: return 0; // Stop + case 1: return 0; // Void + case 2: return 1; // Bool sizeof(byte) + case 3: return 1; // Byte sizeof(byte) + case 4: return 8; // Double sizeof(double) + case 6: return 2; // I16 sizeof(short) + case 8: return 4; // I32 sizeof(int) + case 10: return 8;// I64 sizeof(long) + case 11: return 4; // string length sizeof(int) + case 12: return 0; // empty struct + case 13: return 4; // element count Map sizeof(int) + case 14: return 4; // element count Set sizeof(int) + case 15: return 4; // element count List sizeof(int) + default: throw new TTransportException(TTransportException.UNKNOWN, "unrecognized type code"); + } + } } diff --git a/lib/java/src/org/apache/thrift/protocol/TCompactProtocol.java b/lib/java/src/org/apache/thrift/protocol/TCompactProtocol.java index 56c349a1551..4f4e21f50a1 100644 --- a/lib/java/src/org/apache/thrift/protocol/TCompactProtocol.java +++ b/lib/java/src/org/apache/thrift/protocol/TCompactProtocol.java @@ -22,10 +22,11 @@ import java.io.UnsupportedEncodingException; import java.nio.ByteBuffer; +import java.nio.charset.StandardCharsets; -import org.apache.thrift.ShortStack; import org.apache.thrift.TException; import org.apache.thrift.transport.TTransport; +import org.apache.thrift.transport.TTransportException; /** * TCompactProtocol2 is the Java implementation of the compact protocol specified @@ -202,6 +203,7 @@ public void reset() { * Write a message header to the wire. Compact Protocol messages contain the * protocol version so we can migrate forwards in the future if need be. */ + @Override public void writeMessageBegin(TMessage message) throws TException { writeByteDirect(PROTOCOL_ID); writeByteDirect((VERSION & VERSION_MASK) | ((message.type << TYPE_SHIFT_AMOUNT) & TYPE_MASK)); @@ -214,6 +216,7 @@ public void writeMessageBegin(TMessage message) throws TException { * use it as an opportunity to put special placeholder markers on the field * stack so we can get the field id deltas correct. */ + @Override public void writeStructBegin(TStruct struct) throws TException { lastField_.push(lastFieldId_); lastFieldId_ = 0; @@ -359,25 +362,18 @@ public void writeDouble(double dub) throws TException { * Write a string to the wire with a varint size preceding. */ public void writeString(String str) throws TException { - try { - byte[] bytes = str.getBytes("UTF-8"); - writeBinary(bytes, 0, bytes.length); - } catch (UnsupportedEncodingException e) { - throw new TException("UTF-8 not supported!"); - } + byte[] bytes = str.getBytes(StandardCharsets.UTF_8); + writeVarint32(bytes.length); + trans_.write(bytes, 0, bytes.length); } /** * Write a byte array, using a varint for the size. */ public void writeBinary(ByteBuffer bin) throws TException { - int length = bin.limit() - bin.position(); - writeBinary(bin.array(), bin.position() + bin.arrayOffset(), length); - } - - private void writeBinary(byte[] buf, int offset, int length) throws TException { - writeVarint32(length); - trans_.write(buf, offset, length); + ByteBuffer bb = bin.asReadOnlyBuffer(); + writeVarint32(bb.remaining()); + trans_.write(bb); } // @@ -581,7 +577,9 @@ public TMap readMapBegin() throws TException { int size = readVarint32(); checkContainerReadLength(size); byte keyAndValueType = size == 0 ? 0 : readByte(); - return new TMap(getTType((byte)(keyAndValueType >> 4)), getTType((byte)(keyAndValueType & 0xf)), size); + TMap map = new TMap(getTType((byte)(keyAndValueType >> 4)), getTType((byte)(keyAndValueType & 0xf)), size); + checkReadBytesAvailable(map); + return map; } /** @@ -597,8 +595,9 @@ public TList readListBegin() throws TException { size = readVarint32(); } checkContainerReadLength(size); - byte type = getTType(size_and_type); - return new TList(type, size); + TList list = new TList(getTType(size_and_type), size); + checkReadBytesAvailable(list); + return list; } /** @@ -680,27 +679,26 @@ public String readString() throws TException { return ""; } - try { - if (trans_.getBytesRemainingInBuffer() >= length) { - String str = new String(trans_.getBuffer(), trans_.getBufferPosition(), length, "UTF-8"); - trans_.consumeBuffer(length); - return str; - } else { - return new String(readBinary(length), "UTF-8"); - } - } catch (UnsupportedEncodingException e) { - throw new TException("UTF-8 not supported!"); + final String str; + if (trans_.getBytesRemainingInBuffer() >= length) { + str = new String(trans_.getBuffer(), trans_.getBufferPosition(), + length, StandardCharsets.UTF_8); + trans_.consumeBuffer(length); + } else { + str = new String(readBinary(length), StandardCharsets.UTF_8); } + return str; } /** - * Read a byte[] from the wire. + * Read a ByteBuffer from the wire. */ public ByteBuffer readBinary() throws TException { int length = readVarint32(); - checkStringReadLength(length); - if (length == 0) return EMPTY_BUFFER; - + if (length == 0) { + return EMPTY_BUFFER; + } + getTransport().checkReadBytesAvailable(length); if (trans_.getBytesRemainingInBuffer() >= length) { ByteBuffer bb = ByteBuffer.wrap(trans_.getBuffer(), trans_.getBufferPosition(), length); trans_.consumeBuffer(length); @@ -723,11 +721,14 @@ private byte[] readBinary(int length) throws TException { return buf; } - private void checkStringReadLength(int length) throws TProtocolException { + private void checkStringReadLength(int length) throws TException { if (length < 0) { throw new TProtocolException(TProtocolException.NEGATIVE_SIZE, "Negative length: " + length); } + + getTransport().checkReadBytesAvailable(length); + if (stringLengthLimit_ != NO_LENGTH_LIMIT && length > stringLengthLimit_) { throw new TProtocolException(TProtocolException.SIZE_LIMIT, "Length exceeded max allowed: " + length); @@ -905,4 +906,40 @@ private byte getTType(byte type) throws TProtocolException { private byte getCompactType(byte ttype) { return ttypeToCompactType[ttype]; } + + /** + * Return the minimum number of bytes a type will consume on the wire + */ + public int getMinSerializedSize(byte type) throws TTransportException { + switch (type) { + case 0: + return 0; // Stop + case 1: + return 0; // Void + case 2: + return 1; // Bool sizeof(byte) + case 3: + return 1; // Byte sizeof(byte) + case 4: + return 8; // Double sizeof(double) + case 6: + return 1; // I16 sizeof(byte) + case 8: + return 1; // I32 sizeof(byte) + case 10: + return 1;// I64 sizeof(byte) + case 11: + return 1; // string length sizeof(byte) + case 12: + return 0; // empty struct + case 13: + return 1; // element count Map sizeof(byte) + case 14: + return 1; // element count Set sizeof(byte) + case 15: + return 1; // element count List sizeof(byte) + default: + throw new TTransportException(TTransportException.UNKNOWN, "unrecognized type code"); + } + } } diff --git a/lib/java/src/org/apache/thrift/protocol/TField.java b/lib/java/src/org/apache/thrift/protocol/TField.java index 31331bba36b..3872b008fb3 100644 --- a/lib/java/src/org/apache/thrift/protocol/TField.java +++ b/lib/java/src/org/apache/thrift/protocol/TField.java @@ -21,7 +21,7 @@ /** * Helper class that encapsulates field metadata. - * + *

Two fields are considered equal if they have the same type and id.

*/ public class TField { public TField() { @@ -47,7 +47,6 @@ public int hashCode() { final int prime = 31; int result = 1; result = prime * result + id; - result = prime * result + ((name == null) ? 0 : name.hashCode()); result = prime * result + type; return result; } diff --git a/lib/java/src/org/apache/thrift/protocol/TJSONProtocol.java b/lib/java/src/org/apache/thrift/protocol/TJSONProtocol.java index fd54fdf462c..6bb49cb2f73 100644 --- a/lib/java/src/org/apache/thrift/protocol/TJSONProtocol.java +++ b/lib/java/src/org/apache/thrift/protocol/TJSONProtocol.java @@ -20,14 +20,15 @@ package org.apache.thrift.protocol; import java.io.IOException; -import java.io.UnsupportedEncodingException; import java.nio.ByteBuffer; +import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.Stack; import org.apache.thrift.TByteArrayOutputStream; import org.apache.thrift.TException; import org.apache.thrift.transport.TTransport; +import org.apache.thrift.transport.TTransportException; /** * JSON protocol implementation for thrift. @@ -418,12 +419,8 @@ private void writeJSONInteger(long num) throws TException { if (escapeNum) { trans_.write(QUOTE); } - try { - byte[] buf = str.getBytes("UTF-8"); - trans_.write(buf); - } catch (UnsupportedEncodingException uex) { - throw new TException("JVM DOES NOT SUPPORT UTF-8"); - } + byte[] buf = str.getBytes(StandardCharsets.UTF_8); + trans_.write(buf); if (escapeNum) { trans_.write(QUOTE); } @@ -453,12 +450,8 @@ private void writeJSONDouble(double num) throws TException { if (escapeNum) { trans_.write(QUOTE); } - try { - byte[] b = str.getBytes("UTF-8"); - trans_.write(b, 0, b.length); - } catch (UnsupportedEncodingException uex) { - throw new TException("JVM DOES NOT SUPPORT UTF-8"); - } + byte[] b = str.getBytes(StandardCharsets.UTF_8); + trans_.write(b, 0, b.length); if (escapeNum) { trans_.write(QUOTE); } @@ -513,12 +506,8 @@ public void writeMessageBegin(TMessage message) throws TException { resetContext(); // THRIFT-3743 writeJSONArrayStart(); writeJSONInteger(VERSION); - try { - byte[] b = message.name.getBytes("UTF-8"); - writeJSONString(b); - } catch (UnsupportedEncodingException uex) { - throw new TException("JVM DOES NOT SUPPORT UTF-8"); - } + byte[] b = message.name.getBytes(StandardCharsets.UTF_8); + writeJSONString(b); writeJSONInteger(message.type); writeJSONInteger(message.seqid); } @@ -603,17 +592,17 @@ public void writeBool(boolean b) throws TException { @Override public void writeByte(byte b) throws TException { - writeJSONInteger((long)b); + writeJSONInteger(b); } @Override public void writeI16(short i16) throws TException { - writeJSONInteger((long)i16); + writeJSONInteger(i16); } @Override public void writeI32(int i32) throws TException { - writeJSONInteger((long)i32); + writeJSONInteger(i32); } @Override @@ -628,12 +617,8 @@ public void writeDouble(double dub) throws TException { @Override public void writeString(String str) throws TException { - try { - byte[] b = str.getBytes("UTF-8"); - writeJSONString(b); - } catch (UnsupportedEncodingException uex) { - throw new TException("JVM DOES NOT SUPPORT UTF-8"); - } + byte[] b = str.getBytes(StandardCharsets.UTF_8); + writeJSONString(b); } @Override @@ -684,19 +669,17 @@ else if (Character.isLowSurrogate((char)cu)) { } codeunits.add((char)cu); - arr.write((new String(new int[] { codeunits.get(0), codeunits.get(1) }, 0, 2)).getBytes("UTF-8")); + arr.write( + (new String(new int[] { codeunits.get(0), codeunits.get(1) }, + 0, 2)).getBytes(StandardCharsets.UTF_8)); codeunits.clear(); } else { - arr.write((new String(new int[] { cu }, 0, 1)).getBytes("UTF-8")); + arr.write((new String(new int[] { cu }, 0, 1)) + .getBytes(StandardCharsets.UTF_8)); } continue; - } - catch (UnsupportedEncodingException ex) { - throw new TProtocolException(TProtocolException.NOT_IMPLEMENTED, - "JVM does not support UTF-8"); - } - catch (IOException ex) { + } catch (IOException ex) { throw new TProtocolException(TProtocolException.INVALID_DATA, "Invalid unicode sequence"); } @@ -777,19 +760,14 @@ private double readJSONDouble() throws TException { context_.read(); if (reader_.peek() == QUOTE[0]) { TByteArrayOutputStream arr = readJSONString(true); - try { - double dub = Double.valueOf(arr.toString("UTF-8")); - if (!context_.escapeNum() && !Double.isNaN(dub) && - !Double.isInfinite(dub)) { - // Throw exception -- we should not be in a string in this case - throw new TProtocolException(TProtocolException.INVALID_DATA, - "Numeric data unexpectedly quoted"); - } - return dub; - } - catch (UnsupportedEncodingException ex) { - throw new TException("JVM DOES NOT SUPPORT UTF-8"); + double dub = Double.valueOf(arr.toString(StandardCharsets.UTF_8)); + if (!context_.escapeNum() && !Double.isNaN(dub) + && !Double.isInfinite(dub)) { + // Throw exception -- we should not be in a string in this case + throw new TProtocolException(TProtocolException.INVALID_DATA, + "Numeric data unexpectedly quoted"); } + return dub; } else { if (context_.escapeNum()) { @@ -868,13 +846,7 @@ public TMessage readMessageBegin() throws TException { throw new TProtocolException(TProtocolException.BAD_VERSION, "Message contained bad version."); } - String name; - try { - name = readJSONString(false).toString("UTF-8"); - } - catch (UnsupportedEncodingException ex) { - throw new TException("JVM DOES NOT SUPPORT UTF-8"); - } + String name = readJSONString(false).toString(StandardCharsets.UTF_8); byte type = (byte) readJSONInteger(); int seqid = (int) readJSONInteger(); return new TMessage(name, type, seqid); @@ -924,7 +896,10 @@ public TMap readMapBegin() throws TException { byte valueType = getTypeIDForTypeName(readJSONString(false).get()); int size = (int)readJSONInteger(); readJSONObjectStart(); - return new TMap(keyType, valueType, size); + TMap map = new TMap(keyType, valueType, size); + + checkReadBytesAvailable(map); + return map; } @Override @@ -938,7 +913,10 @@ public TList readListBegin() throws TException { readJSONArrayStart(); byte elemType = getTypeIDForTypeName(readJSONString(false).get()); int size = (int)readJSONInteger(); - return new TList(elemType, size); + TList list = new TList(elemType, size); + + checkReadBytesAvailable(list); + return list; } @Override @@ -951,7 +929,10 @@ public TSet readSetBegin() throws TException { readJSONArrayStart(); byte elemType = getTypeIDForTypeName(readJSONString(false).get()); int size = (int)readJSONInteger(); - return new TSet(elemType, size); + TSet set = new TSet(elemType, size); + + checkReadBytesAvailable(set); + return set; } @Override @@ -961,7 +942,7 @@ public void readSetEnd() throws TException { @Override public boolean readBool() throws TException { - return (readJSONInteger() == 0 ? false : true); + return (readJSONInteger() != 0); } @Override @@ -981,7 +962,7 @@ public int readI32() throws TException { @Override public long readI64() throws TException { - return (long) readJSONInteger(); + return readJSONInteger(); } @Override @@ -991,12 +972,9 @@ public double readDouble() throws TException { @Override public String readString() throws TException { - try { - return readJSONString(false).toString("UTF-8"); - } - catch (UnsupportedEncodingException ex) { - throw new TException("JVM DOES NOT SUPPORT UTF-8"); - } + String str = readJSONString(false).toString(StandardCharsets.UTF_8); + getTransport().checkReadBytesAvailable(str.length() * getMinSerializedSize(TType.STRING)); + return str; } @Override @@ -1004,4 +982,28 @@ public ByteBuffer readBinary() throws TException { return ByteBuffer.wrap(readJSONBase64()); } + /** + * + * Return the minimum number of bytes a type will consume on the wire + */ + public int getMinSerializedSize(byte type) throws TTransportException { + switch (type) + { + case 0: return 0; // Stop + case 1: return 0; // Void + case 2: return 1; // Bool + case 3: return 1; // Byte + case 4: return 1; // Double + case 6: return 1; // I16 + case 8: return 1; // I32 + case 10: return 1;// I64 + case 11: return 2; // string length + case 12: return 2; // empty struct + case 13: return 2; // element count Map + case 14: return 2; // element count Set + case 15: return 2; // element count List + default: throw new TTransportException(TTransportException.UNKNOWN, "unrecognized type code"); + } + } + } diff --git a/lib/java/src/org/apache/thrift/protocol/TProtocol.java b/lib/java/src/org/apache/thrift/protocol/TProtocol.java index 0e96368d494..38c030e73fb 100644 --- a/lib/java/src/org/apache/thrift/protocol/TProtocol.java +++ b/lib/java/src/org/apache/thrift/protocol/TProtocol.java @@ -57,6 +57,27 @@ public TTransport getTransport() { return trans_; } + protected void checkReadBytesAvailable(TMap map) throws TException { + long elemSize = getMinSerializedSize(map.keyType) + getMinSerializedSize(map.valueType); + trans_.checkReadBytesAvailable(map.size * elemSize); + } + + protected void checkReadBytesAvailable(TList list) throws TException { + trans_.checkReadBytesAvailable(list.size * getMinSerializedSize(list.elemType)); + } + + protected void checkReadBytesAvailable(TSet set) throws TException { + trans_.checkReadBytesAvailable(set.size * getMinSerializedSize(set.elemType)); + } + + /** + * Return + * @param type Returns the minimum amount of bytes needed to store the smallest possible instance of TType. + * @return + * @throws TException + */ + public abstract int getMinSerializedSize(byte type) throws TException; + /** * Writing methods. */ @@ -152,7 +173,7 @@ public TTransport getTransport() { * be implemented for stateful protocols. */ public void reset() {} - + /** * Scheme accessor */ diff --git a/lib/java/src/org/apache/thrift/protocol/TProtocolDecorator.java b/lib/java/src/org/apache/thrift/protocol/TProtocolDecorator.java index 2d29cd231f9..9d109622f86 100644 --- a/lib/java/src/org/apache/thrift/protocol/TProtocolDecorator.java +++ b/lib/java/src/org/apache/thrift/protocol/TProtocolDecorator.java @@ -31,7 +31,7 @@ * the behaviour of the enclosed TProtocol. * *

See p.175 of Design Patterns (by Gamma et al.)

- * + * * @see org.apache.thrift.protocol.TMultiplexedProtocol */ public abstract class TProtocolDecorator extends TProtocol { @@ -210,4 +210,14 @@ public String readString() throws TException { public ByteBuffer readBinary() throws TException { return concreteProtocol.readBinary(); } + + /** + * + * @param type Returns the minimum amount of bytes needed to store the smallest possible instance of TType. + * @return + * @throws TException + */ + public int getMinSerializedSize(byte type) throws TException { + return concreteProtocol.getMinSerializedSize(type); + } } diff --git a/lib/java/src/org/apache/thrift/protocol/TSimpleJSONProtocol.java b/lib/java/src/org/apache/thrift/protocol/TSimpleJSONProtocol.java index b24e421a973..9413f619cf3 100644 --- a/lib/java/src/org/apache/thrift/protocol/TSimpleJSONProtocol.java +++ b/lib/java/src/org/apache/thrift/protocol/TSimpleJSONProtocol.java @@ -19,12 +19,13 @@ package org.apache.thrift.protocol; -import java.io.UnsupportedEncodingException; import java.nio.ByteBuffer; +import java.nio.charset.StandardCharsets; import java.util.Stack; import org.apache.thrift.TException; import org.apache.thrift.transport.TTransport; +import org.apache.thrift.transport.TTransportException; /** * JSON protocol implementation for thrift. @@ -167,6 +168,7 @@ public TSimpleJSONProtocol(TTransport trans) { super(trans); } + @Override public void writeMessageBegin(TMessage message) throws TException { resetWriteContext(); // THRIFT-3743 trans_.write(LBRACKET); @@ -176,31 +178,38 @@ public void writeMessageBegin(TMessage message) throws TException { writeI32(message.seqid); } + @Override public void writeMessageEnd() throws TException { popWriteContext(); trans_.write(RBRACKET); } + @Override public void writeStructBegin(TStruct struct) throws TException { writeContext_.write(); trans_.write(LBRACE); pushWriteContext(new StructContext()); } + @Override public void writeStructEnd() throws TException { popWriteContext(); trans_.write(RBRACE); } + @Override public void writeFieldBegin(TField field) throws TException { // Note that extra type information is omitted in JSON! writeString(field.name); } - public void writeFieldEnd() {} + @Override + public void writeFieldEnd() throws TException {} - public void writeFieldStop() {} + @Override + public void writeFieldStop() throws TException {} + @Override public void writeMapBegin(TMap map) throws TException { assertContextIsNotMapKey(MAP); writeContext_.write(); @@ -209,11 +218,13 @@ public void writeMapBegin(TMap map) throws TException { // No metadata! } + @Override public void writeMapEnd() throws TException { popWriteContext(); trans_.write(RBRACE); } + @Override public void writeListBegin(TList list) throws TException { assertContextIsNotMapKey(LIST); writeContext_.write(); @@ -222,11 +233,13 @@ public void writeListBegin(TList list) throws TException { // No metadata! } + @Override public void writeListEnd() throws TException { popWriteContext(); trans_.write(RBRACKET); } + @Override public void writeSetBegin(TSet set) throws TException { assertContextIsNotMapKey(SET); writeContext_.write(); @@ -235,23 +248,28 @@ public void writeSetBegin(TSet set) throws TException { // No metadata! } + @Override public void writeSetEnd() throws TException { popWriteContext(); trans_.write(RBRACKET); } + @Override public void writeBool(boolean b) throws TException { writeByte(b ? (byte)1 : (byte)0); } + @Override public void writeByte(byte b) throws TException { writeI32(b); } + @Override public void writeI16(short i16) throws TException { writeI32(i16); } + @Override public void writeI32(int i32) throws TException { if(writeContext_.isMapKey()) { writeString(Integer.toString(i32)); @@ -262,14 +280,11 @@ public void writeI32(int i32) throws TException { } public void _writeStringData(String s) throws TException { - try { - byte[] b = s.getBytes("UTF-8"); - trans_.write(b); - } catch (UnsupportedEncodingException uex) { - throw new TException("JVM DOES NOT SUPPORT UTF-8"); - } + byte[] b = s.getBytes(StandardCharsets.UTF_8); + trans_.write(b); } + @Override public void writeI64(long i64) throws TException { if(writeContext_.isMapKey()) { writeString(Long.toString(i64)); @@ -279,6 +294,7 @@ public void writeI64(long i64) throws TException { } } + @Override public void writeDouble(double dub) throws TException { if(writeContext_.isMapKey()) { writeString(Double.toString(dub)); @@ -288,6 +304,7 @@ public void writeDouble(double dub) throws TException { } } + @Override public void writeString(String str) throws TException { writeContext_.write(); int length = str.length(); @@ -341,90 +358,108 @@ public void writeString(String str) throws TException { _writeStringData(escape.toString()); } + @Override public void writeBinary(ByteBuffer bin) throws TException { - try { - // TODO(mcslee): Fix this - writeString(new String(bin.array(), bin.position() + bin.arrayOffset(), bin.limit() - bin.position() - bin.arrayOffset(), "UTF-8")); - } catch (UnsupportedEncodingException uex) { - throw new TException("JVM DOES NOT SUPPORT UTF-8"); - } + // TODO(mcslee): Fix this + writeString(new String(bin.array(), bin.position() + bin.arrayOffset(), + bin.limit() - bin.position() - bin.arrayOffset(), + StandardCharsets.UTF_8)); } /** * Reading methods. */ + @Override public TMessage readMessageBegin() throws TException { // TODO(mcslee): implement return EMPTY_MESSAGE; } - public void readMessageEnd() {} + @Override + public void readMessageEnd() throws TException {} - public TStruct readStructBegin() { + @Override + public TStruct readStructBegin() throws TException { // TODO(mcslee): implement return ANONYMOUS_STRUCT; } - public void readStructEnd() {} + @Override + public void readStructEnd() throws TException {} + @Override public TField readFieldBegin() throws TException { // TODO(mcslee): implement return ANONYMOUS_FIELD; } - public void readFieldEnd() {} + @Override + public void readFieldEnd() throws TException {} + @Override public TMap readMapBegin() throws TException { // TODO(mcslee): implement return EMPTY_MAP; } - public void readMapEnd() {} + @Override + public void readMapEnd() throws TException {} + @Override public TList readListBegin() throws TException { // TODO(mcslee): implement return EMPTY_LIST; } - public void readListEnd() {} + @Override + public void readListEnd() throws TException {} + @Override public TSet readSetBegin() throws TException { // TODO(mcslee): implement return EMPTY_SET; } - public void readSetEnd() {} + @Override + public void readSetEnd() throws TException {} + @Override public boolean readBool() throws TException { return (readByte() == 1); } + @Override public byte readByte() throws TException { // TODO(mcslee): implement return 0; } + @Override public short readI16() throws TException { // TODO(mcslee): implement return 0; } + @Override public int readI32() throws TException { // TODO(mcslee): implement return 0; } + @Override public long readI64() throws TException { // TODO(mcslee): implement return 0; } + @Override public double readDouble() throws TException { // TODO(mcslee): implement return 0; } + @Override public String readString() throws TException { // TODO(mcslee): implement return ""; @@ -435,6 +470,7 @@ public String readStringBody(int size) throws TException { return ""; } + @Override public ByteBuffer readBinary() throws TException { // TODO(mcslee): implement return ByteBuffer.wrap(new byte[0]); @@ -445,4 +481,28 @@ public CollectionMapKeyException(String message) { super(message); } } + + /** + * + * Return the minimum number of bytes a type will consume on the wire + */ + public int getMinSerializedSize(byte type) throws TException { + switch (type) + { + case 0: return 0; // Stop + case 1: return 0; // Void + case 2: return 1; // Bool + case 3: return 1; // Byte + case 4: return 1; // Double + case 6: return 1; // I16 + case 8: return 1; // I32 + case 10: return 1;// I64 + case 11: return 2; // string length + case 12: return 2; // empty struct + case 13: return 2; // element count Map + case 14: return 2; // element count Set + case 15: return 2; // element count List + default: throw new TTransportException(TTransportException.UNKNOWN, "unrecognized type code"); + } + } } diff --git a/lib/java/src/org/apache/thrift/protocol/TTupleProtocol.java b/lib/java/src/org/apache/thrift/protocol/TTupleProtocol.java index 74f5226c8af..67d00edb719 100644 --- a/lib/java/src/org/apache/thrift/protocol/TTupleProtocol.java +++ b/lib/java/src/org/apache/thrift/protocol/TTupleProtocol.java @@ -80,9 +80,9 @@ public static BitSet fromByteArray(byte[] bytes) { * extension). The byte-ordering of the result is big-endian which means the * most significant bit is in element 0. The bit at index 0 of the bit set is * assumed to be the least significant bit. - * + * * @param bits - * @param vectorWidth + * @param vectorWidth * @return a byte array of at least length 1 */ public static byte[] toByteArray(BitSet bits, int vectorWidth) { @@ -95,4 +95,27 @@ public static byte[] toByteArray(BitSet bits, int vectorWidth) { return bytes; } + public TMap readMapBegin(byte keyType, byte valTyep) throws TException { + int size = super.readI32(); + TMap map = new TMap(keyType, valTyep, size); + + checkReadBytesAvailable(map); + return map; + } + + public TList readListBegin(byte type) throws TException { + int size = super.readI32(); + TList list = new TList(type, size); + + checkReadBytesAvailable(list); + return list; + } + + public TSet readSetBegin(byte type) throws TException { + return new TSet(readListBegin(type)); + } + + public void readMapEnd() throws TException {} + public void readListEnd() throws TException {} + public void readSetEnd() throws TException {} } diff --git a/lib/java/src/org/apache/thrift/server/AbstractNonblockingServer.java b/lib/java/src/org/apache/thrift/server/AbstractNonblockingServer.java index 5c62b991d41..f91e8254fa3 100644 --- a/lib/java/src/org/apache/thrift/server/AbstractNonblockingServer.java +++ b/lib/java/src/org/apache/thrift/server/AbstractNonblockingServer.java @@ -23,7 +23,7 @@ import org.apache.thrift.TByteArrayOutputStream; import org.apache.thrift.TException; import org.apache.thrift.protocol.TProtocol; -import org.apache.thrift.transport.TFramedTransport; +import org.apache.thrift.transport.layered.TFramedTransport; import org.apache.thrift.transport.TIOStreamTransport; import org.apache.thrift.transport.TMemoryInputTransport; import org.apache.thrift.transport.TNonblockingServerTransport; @@ -305,7 +305,7 @@ public class FrameBuffer { public FrameBuffer(final TNonblockingTransport trans, final SelectionKey selectionKey, - final AbstractSelectThread selectThread) { + final AbstractSelectThread selectThread) throws TTransportException { trans_ = trans; selectionKey_ = selectionKey; selectThread_ = selectThread; @@ -414,8 +414,8 @@ public boolean write() { if (trans_.write(buffer_) < 0) { return false; } - } catch (IOException e) { - LOGGER.warn("Got an IOException during write!", e); + } catch (TTransportException e) { + LOGGER.warn("Got an Exception during write", e); return false; } @@ -435,17 +435,23 @@ public boolean write() { * has come in. */ public void changeSelectInterests() { - if (state_ == FrameBufferState.AWAITING_REGISTER_WRITE) { + switch (state_) { + case AWAITING_REGISTER_WRITE: // set the OP_WRITE interest selectionKey_.interestOps(SelectionKey.OP_WRITE); state_ = FrameBufferState.WRITING; - } else if (state_ == FrameBufferState.AWAITING_REGISTER_READ) { + break; + case AWAITING_REGISTER_READ: prepareRead(); - } else if (state_ == FrameBufferState.AWAITING_CLOSE) { + break; + case AWAITING_CLOSE: close(); selectionKey_.cancel(); - } else { - LOGGER.error("changeSelectInterest was called, but state is invalid (" + state_ + ")"); + break; + default: + LOGGER.error( + "changeSelectInterest was called, but state is invalid ({})", + state_); } } @@ -536,12 +542,9 @@ public void invoke() { */ private boolean internalRead() { try { - if (trans_.read(buffer_) < 0) { - return false; - } - return true; - } catch (IOException e) { - LOGGER.warn("Got an IOException in internalRead!", e); + return trans_.read(buffer_) >= 0; + } catch (TTransportException e) { + LOGGER.warn("Got an Exception in internalRead", e); return false; } } @@ -576,7 +579,7 @@ protected void requestSelectInterestChange() { } // FrameBuffer public class AsyncFrameBuffer extends FrameBuffer { - public AsyncFrameBuffer(TNonblockingTransport trans, SelectionKey selectionKey, AbstractSelectThread selectThread) { + public AsyncFrameBuffer(TNonblockingTransport trans, SelectionKey selectionKey, AbstractSelectThread selectThread) throws TTransportException { super(trans, selectionKey, selectThread); } diff --git a/lib/java/src/org/apache/thrift/server/ServerContext.java b/lib/java/src/org/apache/thrift/server/ServerContext.java index 9b0b99eeaa0..b7c587f3788 100644 --- a/lib/java/src/org/apache/thrift/server/ServerContext.java +++ b/lib/java/src/org/apache/thrift/server/ServerContext.java @@ -18,9 +18,32 @@ */ /** - * Interface for storing server's connection context + * Interface for storing server's connection context. */ - package org.apache.thrift.server; -public interface ServerContext {} +public interface ServerContext { + + /** + * Returns an object that implements the given interface to allow access to + * application specific contexts. + * + * @param iface A Class defining an interface that the result must implement + * @return an object that implements the interface + * @throws RuntimeException If the context cannot be unwrapped to the provided + * class + */ + T unwrap(Class iface); + + /** + * Returns true if this server context is a wrapper for the provided + * application specific context interface argument or returns false otherwise. + * + * @param iface a Class defining the underlying context + * @return true if this implements the interface can be unwrapped to the + * provided class + * @throws RuntimeException if an error occurs while determining whether the + * provided class can be unwrapped from this context. + */ + boolean isWrapperFor(Class iface); +} diff --git a/lib/java/src/org/apache/thrift/server/TNonblockingServer.java b/lib/java/src/org/apache/thrift/server/TNonblockingServer.java index fe0365a518b..eac05a8766d 100644 --- a/lib/java/src/org/apache/thrift/server/TNonblockingServer.java +++ b/lib/java/src/org/apache/thrift/server/TNonblockingServer.java @@ -86,8 +86,8 @@ protected void joinSelector() { try { selectAcceptThread_.join(); } catch (InterruptedException e) { - // for now, just silently ignore. technically this means we'll have less of - // a graceful shutdown as a result. + LOGGER.debug("Interrupted while waiting for accept thread", e); + Thread.currentThread().interrupt(); } } @@ -215,7 +215,7 @@ private void select() { protected FrameBuffer createFrameBuffer(final TNonblockingTransport trans, final SelectionKey selectionKey, - final AbstractSelectThread selectThread) { + final AbstractSelectThread selectThread) throws TTransportException { return processorFactory_.isAsyncProcessor() ? new AsyncFrameBuffer(trans, selectionKey, selectThread) : new FrameBuffer(trans, selectionKey, selectThread); @@ -229,7 +229,7 @@ private void handleAccept() throws IOException { TNonblockingTransport client = null; try { // accept the connection - client = (TNonblockingTransport)serverTransport.accept(); + client = serverTransport.accept(); clientKey = client.registerSelector(selector, SelectionKey.OP_READ); // add this key to the map @@ -239,7 +239,6 @@ private void handleAccept() throws IOException { } catch (TTransportException tte) { // something went wrong accepting. LOGGER.warn("Exception trying to accept!", tte); - tte.printStackTrace(); if (clientKey != null) cleanupSelectionKey(clientKey); if (client != null) client.close(); } diff --git a/lib/java/src/org/apache/thrift/server/TSaslNonblockingServer.java b/lib/java/src/org/apache/thrift/server/TSaslNonblockingServer.java new file mode 100644 index 00000000000..89dbb787240 --- /dev/null +++ b/lib/java/src/org/apache/thrift/server/TSaslNonblockingServer.java @@ -0,0 +1,480 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.thrift.server; + +import java.io.IOException; +import java.nio.channels.SelectionKey; +import java.nio.channels.Selector; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.TimeUnit; + +import javax.security.auth.callback.CallbackHandler; + +import org.apache.thrift.TProcessor; +import org.apache.thrift.transport.TNonblockingServerSocket; +import org.apache.thrift.transport.TNonblockingServerTransport; +import org.apache.thrift.transport.TNonblockingTransport; +import org.apache.thrift.transport.TTransportException; +import org.apache.thrift.transport.sasl.NonblockingSaslHandler; +import org.apache.thrift.transport.sasl.NonblockingSaslHandler.Phase; +import org.apache.thrift.transport.sasl.TBaseSaslProcessorFactory; +import org.apache.thrift.transport.sasl.TSaslProcessorFactory; +import org.apache.thrift.transport.sasl.TSaslServerFactory; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * TServer with sasl support, using asynchronous execution and nonblocking io. + */ +public class TSaslNonblockingServer extends TServer { + private static final Logger LOGGER = LoggerFactory.getLogger(TSaslNonblockingServer.class); + + private static final int DEFAULT_NETWORK_THREADS = 1; + private static final int DEFAULT_AUTHENTICATION_THREADS = 1; + private static final int DEFAULT_PROCESSING_THREADS = Runtime.getRuntime().availableProcessors(); + + private final AcceptorThread acceptor; + private final NetworkThreadPool networkThreadPool; + private final ExecutorService authenticationExecutor; + private final ExecutorService processingExecutor; + private final TSaslServerFactory saslServerFactory; + private final TSaslProcessorFactory saslProcessorFactory; + + public TSaslNonblockingServer(Args args) throws IOException { + super(args); + acceptor = new AcceptorThread((TNonblockingServerSocket) serverTransport_); + networkThreadPool = new NetworkThreadPool(args.networkThreads); + authenticationExecutor = Executors.newFixedThreadPool(args.saslThreads); + processingExecutor = Executors.newFixedThreadPool(args.processingThreads); + saslServerFactory = args.saslServerFactory; + saslProcessorFactory = args.saslProcessorFactory; + } + + @Override + public void serve() { + if (eventHandler_ != null) { + eventHandler_.preServe(); + } + networkThreadPool.start(); + acceptor.start(); + setServing(true); + } + + /** + * Trigger a graceful shutdown, but it does not block to wait for the shutdown to finish. + */ + @Override + public void stop() { + if (!stopped_) { + setServing(false); + stopped_ = true; + acceptor.wakeup(); + networkThreadPool.wakeupAll(); + authenticationExecutor.shutdownNow(); + processingExecutor.shutdownNow(); + } + } + + /** + * Gracefully shut down the server and block until all threads are stopped. + * + * @throws InterruptedException if is interrupted while waiting for shutdown. + */ + public void shutdown() throws InterruptedException { + stop(); + acceptor.join(); + for (NetworkThread networkThread : networkThreadPool.networkThreads) { + networkThread.join(); + } + while (!authenticationExecutor.isTerminated()) { + authenticationExecutor.awaitTermination(10, TimeUnit.SECONDS); + } + while (!processingExecutor.isTerminated()) { + processingExecutor.awaitTermination(10, TimeUnit.SECONDS); + } + } + + private class AcceptorThread extends Thread { + + private final TNonblockingServerTransport serverTransport; + private final Selector acceptSelector; + + private AcceptorThread(TNonblockingServerSocket serverTransport) throws IOException { + super("acceptor-thread"); + this.serverTransport = serverTransport; + acceptSelector = Selector.open(); + serverTransport.registerSelector(acceptSelector); + } + + @Override + public void run() { + try { + serverTransport.listen(); + while (!stopped_) { + select(); + acceptNewConnection(); + } + } catch (TTransportException e) { + // Failed to listen. + LOGGER.error("Failed to listen on server socket, error " + e.getType(), e); + } catch (Throwable e) { + // Unexpected errors. + LOGGER.error("Unexpected error in acceptor thread.", e); + } finally { + TSaslNonblockingServer.this.stop(); + close(); + } + } + + void wakeup() { + acceptSelector.wakeup(); + } + + private void acceptNewConnection() { + Iterator selectedKeyItr = acceptSelector.selectedKeys().iterator(); + while (!stopped_ && selectedKeyItr.hasNext()) { + SelectionKey selected = selectedKeyItr.next(); + selectedKeyItr.remove(); + if (selected.isAcceptable()) { + try { + while (true) { + // Accept all available connections from the backlog. + TNonblockingTransport connection = serverTransport.accept(); + if (connection == null) { + break; + } + if (!networkThreadPool.acceptNewConnection(connection)) { + LOGGER.error("Network thread does not accept: " + connection); + connection.close(); + } + } + } catch (TTransportException e) { + LOGGER.warn("Failed to accept incoming connection.", e); + } + } else { + LOGGER.error("Not acceptable selection: " + selected.channel()); + } + } + } + + private void select() { + try { + acceptSelector.select(); + } catch (IOException e) { + LOGGER.error("Failed to select on the server socket.", e); + } + } + + private void close() { + LOGGER.info("Closing acceptor thread."); + serverTransport.close(); + try { + acceptSelector.close(); + } catch (IOException e) { + LOGGER.error("Failed to close accept selector.", e); + } + } + } + + private class NetworkThread extends Thread { + private final BlockingQueue incomingConnections = new LinkedBlockingQueue<>(); + private final BlockingQueue stateTransitions = new LinkedBlockingQueue<>(); + private final Selector ioSelector; + + NetworkThread(String name) throws IOException { + super(name); + ioSelector = Selector.open(); + } + + @Override + public void run() { + try { + while (!stopped_) { + handleIncomingConnections(); + handleStateChanges(); + select(); + handleIO(); + } + } catch (Throwable e) { + LOGGER.error("Unreoverable error in " + getName(), e); + } finally { + close(); + } + } + + private void handleStateChanges() { + while (true) { + NonblockingSaslHandler statemachine = stateTransitions.poll(); + if (statemachine == null) { + return; + } + tryRunNextPhase(statemachine); + } + } + + private void select() { + try { + ioSelector.select(); + } catch (IOException e) { + LOGGER.error("Failed to select in " + getName(), e); + } + } + + private void handleIO() { + Iterator selectedKeyItr = ioSelector.selectedKeys().iterator(); + while (!stopped_ && selectedKeyItr.hasNext()) { + SelectionKey selected = selectedKeyItr.next(); + selectedKeyItr.remove(); + if (!selected.isValid()) { + closeChannel(selected); + } + NonblockingSaslHandler saslHandler = (NonblockingSaslHandler) selected.attachment(); + if (selected.isReadable()) { + saslHandler.handleRead(); + } else if (selected.isWritable()) { + saslHandler.handleWrite(); + } else { + LOGGER.error("Invalid intrest op " + selected.interestOps()); + closeChannel(selected); + continue; + } + if (saslHandler.isCurrentPhaseDone()) { + tryRunNextPhase(saslHandler); + } + } + } + + // The following methods are modifying the registered channel set on the selector, which itself + // is not thread safe. Thus we need a lock to protect it from race condition. + + private synchronized void handleIncomingConnections() { + while (true) { + TNonblockingTransport connection = incomingConnections.poll(); + if (connection == null) { + return; + } + if (!connection.isOpen()) { + LOGGER.warn("Incoming connection is already closed"); + continue; + } + try { + SelectionKey selectionKey = connection.registerSelector(ioSelector, SelectionKey.OP_READ); + if (selectionKey.isValid()) { + NonblockingSaslHandler saslHandler = new NonblockingSaslHandler(selectionKey, connection, + saslServerFactory, saslProcessorFactory, inputProtocolFactory_, outputProtocolFactory_, + eventHandler_); + selectionKey.attach(saslHandler); + } + } catch (IOException e) { + LOGGER.error("Failed to register connection for the selector, close it.", e); + connection.close(); + } + } + } + + private synchronized void close() { + LOGGER.warn("Closing " + getName()); + while (true) { + TNonblockingTransport incomingConnection = incomingConnections.poll(); + if (incomingConnection == null) { + break; + } + incomingConnection.close(); + } + Set registered = ioSelector.keys(); + for (SelectionKey selection : registered) { + closeChannel(selection); + } + try { + ioSelector.close(); + } catch (IOException e) { + LOGGER.error("Failed to close io selector " + getName(), e); + } + } + + private synchronized void closeChannel(SelectionKey selectionKey) { + if (selectionKey.attachment() == null) { + try { + selectionKey.channel().close(); + } catch (IOException e) { + LOGGER.error("Failed to close channel.", e); + } finally { + selectionKey.cancel(); + } + } else { + NonblockingSaslHandler saslHandler = (NonblockingSaslHandler) selectionKey.attachment(); + saslHandler.close(); + } + } + + private void tryRunNextPhase(NonblockingSaslHandler saslHandler) { + Phase nextPhase = saslHandler.getNextPhase(); + saslHandler.stepToNextPhase(); + switch (nextPhase) { + case EVALUATING_SASL_RESPONSE: + authenticationExecutor.submit(new Computation(saslHandler)); + break; + case PROCESSING: + processingExecutor.submit(new Computation(saslHandler)); + break; + case CLOSING: + saslHandler.runCurrentPhase(); + break; + default: // waiting for next io event for the current state machine + } + } + + public boolean accept(TNonblockingTransport connection) { + if (stopped_) { + return false; + } + if (incomingConnections.offer(connection)) { + wakeup(); + return true; + } + return false; + } + + private void wakeup() { + ioSelector.wakeup(); + } + + private class Computation implements Runnable { + + private final NonblockingSaslHandler statemachine; + + private Computation(NonblockingSaslHandler statemachine) { + this.statemachine = statemachine; + } + + @Override + public void run() { + try { + while (!statemachine.isCurrentPhaseDone()) { + statemachine.runCurrentPhase(); + } + stateTransitions.add(statemachine); + wakeup(); + } catch (Throwable e) { + LOGGER.error("Damn it!", e); + } + } + } + } + + private class NetworkThreadPool { + private final List networkThreads; + private int accepted = 0; + + NetworkThreadPool(int size) throws IOException { + networkThreads = new ArrayList<>(size); + int digits = (int) Math.log10(size) + 1; + String threadNamePattern = "network-thread-%0" + digits + "d"; + for (int i = 0; i < size; i++) { + networkThreads.add(new NetworkThread(String.format(threadNamePattern, i))); + } + } + + /** + * Round robin new connection among all the network threads. + * + * @param connection incoming connection. + * @return true if the incoming connection is accepted by network thread pool. + */ + boolean acceptNewConnection(TNonblockingTransport connection) { + return networkThreads.get((accepted ++) % networkThreads.size()).accept(connection); + } + + public void start() { + for (NetworkThread thread : networkThreads) { + thread.start(); + } + } + + void wakeupAll() { + for (NetworkThread networkThread : networkThreads) { + networkThread.wakeup(); + } + } + } + + public static class Args extends AbstractServerArgs { + + private int networkThreads = DEFAULT_NETWORK_THREADS; + private int saslThreads = DEFAULT_AUTHENTICATION_THREADS; + private int processingThreads = DEFAULT_PROCESSING_THREADS; + private TSaslServerFactory saslServerFactory = new TSaslServerFactory(); + private TSaslProcessorFactory saslProcessorFactory; + + public Args(TNonblockingServerTransport transport) { + super(transport); + } + + public Args networkThreads(int networkThreads) { + this.networkThreads = networkThreads <= 0 ? DEFAULT_NETWORK_THREADS : networkThreads; + return this; + } + + public Args saslThreads(int authenticationThreads) { + this.saslThreads = authenticationThreads <= 0 ? DEFAULT_AUTHENTICATION_THREADS : authenticationThreads; + return this; + } + + public Args processingThreads(int processingThreads) { + this.processingThreads = processingThreads <= 0 ? DEFAULT_PROCESSING_THREADS : processingThreads; + return this; + } + + public Args processor(TProcessor processor) { + saslProcessorFactory = new TBaseSaslProcessorFactory(processor); + return this; + } + + public Args saslProcessorFactory(TSaslProcessorFactory saslProcessorFactory) { + if (saslProcessorFactory == null) { + throw new NullPointerException("Processor factory cannot be null"); + } + this.saslProcessorFactory = saslProcessorFactory; + return this; + } + + public Args addSaslMechanism(String mechanism, String protocol, String serverName, + Map props, CallbackHandler cbh) { + saslServerFactory.addSaslMechanism(mechanism, protocol, serverName, props, cbh); + return this; + } + + public Args saslServerFactory(TSaslServerFactory saslServerFactory) { + if (saslServerFactory == null) { + throw new NullPointerException("saslServerFactory cannot be null"); + } + this.saslServerFactory = saslServerFactory; + return this; + } + } +} diff --git a/lib/java/src/org/apache/thrift/server/TServer.java b/lib/java/src/org/apache/thrift/server/TServer.java index 80f4f8629d0..bac06b26bfb 100644 --- a/lib/java/src/org/apache/thrift/server/TServer.java +++ b/lib/java/src/org/apache/thrift/server/TServer.java @@ -123,7 +123,7 @@ public T outputProtocolFactory(TProtocolFactory factory) { */ protected TProtocolFactory outputProtocolFactory_; - private boolean isServing; + private volatile boolean isServing; protected TServerEventHandler eventHandler_; diff --git a/lib/java/src/org/apache/thrift/server/TServerEventHandler.java b/lib/java/src/org/apache/thrift/server/TServerEventHandler.java index f069b9bfbe0..3bd79598622 100644 --- a/lib/java/src/org/apache/thrift/server/TServerEventHandler.java +++ b/lib/java/src/org/apache/thrift/server/TServerEventHandler.java @@ -28,6 +28,10 @@ * 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). + * + * TODO: It seems this is a custom code entry point created for some resource management purpose in hive. + * But when looking into hive code, we see that the argments of TProtocol and TTransport are never used. + * We probably should remove these arguments from all the methods. */ public interface TServerEventHandler { @@ -56,4 +60,4 @@ void deleteContext(ServerContext serverContext, void processContext(ServerContext serverContext, TTransport inputTransport, TTransport outputTransport); -} \ No newline at end of file +} diff --git a/lib/java/src/org/apache/thrift/server/TSimpleServer.java b/lib/java/src/org/apache/thrift/server/TSimpleServer.java index e815b2cf166..70cc3d77647 100644 --- a/lib/java/src/org/apache/thrift/server/TSimpleServer.java +++ b/lib/java/src/org/apache/thrift/server/TSimpleServer.java @@ -77,13 +77,12 @@ public void serve() { if (eventHandler_ != null) { eventHandler_.processContext(connectionContext, inputTransport, outputTransport); } - if(!processor.process(inputProtocol, outputProtocol)) { - break; - } + processor.process(inputProtocol, outputProtocol); } } } catch (TTransportException ttx) { // Client died, just move on + LOGGER.debug("Client Transportation Exception", ttx); } catch (TException tx) { if (!stopped_) { LOGGER.error("Thrift error occurred during processing of message.", tx); diff --git a/lib/java/src/org/apache/thrift/server/TThreadPoolServer.java b/lib/java/src/org/apache/thrift/server/TThreadPoolServer.java index 3b5f21e84c5..01749b98aeb 100644 --- a/lib/java/src/org/apache/thrift/server/TThreadPoolServer.java +++ b/lib/java/src/org/apache/thrift/server/TThreadPoolServer.java @@ -19,31 +19,29 @@ package org.apache.thrift.server; -import java.util.Random; +import java.util.Optional; import java.util.concurrent.ExecutorService; import java.util.concurrent.RejectedExecutionException; import java.util.concurrent.SynchronousQueue; +import java.util.concurrent.ThreadFactory; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; import org.apache.thrift.TException; import org.apache.thrift.TProcessor; import org.apache.thrift.protocol.TProtocol; -import org.apache.thrift.transport.TSaslTransportException; import org.apache.thrift.transport.TServerTransport; import org.apache.thrift.transport.TTransport; import org.apache.thrift.transport.TTransportException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; - /** * Server which uses Java's built in ThreadPool management to spawn off - * a worker pool that - * + * a worker pool that deals with client connections in blocking way. */ public class TThreadPoolServer extends TServer { - private static final Logger LOGGER = LoggerFactory.getLogger(TThreadPoolServer.class.getName()); + private static final Logger LOGGER = LoggerFactory.getLogger(TThreadPoolServer.class); public static class Args extends AbstractServerArgs { public int minWorkerThreads = 5; @@ -51,10 +49,6 @@ public static class Args extends AbstractServerArgs { public ExecutorService executorService; public int stopTimeoutVal = 60; public TimeUnit stopTimeoutUnit = TimeUnit.SECONDS; - public int requestTimeout = 20; - public TimeUnit requestTimeoutUnit = TimeUnit.SECONDS; - public int beBackoffSlotLength = 100; - public TimeUnit beBackoffSlotLengthUnit = TimeUnit.MILLISECONDS; public Args(TServerTransport transport) { super(transport); @@ -80,27 +74,6 @@ public Args stopTimeoutUnit(TimeUnit tu) { return this; } - public Args requestTimeout(int n) { - requestTimeout = n; - return this; - } - - public Args requestTimeoutUnit(TimeUnit tu) { - requestTimeoutUnit = tu; - return this; - } - //Binary exponential backoff slot length - public Args beBackoffSlotLength(int n) { - beBackoffSlotLength = n; - return this; - } - - //Binary exponential backoff slot time unit - public Args beBackoffSlotLengthUnit(TimeUnit tu) { - beBackoffSlotLengthUnit = tu; - return this; - } - public Args executorService(ExecutorService executorService) { this.executorService = executorService; return this; @@ -114,43 +87,35 @@ public Args executorService(ExecutorService executorService) { private final long stopTimeoutVal; - private final TimeUnit requestTimeoutUnit; - - private final long requestTimeout; - - private final long beBackoffSlotInMillis; - - private Random random = new Random(System.currentTimeMillis()); - public TThreadPoolServer(Args args) { super(args); stopTimeoutUnit = args.stopTimeoutUnit; stopTimeoutVal = args.stopTimeoutVal; - requestTimeoutUnit = args.requestTimeoutUnit; - requestTimeout = args.requestTimeout; - beBackoffSlotInMillis = args.beBackoffSlotLengthUnit.toMillis(args.beBackoffSlotLength); executorService_ = args.executorService != null ? args.executorService : createDefaultExecutorService(args); } private static ExecutorService createDefaultExecutorService(Args args) { - SynchronousQueue executorQueue = - new SynchronousQueue(); - return new ThreadPoolExecutor(args.minWorkerThreads, - args.maxWorkerThreads, - args.stopTimeoutVal, - args.stopTimeoutUnit, - executorQueue); + return new ThreadPoolExecutor(args.minWorkerThreads, args.maxWorkerThreads, 60L, TimeUnit.SECONDS, + new SynchronousQueue<>(), new ThreadFactory() { + @Override + public Thread newThread(Runnable r) { + Thread thread = new Thread(r); + thread.setDaemon(true); + thread.setName("TThreadPoolServer WorkerProcess-%d"); + return thread; + } + }); } protected ExecutorService getExecutorService() { return executorService_; } - + protected boolean preServe() { - try { + try { serverTransport_.listen(); } catch (TTransportException ttx) { LOGGER.error("Error occurred during listening.", ttx); @@ -163,79 +128,46 @@ protected boolean preServe() { } stopped_ = false; setServing(true); - return true; } public void serve() { - if (!preServe()) { - return; - } + if (!preServe()) { + return; + } + + execute(); + + executorService_.shutdownNow(); + + if (!waitForShutdown()) { + LOGGER.error("Shutdown is not done after " + stopTimeoutVal + stopTimeoutUnit); + } - execute(); - waitForShutdown(); - setServing(false); } - + protected void execute() { - int failureCount = 0; while (!stopped_) { try { TTransport client = serverTransport_.accept(); - WorkerProcess wp = new WorkerProcess(client); - - int retryCount = 0; - long remainTimeInMillis = requestTimeoutUnit.toMillis(requestTimeout); - while(true) { - try { - executorService_.execute(wp); - break; - } catch(Throwable t) { - if (t instanceof RejectedExecutionException) { - retryCount++; - try { - if (remainTimeInMillis > 0) { - //do a truncated 20 binary exponential backoff sleep - long sleepTimeInMillis = ((long) (random.nextDouble() * - (1L << Math.min(retryCount, 20)))) * beBackoffSlotInMillis; - sleepTimeInMillis = Math.min(sleepTimeInMillis, remainTimeInMillis); - TimeUnit.MILLISECONDS.sleep(sleepTimeInMillis); - remainTimeInMillis = remainTimeInMillis - sleepTimeInMillis; - } else { - client.close(); - wp = null; - LOGGER.warn("Task has been rejected by ExecutorService " + retryCount - + " times till timedout, reason: " + t); - break; - } - } catch (InterruptedException e) { - LOGGER.warn("Interrupted while waiting to place client on executor queue."); - Thread.currentThread().interrupt(); - break; - } - } else if (t instanceof Error) { - LOGGER.error("ExecutorService threw error: " + t, t); - throw (Error)t; - } else { - //for other possible runtime errors from ExecutorService, should also not kill serve - LOGGER.warn("ExecutorService threw error: " + t, t); - break; - } + try { + executorService_.execute(new WorkerProcess(client)); + } catch (RejectedExecutionException ree) { + if (!stopped_) { + LOGGER.warn("ThreadPool is saturated with incoming requests. Closing latest connection."); } + client.close(); } } catch (TTransportException ttx) { if (!stopped_) { - ++failureCount; - LOGGER.warn("Transport error occurred during acceptance of message.", ttx); + LOGGER.warn("Transport error occurred during acceptance of message", ttx); } } } } - - protected void waitForShutdown() { - executorService_.shutdown(); + protected boolean waitForShutdown() { // Loop until awaitTermination finally does return without a interrupted // exception. If we don't do this, then we'll shut down prematurely. We want // to let the executorService clear it's task queue, closing client sockets @@ -244,14 +176,14 @@ protected void waitForShutdown() { long now = System.currentTimeMillis(); while (timeoutMS >= 0) { try { - executorService_.awaitTermination(timeoutMS, TimeUnit.MILLISECONDS); - break; + return executorService_.awaitTermination(timeoutMS, TimeUnit.MILLISECONDS); } catch (InterruptedException ix) { long newnow = System.currentTimeMillis(); timeoutMS -= (newnow - now); now = newnow; } } + return false; } public void stop() { @@ -285,7 +217,7 @@ public void run() { TProtocol inputProtocol = null; TProtocol outputProtocol = null; - TServerEventHandler eventHandler = null; + Optional eventHandler = Optional.empty(); ServerContext connectionContext = null; try { @@ -295,43 +227,39 @@ public void run() { inputProtocol = inputProtocolFactory_.getProtocol(inputTransport); outputProtocol = outputProtocolFactory_.getProtocol(outputTransport); - eventHandler = getEventHandler(); - if (eventHandler != null) { - connectionContext = eventHandler.createContext(inputProtocol, outputProtocol); - } - // we check stopped_ first to make sure we're not supposed to be shutting - // down. this is necessary for graceful shutdown. - while (true) { + eventHandler = Optional.ofNullable(getEventHandler()); - if (eventHandler != null) { - eventHandler.processContext(connectionContext, inputTransport, outputTransport); - } + if (eventHandler.isPresent()) { + connectionContext = eventHandler.get().createContext(inputProtocol, outputProtocol); + } - if(stopped_ || !processor.process(inputProtocol, outputProtocol)) { - break; - } + while (true) { + if (Thread.currentThread().isInterrupted()) { + LOGGER.debug("WorkerProcess requested to shutdown"); + break; + } + if (eventHandler.isPresent()) { + eventHandler.get().processContext(connectionContext, inputTransport, outputTransport); + } + // This process cannot be interrupted by Interrupting the Thread. This + // will return once a message has been processed or the socket timeout + // has elapsed, at which point it will return and check the interrupt + // state of the thread. + processor.process(inputProtocol, outputProtocol); } - } catch (TException tx) { - LOGGER.error("Thrift error occurred during processing of message.", tx); } catch (Exception x) { + LOGGER.debug("Error processing request", x); + // We'll usually receive RuntimeException types here // Need to unwrap to ascertain real causing exception before we choose to ignore - Throwable realCause = x.getCause(); // Ignore err-logging all transport-level/type exceptions - if ((realCause != null && realCause instanceof TTransportException) - || (x instanceof TTransportException)) { - if (LOGGER.isDebugEnabled()) { - // Write to debug, just in case the exception gets required - LOGGER - .debug("Received TTransportException during processing of message, ignoring: ", x); - } - } else { + if (!isIgnorableException(x)) { // Log the exception at error level and continue - LOGGER.error("Error occurred during processing of message.", x); + LOGGER.error((x instanceof TException ? "Thrift " : "") + "Error occurred during processing of message.", x); } } finally { - if (eventHandler != null) { - eventHandler.deleteContext(connectionContext, inputProtocol, outputProtocol); + if (eventHandler.isPresent()) { + eventHandler.get().deleteContext(connectionContext, inputProtocol, outputProtocol); } if (inputTransport != null) { inputTransport.close(); @@ -344,5 +272,24 @@ public void run() { } } } + + private boolean isIgnorableException(Exception x) { + TTransportException tTransportException = null; + + if (x instanceof TTransportException) { + tTransportException = (TTransportException) x; + } else if (x.getCause() instanceof TTransportException) { + tTransportException = (TTransportException) x.getCause(); + } + + if (tTransportException != null) { + switch(tTransportException.getType()) { + case TTransportException.END_OF_FILE: + case TTransportException.TIMED_OUT: + return true; + } + } + return false; + } } } diff --git a/lib/java/src/org/apache/thrift/server/TThreadedSelectorServer.java b/lib/java/src/org/apache/thrift/server/TThreadedSelectorServer.java index 038507e9c68..095aacbc5ce 100644 --- a/lib/java/src/org/apache/thrift/server/TThreadedSelectorServer.java +++ b/lib/java/src/org/apache/thrift/server/TThreadedSelectorServer.java @@ -457,7 +457,7 @@ public void run() { private TNonblockingTransport doAccept() { try { - return (TNonblockingTransport) serverTransport.accept(); + return serverTransport.accept(); } catch (TTransportException tte) { // something went wrong accepting. LOGGER.warn("Exception trying to accept!", tte); @@ -685,7 +685,7 @@ private void processAcceptedConnections() { protected FrameBuffer createFrameBuffer(final TNonblockingTransport trans, final SelectionKey selectionKey, - final AbstractSelectThread selectThread) { + final AbstractSelectThread selectThread) throws TTransportException { return processorFactory_.isAsyncProcessor() ? new AsyncFrameBuffer(trans, selectionKey, selectThread) : new FrameBuffer(trans, selectionKey, selectThread); @@ -699,7 +699,7 @@ private void registerAccepted(TNonblockingTransport accepted) { FrameBuffer frameBuffer = createFrameBuffer(accepted, clientKey, SelectorThread.this); clientKey.attach(frameBuffer); - } catch (IOException e) { + } catch (IOException | TTransportException e) { LOGGER.warn("Failed to register accepted connection to selector!", e); if (clientKey != null) { cleanupSelectionKey(clientKey); diff --git a/lib/java/src/org/apache/thrift/transport/AutoExpandingBuffer.java b/lib/java/src/org/apache/thrift/transport/AutoExpandingBuffer.java index b02905f3201..b355d11ca4e 100644 --- a/lib/java/src/org/apache/thrift/transport/AutoExpandingBuffer.java +++ b/lib/java/src/org/apache/thrift/transport/AutoExpandingBuffer.java @@ -18,6 +18,8 @@ */ package org.apache.thrift.transport; +import java.util.Arrays; + /** * Helper class that wraps a byte[] so that it can expand and be reused. Users * should call resizeIfNecessary to make sure the buffer has suitable capacity, @@ -28,25 +30,21 @@ public class AutoExpandingBuffer { private byte[] array; - private final double growthCoefficient; - - public AutoExpandingBuffer(int initialCapacity, double growthCoefficient) { - if (growthCoefficient < 1.0) { - throw new IllegalArgumentException("Growth coefficient must be >= 1.0"); - } - array = new byte[initialCapacity]; - this.growthCoefficient = growthCoefficient; + public AutoExpandingBuffer(int initialCapacity) { + this.array = new byte[initialCapacity]; } public void resizeIfNecessary(int size) { - if (array.length < size) { - byte[] newBuf = new byte[(int)(size * growthCoefficient)]; - System.arraycopy(array, 0, newBuf, 0, array.length); - array = newBuf; + final int currentCapacity = this.array.length; + if (currentCapacity < size) { + // Increase by a factor of 1.5x + int growCapacity = currentCapacity + (currentCapacity >> 1); + int newCapacity = Math.max(growCapacity, size); + this.array = Arrays.copyOf(array, newCapacity); } } public byte[] array() { - return array; + return this.array; } } diff --git a/lib/java/src/org/apache/thrift/transport/AutoExpandingBufferReadTransport.java b/lib/java/src/org/apache/thrift/transport/AutoExpandingBufferReadTransport.java index 081bc48a5be..6fd4075b9a8 100644 --- a/lib/java/src/org/apache/thrift/transport/AutoExpandingBufferReadTransport.java +++ b/lib/java/src/org/apache/thrift/transport/AutoExpandingBufferReadTransport.java @@ -18,18 +18,21 @@ */ package org.apache.thrift.transport; +import org.apache.thrift.TConfiguration; + /** * TTransport for reading from an AutoExpandingBuffer. */ -public class AutoExpandingBufferReadTransport extends TTransport { +public class AutoExpandingBufferReadTransport extends TEndpointTransport { private final AutoExpandingBuffer buf; private int pos = 0; private int limit = 0; - public AutoExpandingBufferReadTransport(int initialCapacity, double overgrowthCoefficient) { - this.buf = new AutoExpandingBuffer(initialCapacity, overgrowthCoefficient); + public AutoExpandingBufferReadTransport(TConfiguration config, int initialCapacity) throws TTransportException { + super(config); + this.buf = new AutoExpandingBuffer(initialCapacity); } public void fill(TTransport inTrans, int length) throws TTransportException { @@ -51,8 +54,10 @@ public void open() throws TTransportException {} @Override public final int read(byte[] target, int off, int len) throws TTransportException { int amtToRead = Math.min(len, getBytesRemainingInBuffer()); - System.arraycopy(buf.array(), pos, target, off, amtToRead); - consumeBuffer(amtToRead); + if(amtToRead > 0){ + System.arraycopy(buf.array(), pos, target, off, amtToRead); + consumeBuffer(amtToRead); + } return amtToRead; } @@ -81,4 +86,3 @@ public final int getBytesRemainingInBuffer() { return limit - pos; } } - \ No newline at end of file diff --git a/lib/java/src/org/apache/thrift/transport/AutoExpandingBufferWriteTransport.java b/lib/java/src/org/apache/thrift/transport/AutoExpandingBufferWriteTransport.java index 9b35693bac6..25f974a73cc 100644 --- a/lib/java/src/org/apache/thrift/transport/AutoExpandingBufferWriteTransport.java +++ b/lib/java/src/org/apache/thrift/transport/AutoExpandingBufferWriteTransport.java @@ -18,17 +18,39 @@ */ package org.apache.thrift.transport; +import org.apache.thrift.TConfiguration; + /** * TTransport for writing to an AutoExpandingBuffer. */ -public final class AutoExpandingBufferWriteTransport extends TTransport { +public final class AutoExpandingBufferWriteTransport extends TEndpointTransport { private final AutoExpandingBuffer buf; private int pos; + private int res; - public AutoExpandingBufferWriteTransport(int initialCapacity, double growthCoefficient) { - this.buf = new AutoExpandingBuffer(initialCapacity, growthCoefficient); - this.pos = 0; + /** + * Constructor. + * @param initialCapacity the initial capacity of the buffer + * @param frontReserve space, if any, to reserve at the beginning such + * that the first write is after this reserve. + * This allows framed transport to reserve space + * for the frame buffer length. + * @throws IllegalArgumentException if initialCapacity is less than one + * @throws IllegalArgumentException if frontReserve is less than zero + * @throws IllegalArgumentException if frontReserve is greater than initialCapacity + */ + public AutoExpandingBufferWriteTransport(TConfiguration config, int initialCapacity, int frontReserve) throws TTransportException { + super(config); + if (initialCapacity < 1) { + throw new IllegalArgumentException("initialCapacity"); + } + if (frontReserve < 0 || initialCapacity < frontReserve) { + throw new IllegalArgumentException("frontReserve"); + } + this.buf = new AutoExpandingBuffer(initialCapacity); + this.pos = frontReserve; + this.res = frontReserve; } @Override @@ -56,11 +78,14 @@ public AutoExpandingBuffer getBuf() { return buf; } - public int getPos() { + /** + * @return length of the buffer, including any front reserve + */ + public int getLength() { return pos; } public void reset() { - pos = 0; + pos = res; } } diff --git a/lib/java/src/org/apache/thrift/transport/TByteBuffer.java b/lib/java/src/org/apache/thrift/transport/TByteBuffer.java index b6b065748cf..c792f3b1c35 100644 --- a/lib/java/src/org/apache/thrift/transport/TByteBuffer.java +++ b/lib/java/src/org/apache/thrift/transport/TByteBuffer.java @@ -1,5 +1,7 @@ package org.apache.thrift.transport; +import org.apache.thrift.TConfiguration; + import java.nio.BufferOverflowException; import java.nio.BufferUnderflowException; import java.nio.ByteBuffer; @@ -7,14 +9,16 @@ /** * ByteBuffer-backed implementation of TTransport. */ -public final class TByteBuffer extends TTransport { +public final class TByteBuffer extends TEndpointTransport { private final ByteBuffer byteBuffer; /** * Creates a new TByteBuffer wrapping a given NIO ByteBuffer. */ - public TByteBuffer(ByteBuffer byteBuffer) { + public TByteBuffer(ByteBuffer byteBuffer) throws TTransportException { + super(new TConfiguration()); this.byteBuffer = byteBuffer; + updateKnownMessageSize(byteBuffer.capacity()); } @Override @@ -32,6 +36,9 @@ public void close() { @Override public int read(byte[] buf, int off, int len) throws TTransportException { + // + checkReadBytesAvailable(len); + final int n = Math.min(byteBuffer.remaining(), len); if (n > 0) { try { diff --git a/lib/csharp/src/Protocol/TBase.cs b/lib/java/src/org/apache/thrift/transport/TEOFException.java similarity index 75% rename from lib/csharp/src/Protocol/TBase.cs rename to lib/java/src/org/apache/thrift/transport/TEOFException.java index 411e4d95f6d..b5ae6eff463 100644 --- a/lib/csharp/src/Protocol/TBase.cs +++ b/lib/java/src/org/apache/thrift/transport/TEOFException.java @@ -1,4 +1,4 @@ -/** +/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information @@ -17,13 +17,14 @@ * under the License. */ -namespace Thrift.Protocol -{ - public interface TBase : TAbstractBase - { - /// - /// Reads the TObject from the given input protocol. - /// - void Read(TProtocol tProtocol); - } +package org.apache.thrift.transport; + +/** + * End of file, especially, the underlying socket is closed. + */ +public class TEOFException extends TTransportException { + + public TEOFException(String message) { + super(TTransportException.END_OF_FILE, message); + } } diff --git a/lib/java/src/org/apache/thrift/transport/TEndpointTransport.java b/lib/java/src/org/apache/thrift/transport/TEndpointTransport.java new file mode 100644 index 00000000000..f32efae9081 --- /dev/null +++ b/lib/java/src/org/apache/thrift/transport/TEndpointTransport.java @@ -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. + */ +package org.apache.thrift.transport; + +import org.apache.thrift.TConfiguration; + +import java.util.Objects; + +public abstract class TEndpointTransport extends TTransport{ + + protected long getMaxMessageSize() { return getConfiguration().getMaxMessageSize(); } + + protected long knownMessageSize; + protected long remainingMessageSize; + + private TConfiguration _configuration; + public TConfiguration getConfiguration() { + return _configuration; + } + + public TEndpointTransport( TConfiguration config) throws TTransportException { + _configuration = Objects.isNull(config) ? new TConfiguration() : config; + + resetConsumedMessageSize(-1); + } + + /** + * Resets RemainingMessageSize to the configured maximum + * @param newSize + */ + protected void resetConsumedMessageSize(long newSize) throws TTransportException { + // full reset + if (newSize < 0) + { + knownMessageSize = getMaxMessageSize(); + remainingMessageSize = getMaxMessageSize(); + return; + } + + // update only: message size can shrink, but not grow + if (newSize > knownMessageSize) + throw new TTransportException(TTransportException.END_OF_FILE, "MaxMessageSize reached"); + + knownMessageSize = newSize; + remainingMessageSize = newSize; + } + + /** + * Updates RemainingMessageSize to reflect then known real message size (e.g. framed transport). + * Will throw if we already consumed too many bytes or if the new size is larger than allowed. + * @param size + */ + public void updateKnownMessageSize(long size) throws TTransportException { + long consumed = knownMessageSize - remainingMessageSize; + resetConsumedMessageSize(size == 0 ? -1 : size); + countConsumedMessageBytes(consumed); + } + + /** + * Throws if there are not enough bytes in the input stream to satisfy a read of numBytes bytes of data + * @param numBytes + */ + public void checkReadBytesAvailable(long numBytes) throws TTransportException { + if (remainingMessageSize < numBytes) + throw new TTransportException(TTransportException.END_OF_FILE, "MaxMessageSize reached"); + } + + /** + * Consumes numBytes from the RemainingMessageSize. + * @param numBytes + */ + protected void countConsumedMessageBytes(long numBytes) throws TTransportException { + if (remainingMessageSize >= numBytes) + { + remainingMessageSize -= numBytes; + } + else + { + remainingMessageSize = 0; + throw new TTransportException(TTransportException.END_OF_FILE, "MaxMessageSize reached"); + } + } + +} diff --git a/lib/java/src/org/apache/thrift/transport/TFileTransport.java b/lib/java/src/org/apache/thrift/transport/TFileTransport.java index c011c52a794..85f97084cb7 100644 --- a/lib/java/src/org/apache/thrift/transport/TFileTransport.java +++ b/lib/java/src/org/apache/thrift/transport/TFileTransport.java @@ -26,20 +26,26 @@ import java.io.IOException; import java.util.Random; +import org.apache.thrift.TConfiguration; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + /** * FileTransport implementation of the TTransport interface. * Currently this is a straightforward port of the cpp implementation - * + * * It may make better sense to provide a basic stream access on top of the framed file format * The FileTransport can then be a user of this framed file format with some additional logic * for chunking. */ public class TFileTransport extends TTransport { + private static final Logger LOGGER = LoggerFactory.getLogger(TFileTransport.class.getName()); + public static class TruncableBufferedInputStream extends BufferedInputStream { public void trunc() { pos = count = 0; - } + } public TruncableBufferedInputStream(InputStream in) { super(in); } @@ -57,7 +63,7 @@ public static class Event { /** * Initialize an event. Initially, it has no valid contents * - * @param buf byte array buffer to store event + * @param buf byte array buffer to store event */ public Event(byte[] buf) { buf_ = buf; @@ -83,9 +89,9 @@ public int emit(byte[] buf, int offset, int ndesired) { return(ndesired); } - }; + } - public static class ChunkState { + public static class ChunkState { /** * Chunk Size. Must be same across all implementations */ @@ -106,7 +112,7 @@ public ChunkState() {} public long getOffset() { return (offset_);} } - public static enum TailPolicy { + public enum TailPolicy { NOWAIT(0, 0), WAIT_FOREVER(500, -1); @@ -143,13 +149,13 @@ public static enum TailPolicy { TailPolicy currentPolicy_ = TailPolicy.NOWAIT; - /** + /** * Underlying file being read */ protected TSeekableFile inputFile_ = null; - /** - * Underlying outputStream + /** + * Underlying outputStream */ protected OutputStream outputStream_ = null; @@ -176,7 +182,7 @@ public static enum TailPolicy { /** * Get File Tailing Policy - * + * * @return current read policy */ public TailPolicy getTailPolicy() { @@ -185,7 +191,7 @@ public TailPolicy getTailPolicy() { /** * Set file Tailing Policy - * + * * @param policy New policy to set * @return Old policy */ @@ -198,7 +204,7 @@ public TailPolicy setTailPolicy(TailPolicy policy) { /** * Initialize read input stream - * + * * @return input stream to read from file */ private InputStream createInputStream() throws TTransportException { @@ -211,7 +217,6 @@ private InputStream createInputStream() throws TTransportException { is = new TruncableBufferedInputStream(inputFile_.getInputStream()); } } catch (IOException iox) { - System.err.println("createInputStream: "+iox.getMessage()); throw new TTransportException(iox.getMessage(), iox); } return(is); @@ -219,7 +224,7 @@ private InputStream createInputStream() throws TTransportException { /** * Read (potentially tailing) an input stream - * + * * @param is InputStream to read from * @param buf Buffer to read into * @param off Offset in buffer to read into @@ -228,7 +233,7 @@ private InputStream createInputStream() throws TTransportException { * * @return number of bytes read */ - private int tailRead(InputStream is, byte[] buf, + private int tailRead(InputStream is, byte[] buf, int off, int len, TailPolicy tp) throws TTransportException { int orig_len = len; try { @@ -318,7 +323,7 @@ private boolean readEvent() throws TTransportException { // check if event is corrupted and do recovery as required if(esize > cs.getRemaining()) { throw new TTransportException("FileTransport error: bad event size"); - /* + /* if(performRecovery()) { esize=0; } else { @@ -357,7 +362,7 @@ public boolean isOpen() { * Files are not opened in ctor - but in explicit open call */ public void open() throws TTransportException { - if (isOpen()) + if (isOpen()) throw new TTransportException(TTransportException.ALREADY_OPEN); try { @@ -380,7 +385,7 @@ public void close() { try { inputFile_.close(); } catch (IOException iox) { - System.err.println("WARNING: Error closing input file: " + + LOGGER.warn("WARNING: Error closing input file: " + iox.getMessage()); } inputFile_ = null; @@ -389,7 +394,7 @@ public void close() { try { outputStream_.close(); } catch (IOException iox) { - System.err.println("WARNING: Error closing output stream: " + + LOGGER.warn("WARNING: Error closing output stream: " + iox.getMessage()); } outputStream_ = null; @@ -402,7 +407,7 @@ public void close() { * * @param path File path to read and write from * @param readOnly Whether this is a read-only transport - */ + */ public TFileTransport(final String path, boolean readOnly) throws IOException { inputFile_ = new TStandardFile(path); readOnly_ = readOnly; @@ -453,8 +458,8 @@ public int readAll(byte[] buf, int off, int len) * @throws TTransportException if there was an error reading data */ public int read(byte[] buf, int off, int len) throws TTransportException { - if(!isOpen()) - throw new TTransportException(TTransportException.NOT_OPEN, + if(!isOpen()) + throw new TTransportException(TTransportException.NOT_OPEN, "Must open before reading"); if(currentEvent_.getRemaining() == 0) { @@ -467,14 +472,14 @@ public int read(byte[] buf, int off, int len) throws TTransportException { } public int getNumChunks() throws TTransportException { - if(!isOpen()) - throw new TTransportException(TTransportException.NOT_OPEN, + if(!isOpen()) + throw new TTransportException(TTransportException.NOT_OPEN, "Must open before getNumChunks"); try { long len = inputFile_.length(); if(len == 0) return 0; - else + else return (((int)(len/cs.getChunkSize())) + 1); } catch (IOException iox) { @@ -483,8 +488,8 @@ public int getNumChunks() throws TTransportException { } public int getCurChunk() throws TTransportException { - if(!isOpen()) - throw new TTransportException(TTransportException.NOT_OPEN, + if(!isOpen()) + throw new TTransportException(TTransportException.NOT_OPEN, "Must open before getCurChunk"); return (cs.getChunkNum()); @@ -492,8 +497,8 @@ public int getCurChunk() throws TTransportException { public void seekToChunk(int chunk) throws TTransportException { - if(!isOpen()) - throw new TTransportException(TTransportException.NOT_OPEN, + if(!isOpen()) + throw new TTransportException(TTransportException.NOT_OPEN, "Must open before seeking"); int numChunks = getNumChunks(); @@ -523,9 +528,8 @@ public void seekToChunk(int chunk) throws TTransportException { } if(chunk*cs.getChunkSize() != cs.getOffset()) { - try { inputFile_.seek((long)chunk*cs.getChunkSize()); } + try { inputFile_.seek((long)chunk*cs.getChunkSize()); } catch (IOException iox) { - System.err.println("createInputStream: "+iox.getMessage()); throw new TTransportException("Seek to chunk " + chunk + " " +iox.getMessage(), iox); } @@ -546,8 +550,8 @@ public void seekToChunk(int chunk) throws TTransportException { } public void seekToEnd() throws TTransportException { - if(!isOpen()) - throw new TTransportException(TTransportException.NOT_OPEN, + if(!isOpen()) + throw new TTransportException(TTransportException.NOT_OPEN, "Must open before seeking"); seekToChunk(getNumChunks()); } @@ -574,9 +578,25 @@ public void flush() throws TTransportException { throw new TTransportException("Not Supported"); } + + @Override + public TConfiguration getConfiguration() { + return null; + } + + @Override + public void updateKnownMessageSize(long size) throws TTransportException { + + } + + @Override + public void checkReadBytesAvailable(long numBytes) throws TTransportException { + + } + /** * test program - * + * */ public static void main(String[] args) throws Exception { @@ -591,20 +611,20 @@ public static void main(String[] args) throws Exception { try { num_chunks = Integer.parseInt(args[1]); } catch (Exception e) { - System.err.println("Cannot parse " + args[1]); + LOGGER.error("Cannot parse " + args[1]); printUsage(); } } TFileTransport t = new TFileTransport(args[0], true); t.open(); - System.out.println("NumChunks="+t.getNumChunks()); + LOGGER.info("NumChunks="+t.getNumChunks()); Random r = new Random(); for(int j=0; j [num_chunks]"); - System.err.println(" (Opens and reads num_chunks chunks from file randomly)"); + LOGGER.error("Usage: TFileTransport [num_chunks]"); + LOGGER.error(" (Opens and reads num_chunks chunks from file randomly)"); System.exit(1); } diff --git a/lib/java/src/org/apache/thrift/transport/THttpClient.java b/lib/java/src/org/apache/thrift/transport/THttpClient.java index c3063fe4336..7d61b5c8e72 100644 --- a/lib/java/src/org/apache/thrift/transport/THttpClient.java +++ b/lib/java/src/org/apache/thrift/transport/THttpClient.java @@ -37,6 +37,7 @@ import org.apache.http.client.methods.HttpPost; import org.apache.http.entity.ByteArrayEntity; import org.apache.http.params.CoreConnectionPNames; +import org.apache.thrift.TConfiguration; /** * HTTP implementation of the TTransport interface. Used for working with a @@ -51,7 +52,7 @@ * HttpClient to THttpClient(String url, HttpClient client) will create an * instance which will use HttpURLConnection. * - * When using HttpClient, the following configuration leads to 5-15% + * When using HttpClient, the following configuration leads to 5-15% * better performance than the HttpURLConnection implementation: * * http.protocol.version=HttpVersion.HTTP_1_1 @@ -65,7 +66,7 @@ * @see THRIFT-970 */ -public class THttpClient extends TTransport { +public class THttpClient extends TEndpointTransport { private URL url_ = null; @@ -80,14 +81,14 @@ public class THttpClient extends TTransport { private Map customHeaders_ = null; private final HttpHost host; - + private final HttpClient client; - + public static class Factory extends TTransportFactory { - + private final String url; private final HttpClient client; - + public Factory(String url) { this.url = url; this.client = null; @@ -97,14 +98,14 @@ public Factory(String url, HttpClient client) { this.url = url; this.client = client; } - + @Override public TTransport getTransport(TTransport trans) { try { if (null != client) { - return new THttpClient(url, client); + return new THttpClient(trans.getConfiguration(), url, client); } else { - return new THttpClient(url); + return new THttpClient(trans.getConfiguration(), url); } } catch (TTransportException tte) { return null; @@ -112,7 +113,19 @@ public TTransport getTransport(TTransport trans) { } } + public THttpClient(TConfiguration config, String url) throws TTransportException { + super(config); + try { + url_ = new URL(url); + this.client = null; + this.host = null; + } catch (IOException iox) { + throw new TTransportException(iox); + } + } + public THttpClient(String url) throws TTransportException { + super(new TConfiguration()); try { url_ = new URL(url); this.client = null; @@ -122,7 +135,19 @@ public THttpClient(String url) throws TTransportException { } } + public THttpClient(TConfiguration config, String url, HttpClient client) throws TTransportException { + super(config); + try { + url_ = new URL(url); + this.client = client; + this.host = new HttpHost(url_.getHost(), -1 == url_.getPort() ? url_.getDefaultPort() : url_.getPort(), url_.getProtocol()); + } catch (IOException iox) { + throw new TTransportException(iox); + } + } + public THttpClient(String url, HttpClient client) throws TTransportException { + super(new TConfiguration()); try { url_ = new URL(url); this.client = client; @@ -168,7 +193,6 @@ public void close() { try { inputStream_.close(); } catch (IOException ioe) { - ; } inputStream_ = null; } @@ -182,11 +206,16 @@ public int read(byte[] buf, int off, int len) throws TTransportException { if (inputStream_ == null) { throw new TTransportException("Response buffer is empty, no request."); } + + checkReadBytesAvailable(len); + try { int ret = inputStream_.read(buf, off, len); if (ret == -1) { throw new TTransportException("No more data available."); } + countConsumedMessageBytes(ret); + return ret; } catch (IOException iox) { throw new TTransportException(iox); @@ -214,7 +243,7 @@ private static void consume(final HttpEntity entity) throws IOException { } private void flushUsingHttpClient() throws TTransportException { - + if (null == this.client) { throw new TTransportException("Null HttpClient, aborting."); } @@ -224,22 +253,22 @@ private void flushUsingHttpClient() throws TTransportException { requestBuffer_.reset(); HttpPost post = null; - + InputStream is = null; - - try { + + try { // Set request to path + query string post = new HttpPost(this.url_.getFile()); - + // // Headers are added to the HttpPost instance, not // to HttpClient. // - + post.setHeader("Content-Type", "application/x-thrift"); post.setHeader("Accept", "application/x-thrift"); post.setHeader("User-Agent", "Java/THttpClient/HC"); - + if (null != customHeaders_) { for (Map.Entry header : customHeaders_.entrySet()) { post.setHeader(header.getKey(), header.getValue()); @@ -247,17 +276,17 @@ private void flushUsingHttpClient() throws TTransportException { } post.setEntity(new ByteArrayEntity(data)); - + HttpResponse response = this.client.execute(this.host, post); int responseCode = response.getStatusLine().getStatusCode(); - // + // // Retrieve the inputstream BEFORE checking the status code so // resources get freed in the finally clause. // is = response.getEntity().getContent(); - + if (responseCode != HttpStatus.SC_OK) { throw new TTransportException("HTTP Response code: " + responseCode); } @@ -268,10 +297,10 @@ private void flushUsingHttpClient() throws TTransportException { // thrift struct is being read up the chain). // Proceeding differently might lead to exhaustion of connections and thus // to app failure. - + byte[] buf = new byte[1024]; ByteArrayOutputStream baos = new ByteArrayOutputStream(); - + int len = 0; do { len = is.read(buf); @@ -279,7 +308,7 @@ private void flushUsingHttpClient() throws TTransportException { baos.write(buf, 0, len); } } while (-1 != len); - + try { // Indicate we're done with the content. consume(response.getEntity()); @@ -287,7 +316,7 @@ private void flushUsingHttpClient() throws TTransportException { // We ignore this exception, it might only mean the server has no // keep-alive capability. } - + inputStream_ = new ByteArrayInputStream(baos.toByteArray()); } catch (IOException ioe) { // Abort method so the connection gets released back to the connection manager @@ -296,6 +325,7 @@ private void flushUsingHttpClient() throws TTransportException { } throw new TTransportException(ioe); } finally { + resetConsumedMessageSize(-1); if (null != is) { // Close the entity's input stream, this will release the underlying connection try { @@ -357,6 +387,8 @@ public void flush() throws TTransportException { } catch (IOException iox) { throw new TTransportException(iox); + } finally { + resetConsumedMessageSize(-1); } } } diff --git a/lib/java/src/org/apache/thrift/transport/TIOStreamTransport.java b/lib/java/src/org/apache/thrift/transport/TIOStreamTransport.java index c8cd1189996..763e66ad694 100644 --- a/lib/java/src/org/apache/thrift/transport/TIOStreamTransport.java +++ b/lib/java/src/org/apache/thrift/transport/TIOStreamTransport.java @@ -19,6 +19,7 @@ package org.apache.thrift.transport; +import org.apache.thrift.TConfiguration; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -27,13 +28,13 @@ import java.io.OutputStream; /** - * This is the most commonly used base transport. It takes an InputStream - * and an OutputStream and uses those to perform all transport operations. + * This is the most commonly used base transport. It takes an InputStream or + * an OutputStream or both and uses it/them to perform transport operations. * This allows for compatibility with all the nice constructs Java already * has to provide a variety of types of streams. * */ -public class TIOStreamTransport extends TTransport { +public class TIOStreamTransport extends TEndpointTransport { private static final Logger LOGGER = LoggerFactory.getLogger(TIOStreamTransport.class.getName()); @@ -47,23 +48,69 @@ public class TIOStreamTransport extends TTransport { * Subclasses can invoke the default constructor and then assign the input * streams in the open method. */ - protected TIOStreamTransport() {} + protected TIOStreamTransport(TConfiguration config) throws TTransportException { + super(config); + } /** - * Input stream constructor. + * Subclasses can invoke the default constructor and then assign the input + * streams in the open method. + */ + protected TIOStreamTransport() throws TTransportException { + super(new TConfiguration()); + } + + /** + * Input stream constructor, constructs an input only transport. * + * @param config * @param is Input stream to read from */ - public TIOStreamTransport(InputStream is) { + public TIOStreamTransport(TConfiguration config, InputStream is) throws TTransportException { + super(config); inputStream_ = is; } + /** + * Input stream constructor, constructs an input only transport. + * + * @param is Input stream to read from + */ + public TIOStreamTransport(InputStream is) throws TTransportException { + super(new TConfiguration()); + inputStream_ = is; + } + + /** + * Output stream constructor, constructs an output only transport. + * + * @param config + * @param os Output stream to write to + */ + public TIOStreamTransport(TConfiguration config, OutputStream os) throws TTransportException { + super(config); + outputStream_ = os; + } /** - * Output stream constructor. + * Output stream constructor, constructs an output only transport. + * + * @param os Output stream to write to + */ + public TIOStreamTransport(OutputStream os) throws TTransportException { + super(new TConfiguration()); + outputStream_ = os; + } + + /** + * Two-way stream constructor. * + * @param config + * @param is Input stream to read from * @param os Output stream to read from */ - public TIOStreamTransport(OutputStream os) { + public TIOStreamTransport(TConfiguration config, InputStream is, OutputStream os) throws TTransportException { + super(config); + inputStream_ = is; outputStream_ = os; } @@ -73,19 +120,18 @@ public TIOStreamTransport(OutputStream os) { * @param is Input stream to read from * @param os Output stream to read from */ - public TIOStreamTransport(InputStream is, OutputStream os) { + public TIOStreamTransport(InputStream is, OutputStream os) throws TTransportException { + super(new TConfiguration()); inputStream_ = is; outputStream_ = os; } /** - * The streams must already be open at construction time, so this should - * always return true. * - * @return true + * @return false after close is called. */ public boolean isOpen() { - return true; + return inputStream_ != null || outputStream_ != null; } /** @@ -97,20 +143,23 @@ public void open() throws TTransportException {} * Closes both the input and output streams. */ public void close() { - if (inputStream_ != null) { - try { - inputStream_.close(); - } catch (IOException iox) { - LOGGER.warn("Error closing input stream.", iox); + try { + if (inputStream_ != null) { + try { + inputStream_.close(); + } catch (IOException iox) { + LOGGER.warn("Error closing input stream.", iox); + } } - inputStream_ = null; - } - if (outputStream_ != null) { - try { - outputStream_.close(); - } catch (IOException iox) { - LOGGER.warn("Error closing output stream.", iox); + if (outputStream_ != null) { + try { + outputStream_.close(); + } catch (IOException iox) { + LOGGER.warn("Error closing output stream.", iox); + } } + } finally { + inputStream_ = null; outputStream_ = null; } } @@ -129,7 +178,7 @@ public int read(byte[] buf, int off, int len) throws TTransportException { throw new TTransportException(TTransportException.UNKNOWN, iox); } if (bytesRead < 0) { - throw new TTransportException(TTransportException.END_OF_FILE); + throw new TTransportException(TTransportException.END_OF_FILE, "Socket is closed by peer."); } return bytesRead; } @@ -157,6 +206,9 @@ public void flush() throws TTransportException { } try { outputStream_.flush(); + + resetConsumedMessageSize(-1); + } catch (IOException iox) { throw new TTransportException(TTransportException.UNKNOWN, iox); } diff --git a/lib/java/src/org/apache/thrift/transport/TMemoryBuffer.java b/lib/java/src/org/apache/thrift/transport/TMemoryBuffer.java index ef5f5c28e04..c3a3eb4e17f 100644 --- a/lib/java/src/org/apache/thrift/transport/TMemoryBuffer.java +++ b/lib/java/src/org/apache/thrift/transport/TMemoryBuffer.java @@ -20,19 +20,39 @@ package org.apache.thrift.transport; import org.apache.thrift.TByteArrayOutputStream; -import java.io.UnsupportedEncodingException; +import org.apache.thrift.TConfiguration; + +import java.nio.charset.Charset; /** * Memory buffer-based implementation of the TTransport interface. */ -public class TMemoryBuffer extends TTransport { +public class TMemoryBuffer extends TEndpointTransport { + /** + * Create a TMemoryBuffer with an initial buffer size of size. The + * internal buffer will grow as necessary to accommodate the size of the data + * being written to it. + * + * @param size the initial size of the buffer + */ + public TMemoryBuffer(int size) throws TTransportException { + super(new TConfiguration()); + arr_ = new TByteArrayOutputStream(size); + updateKnownMessageSize(size); + } + /** * Create a TMemoryBuffer with an initial buffer size of size. The * internal buffer will grow as necessary to accommodate the size of the data * being written to it. + * + * @param config + * @param size the initial size of the buffer */ - public TMemoryBuffer(int size) { + public TMemoryBuffer(TConfiguration config, int size) throws TTransportException { + super(config); arr_ = new TByteArrayOutputStream(size); + updateKnownMessageSize(size); } @Override @@ -51,9 +71,11 @@ public void close() { } @Override - public int read(byte[] buf, int off, int len) { + public int read(byte[] buf, int off, int len) throws TTransportException { + checkReadBytesAvailable(len); byte[] src = arr_.get(); int amtToRead = (len > arr_.len() - pos_ ? arr_.len() - pos_ : len); + if (amtToRead > 0) { System.arraycopy(src, pos_, buf, off, amtToRead); pos_ += amtToRead; @@ -69,11 +91,11 @@ public void write(byte[] buf, int off, int len) { /** * Output the contents of the memory buffer as a String, using the supplied * encoding - * @param enc the encoding to use + * @param charset the encoding to use * @return the contents of the memory buffer as a String */ - public String toString(String enc) throws UnsupportedEncodingException { - return arr_.toString(enc); + public String toString(Charset charset) { + return arr_.toString(charset); } public String inspect() { diff --git a/lib/java/src/org/apache/thrift/transport/TMemoryInputTransport.java b/lib/java/src/org/apache/thrift/transport/TMemoryInputTransport.java index 2530dcc3697..6cb06fc37ac 100644 --- a/lib/java/src/org/apache/thrift/transport/TMemoryInputTransport.java +++ b/lib/java/src/org/apache/thrift/transport/TMemoryInputTransport.java @@ -18,21 +18,38 @@ */ package org.apache.thrift.transport; -public final class TMemoryInputTransport extends TTransport { +import org.apache.thrift.TConfiguration; + +public final class TMemoryInputTransport extends TEndpointTransport { private byte[] buf_; private int pos_; private int endPos_; - public TMemoryInputTransport() { + public TMemoryInputTransport() throws TTransportException { + this(new TConfiguration()); + } + + public TMemoryInputTransport(TConfiguration _configuration) throws TTransportException { + this(_configuration, new byte[0]); + } + + public TMemoryInputTransport(byte[] buf) throws TTransportException { + this(new TConfiguration(), buf); } - public TMemoryInputTransport(byte[] buf) { - reset(buf); + public TMemoryInputTransport(TConfiguration _configuration, byte[] buf) throws TTransportException { + this(_configuration, buf, 0, buf.length); } - public TMemoryInputTransport(byte[] buf, int offset, int length) { + public TMemoryInputTransport(byte[] buf, int offset, int length) throws TTransportException { + this(new TConfiguration(), buf, offset, length); + } + + public TMemoryInputTransport(TConfiguration _configuration, byte[] buf, int offset, int length) throws TTransportException { + super(_configuration); reset(buf, offset, length); + updateKnownMessageSize(length); } public void reset(byte[] buf) { @@ -43,10 +60,20 @@ public void reset(byte[] buf, int offset, int length) { buf_ = buf; pos_ = offset; endPos_ = offset + length; + try { + resetConsumedMessageSize(-1); + } catch (TTransportException e) { + // ignore + } } public void clear() { buf_ = null; + try { + resetConsumedMessageSize(-1); + } catch (TTransportException e) { + // ignore + } } @Override @@ -67,6 +94,7 @@ public int read(byte[] buf, int off, int len) throws TTransportException { if (amtToRead > 0) { System.arraycopy(buf_, pos_, buf, off, amtToRead); consumeBuffer(amtToRead); + countConsumedMessageBytes(amtToRead); } return amtToRead; } diff --git a/lib/java/src/org/apache/thrift/transport/TMemoryTransport.java b/lib/java/src/org/apache/thrift/transport/TMemoryTransport.java new file mode 100644 index 00000000000..0172ca81685 --- /dev/null +++ b/lib/java/src/org/apache/thrift/transport/TMemoryTransport.java @@ -0,0 +1,92 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.thrift.transport; + +import java.nio.ByteBuffer; + +import org.apache.thrift.TByteArrayOutputStream; +import org.apache.thrift.TConfiguration; + +/** + * In memory transport with separate buffers for input and output. + */ +public class TMemoryTransport extends TEndpointTransport { + + private final ByteBuffer inputBuffer; + private final TByteArrayOutputStream outputBuffer; + + public TMemoryTransport(byte[] input) throws TTransportException { + super(new TConfiguration()); + inputBuffer = ByteBuffer.wrap(input); + outputBuffer = new TByteArrayOutputStream(1024); + updateKnownMessageSize(input.length); + } + + public TMemoryTransport(TConfiguration config, byte[] input) throws TTransportException { + super(config); + inputBuffer = ByteBuffer.wrap(input); + outputBuffer = new TByteArrayOutputStream(1024); + updateKnownMessageSize(input.length); + } + + @Override + public boolean isOpen() { + return true; + } + + /** + * Opening on an in memory transport should have no effect. + */ + @Override + public void open() { + // Do nothing. + } + + @Override + public void close() { + // Do nothing. + } + + @Override + public int read(byte[] buf, int off, int len) throws TTransportException { + checkReadBytesAvailable(len); + int remaining = inputBuffer.remaining(); + if (remaining < len) { + throw new TTransportException(TTransportException.END_OF_FILE, + "There's only " + remaining + "bytes, but it asks for " + len); + } + inputBuffer.get(buf, off, len); + return len; + } + + @Override + public void write(byte[] buf, int off, int len) throws TTransportException { + outputBuffer.write(buf, off, len); + } + + /** + * Get all the bytes written by thrift output protocol. + * + * @return a byte array. + */ + public TByteArrayOutputStream getOutput() { + return outputBuffer; + } +} diff --git a/lib/java/src/org/apache/thrift/transport/TNonblockingServerSocket.java b/lib/java/src/org/apache/thrift/transport/TNonblockingServerSocket.java index ef82ac2c4fb..1631892334f 100644 --- a/lib/java/src/org/apache/thrift/transport/TNonblockingServerSocket.java +++ b/lib/java/src/org/apache/thrift/transport/TNonblockingServerSocket.java @@ -103,12 +103,13 @@ public void listen() throws TTransportException { try { serverSocket_.setSoTimeout(0); } catch (SocketException sx) { - sx.printStackTrace(); + LOGGER.error("Socket exception while setting socket timeout", sx); } } } - protected TNonblockingSocket acceptImpl() throws TTransportException { + @Override + public TNonblockingSocket accept() throws TTransportException { if (serverSocket_ == null) { throw new TTransportException(TTransportException.NOT_OPEN, "No underlying server socket."); } @@ -160,4 +161,9 @@ public int getPort() { return serverSocket_.getLocalPort(); } + // Expose it for test purpose. + ServerSocketChannel getServerSocketChannel() { + return serverSocketChannel; + } + } diff --git a/lib/java/src/org/apache/thrift/transport/TNonblockingServerTransport.java b/lib/java/src/org/apache/thrift/transport/TNonblockingServerTransport.java index ba45b09dcff..daac0d574b4 100644 --- a/lib/java/src/org/apache/thrift/transport/TNonblockingServerTransport.java +++ b/lib/java/src/org/apache/thrift/transport/TNonblockingServerTransport.java @@ -28,4 +28,12 @@ public abstract class TNonblockingServerTransport extends TServerTransport { public abstract void registerSelector(Selector selector); + + /** + * + * @return an incoming connection or null if there is none. + * @throws TTransportException + */ + @Override + public abstract TNonblockingTransport accept() throws TTransportException; } diff --git a/lib/java/src/org/apache/thrift/transport/TNonblockingSocket.java b/lib/java/src/org/apache/thrift/transport/TNonblockingSocket.java index f86a48b4285..13c8586480f 100644 --- a/lib/java/src/org/apache/thrift/transport/TNonblockingSocket.java +++ b/lib/java/src/org/apache/thrift/transport/TNonblockingSocket.java @@ -30,6 +30,7 @@ import java.nio.channels.Selector; import java.nio.channels.SocketChannel; +import org.apache.thrift.TConfiguration; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -47,7 +48,7 @@ public class TNonblockingSocket extends TNonblockingTransport { private final SocketChannel socketChannel_; - public TNonblockingSocket(String host, int port) throws IOException { + public TNonblockingSocket(String host, int port) throws IOException, TTransportException { this(host, port, 0); } @@ -57,7 +58,7 @@ public TNonblockingSocket(String host, int port) throws IOException { * @param port * @throws IOException */ - public TNonblockingSocket(String host, int port, int timeout) throws IOException { + public TNonblockingSocket(String host, int port, int timeout) throws IOException, TTransportException { this(SocketChannel.open(), timeout, new InetSocketAddress(host, port)); } @@ -67,13 +68,19 @@ public TNonblockingSocket(String host, int port, int timeout) throws IOException * @param socketChannel Already created SocketChannel object * @throws IOException if there is an error setting up the streams */ - public TNonblockingSocket(SocketChannel socketChannel) throws IOException { + public TNonblockingSocket(SocketChannel socketChannel) throws IOException, TTransportException { this(socketChannel, 0, null); if (!socketChannel.isConnected()) throw new IOException("Socket must already be connected"); } private TNonblockingSocket(SocketChannel socketChannel, int timeout, SocketAddress socketAddress) - throws IOException { + throws IOException, TTransportException { + this(new TConfiguration(), socketChannel, timeout, socketAddress); + } + + private TNonblockingSocket(TConfiguration config, SocketChannel socketChannel, int timeout, SocketAddress socketAddress) + throws IOException, TTransportException { + super(config); socketChannel_ = socketChannel; socketAddress_ = socketAddress; @@ -137,11 +144,14 @@ public void open() throws TTransportException { /** * Perform a nonblocking read into buffer. */ - public int read(ByteBuffer buffer) throws IOException { - return socketChannel_.read(buffer); + public int read(ByteBuffer buffer) throws TTransportException { + try { + return socketChannel_.read(buffer); + } catch (IOException iox) { + throw new TTransportException(TTransportException.UNKNOWN, iox); + } } - /** * Reads from the underlying input stream if not null. */ @@ -160,8 +170,12 @@ public int read(byte[] buf, int off, int len) throws TTransportException { /** * Perform a nonblocking write of the data in buffer; */ - public int write(ByteBuffer buffer) throws IOException { - return socketChannel_.write(buffer); + public int write(ByteBuffer buffer) throws TTransportException { + try { + return socketChannel_.write(buffer); + } catch (IOException iox) { + throw new TTransportException(TTransportException.UNKNOWN, iox); + } } /** @@ -172,11 +186,7 @@ public void write(byte[] buf, int off, int len) throws TTransportException { throw new TTransportException(TTransportException.NOT_OPEN, "Cannot write to write-only socket channel"); } - try { - socketChannel_.write(ByteBuffer.wrap(buf, off, len)); - } catch (IOException iox) { - throw new TTransportException(TTransportException.UNKNOWN, iox); - } + write(ByteBuffer.wrap(buf, off, len)); } /** @@ -207,4 +217,9 @@ public boolean finishConnect() throws IOException { return socketChannel_.finishConnect(); } + @Override + public String toString() { + return "[remote: " + socketChannel_.socket().getRemoteSocketAddress() + + ", local: " + socketChannel_.socket().getLocalAddress() + "]" ; + } } diff --git a/lib/java/src/org/apache/thrift/transport/TNonblockingTransport.java b/lib/java/src/org/apache/thrift/transport/TNonblockingTransport.java index 43c130688de..30ec9d25c05 100644 --- a/lib/java/src/org/apache/thrift/transport/TNonblockingTransport.java +++ b/lib/java/src/org/apache/thrift/transport/TNonblockingTransport.java @@ -19,13 +19,18 @@ package org.apache.thrift.transport; +import org.apache.thrift.TConfiguration; + import java.io.IOException; import java.net.SocketAddress; -import java.nio.ByteBuffer; import java.nio.channels.SelectionKey; import java.nio.channels.Selector; -public abstract class TNonblockingTransport extends TTransport { +public abstract class TNonblockingTransport extends TEndpointTransport { + + public TNonblockingTransport(TConfiguration config) throws TTransportException { + super(config); + } /** * Non-blocking connection initialization. @@ -41,7 +46,4 @@ public abstract class TNonblockingTransport extends TTransport { public abstract SelectionKey registerSelector(Selector selector, int interests) throws IOException; - public abstract int read(ByteBuffer buffer) throws IOException; - - public abstract int write(ByteBuffer buffer) throws IOException; } diff --git a/lib/java/src/org/apache/thrift/transport/TSSLTransportFactory.java b/lib/java/src/org/apache/thrift/transport/TSSLTransportFactory.java index 2232a315c12..3389e4d2acc 100644 --- a/lib/java/src/org/apache/thrift/transport/TSSLTransportFactory.java +++ b/lib/java/src/org/apache/thrift/transport/TSSLTransportFactory.java @@ -37,12 +37,18 @@ import javax.net.ssl.SSLSocketFactory; import javax.net.ssl.TrustManagerFactory; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + /** * A Factory for providing and setting up Client and Server SSL wrapped * TSocket and TServerSocket */ public class TSSLTransportFactory { + private static final Logger LOGGER = + LoggerFactory.getLogger(TSSLTransportFactory.class); + /** * Get a SSL wrapped TServerSocket bound to the specified port. In this * configuration the default settings are used. Default settings are retrieved @@ -166,7 +172,7 @@ public static TSocket getClientSocket(String host, int port) throws TTransportEx */ public static TSocket getClientSocket(String host, int port, int timeout, TSSLTransportParameters params) throws TTransportException { if (params == null || !(params.isKeyStoreSet || params.isTrustStoreSet)) { - throw new TTransportException("Either one of the KeyStore or TrustStore must be set for SSLTransportParameters"); + throw new TTransportException(TTransportException.NOT_OPEN, "Either one of the KeyStore or TrustStore must be set for SSLTransportParameters"); } SSLContext ctx = createSSLContext(params); @@ -219,20 +225,20 @@ else if (params.isKeyStoreSet) { } } catch (Exception e) { - throw new TTransportException("Error creating the transport", e); + throw new TTransportException(TTransportException.NOT_OPEN, "Error creating the transport", e); } finally { if (in != null) { try { in.close(); } catch (IOException e) { - e.printStackTrace(); + LOGGER.warn("Unable to close stream", e); } } if (is != null) { try { is.close(); } catch (IOException e) { - e.printStackTrace(); + LOGGER.warn("Unable to close stream", e); } } } @@ -269,8 +275,10 @@ private static TSocket createClient(SSLSocketFactory factory, String host, int p SSLSocket socket = (SSLSocket) factory.createSocket(host, port); socket.setSoTimeout(timeout); return new TSocket(socket); + } catch (TTransportException tte) { + throw tte; } catch (Exception e) { - throw new TTransportException("Could not connect to " + host + " on port " + port, e); + throw new TTransportException(TTransportException.NOT_OPEN, "Could not connect to " + host + " on port " + port, e); } } @@ -342,7 +350,7 @@ public void setKeyStore(String keyStore, String keyPass, String keyManagerType, } isKeyStoreSet = true; } - + /** * Set the keystore, password, certificate type and the store type * @@ -355,7 +363,7 @@ public void setKeyStore(InputStream keyStoreStream, String keyPass, String keyMa this.keyStoreStream = keyStoreStream; setKeyStore("", keyPass, keyManagerType, keyStoreType); } - + /** * Set the keystore and password * @@ -365,7 +373,7 @@ public void setKeyStore(InputStream keyStoreStream, String keyPass, String keyMa public void setKeyStore(String keyStore, String keyPass) { setKeyStore(keyStore, keyPass, null, null); } - + /** * Set the keystore and password * @@ -375,7 +383,7 @@ public void setKeyStore(String keyStore, String keyPass) { public void setKeyStore(InputStream keyStoreStream, String keyPass) { setKeyStore(keyStoreStream, keyPass, null, null); } - + /** * Set the truststore, password, certificate type and the store type * @@ -395,7 +403,7 @@ public void setTrustStore(String trustStore, String trustPass, String trustManag } isTrustStoreSet = true; } - + /** * Set the truststore, password, certificate type and the store type * @@ -418,7 +426,7 @@ public void setTrustStore(InputStream trustStoreStream, String trustPass, String public void setTrustStore(String trustStore, String trustPass) { setTrustStore(trustStore, trustPass, null, null); } - + /** * Set the truststore and password * diff --git a/lib/java/src/org/apache/thrift/transport/TSaslClientTransport.java b/lib/java/src/org/apache/thrift/transport/TSaslClientTransport.java index 81222897828..e5ca41831bd 100644 --- a/lib/java/src/org/apache/thrift/transport/TSaslClientTransport.java +++ b/lib/java/src/org/apache/thrift/transport/TSaslClientTransport.java @@ -19,7 +19,7 @@ package org.apache.thrift.transport; -import java.io.UnsupportedEncodingException; +import java.nio.charset.StandardCharsets; import java.util.Map; import javax.security.auth.callback.CallbackHandler; @@ -27,6 +27,7 @@ import javax.security.sasl.SaslClient; import javax.security.sasl.SaslException; +import org.apache.thrift.transport.sasl.NegotiationStatus; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -46,14 +47,14 @@ public class TSaslClientTransport extends TSaslTransport { /** * Uses the given SaslClient. - * + * * @param saslClient * The SaslClient to use for the subsequent SASL * negotiation. * @param transport * Transport underlying this one. */ - public TSaslClientTransport(SaslClient saslClient, TTransport transport) { + public TSaslClientTransport(SaslClient saslClient, TTransport transport) throws TTransportException { super(saslClient, transport); mechanism = saslClient.getMechanismName(); } @@ -62,14 +63,14 @@ public TSaslClientTransport(SaslClient saslClient, TTransport transport) { * Creates a SaslClient using the given SASL-specific parameters. * See the Java documentation for Sasl.createSaslClient for the * details of the parameters. - * + * * @param transport * The underlying Thrift transport. * @throws SaslException */ public TSaslClientTransport(String mechanism, String authorizationId, String protocol, String serverName, Map props, CallbackHandler cbh, TTransport transport) - throws SaslException { + throws SaslException, TTransportException { super(Sasl.createSaslClient(new String[] { mechanism }, authorizationId, protocol, serverName, props, cbh), transport); this.mechanism = mechanism; @@ -97,12 +98,7 @@ protected void handleSaslStartMessage() throws TTransportException, SaslExceptio LOGGER.debug("Sending mechanism name {} and initial response of length {}", mechanism, initialResponse.length); - byte[] mechanismBytes; - try { - mechanismBytes = mechanism.getBytes("UTF-8"); - } catch (UnsupportedEncodingException e) { - throw new TTransportException(e); - } + byte[] mechanismBytes = mechanism.getBytes(StandardCharsets.UTF_8); sendSaslMessage(NegotiationStatus.START, mechanismBytes); // Send initial response diff --git a/lib/java/src/org/apache/thrift/transport/TSaslServerTransport.java b/lib/java/src/org/apache/thrift/transport/TSaslServerTransport.java index e6c0e3e979a..9111712a41c 100644 --- a/lib/java/src/org/apache/thrift/transport/TSaslServerTransport.java +++ b/lib/java/src/org/apache/thrift/transport/TSaslServerTransport.java @@ -19,8 +19,8 @@ package org.apache.thrift.transport; -import java.io.UnsupportedEncodingException; import java.lang.ref.WeakReference; +import java.nio.charset.StandardCharsets; import java.util.Collections; import java.util.HashMap; import java.util.Map; @@ -31,7 +31,8 @@ import javax.security.sasl.SaslException; import javax.security.sasl.SaslServer; -import org.apache.thrift.TException; +import org.apache.thrift.transport.sasl.NegotiationStatus; +import org.apache.thrift.transport.sasl.TSaslServerDefinition; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -50,34 +51,14 @@ public class TSaslServerTransport extends TSaslTransport { */ private Map serverDefinitionMap = new HashMap(); - /** - * Contains all the parameters used to define a SASL server implementation. - */ - private static class TSaslServerDefinition { - public String mechanism; - public String protocol; - public String serverName; - public Map props; - public CallbackHandler cbh; - - public TSaslServerDefinition(String mechanism, String protocol, String serverName, - Map props, CallbackHandler cbh) { - this.mechanism = mechanism; - this.protocol = protocol; - this.serverName = serverName; - this.props = props; - this.cbh = cbh; - } - } - /** * Uses the given underlying transport. Assumes that addServerDefinition is * called later. - * + * * @param transport * Transport underlying this one. */ - public TSaslServerTransport(TTransport transport) { + public TSaslServerTransport(TTransport transport) throws TTransportException { super(transport); } @@ -85,17 +66,17 @@ public TSaslServerTransport(TTransport transport) { * Creates a SaslServer using the given SASL-specific parameters. * See the Java documentation for Sasl.createSaslServer for the * details of the parameters. - * + * * @param transport * The underlying Thrift transport. */ public TSaslServerTransport(String mechanism, String protocol, String serverName, - Map props, CallbackHandler cbh, TTransport transport) { + Map props, CallbackHandler cbh, TTransport transport) throws TTransportException { super(transport); addServerDefinition(mechanism, protocol, serverName, props, cbh); } - private TSaslServerTransport(Map serverDefinitionMap, TTransport transport) { + private TSaslServerTransport(Map serverDefinitionMap, TTransport transport) throws TTransportException { super(transport); this.serverDefinitionMap.putAll(serverDefinitionMap); } @@ -132,12 +113,7 @@ protected void handleSaslStartMessage() throws TTransportException, SaslExceptio } // Get the mechanism name. - String mechanismName; - try { - mechanismName = new String(message.payload, "UTF-8"); - } catch (UnsupportedEncodingException e) { - throw new TTransportException("JVM DOES NOT SUPPORT UTF-8"); - } + String mechanismName = new String(message.payload, StandardCharsets.UTF_8); TSaslServerDefinition serverDefinition = serverDefinitionMap.get(mechanismName); LOGGER.debug("Received mechanism name '{}'", mechanismName); @@ -214,7 +190,7 @@ public void addServerDefinition(String mechanism, String protocol, String server * receives the same TSaslServerTransport. */ @Override - public TTransport getTransport(TTransport base) { + public TTransport getTransport(TTransport base) throws TTransportException { WeakReference ret = transportMap.get(base); if (ret == null || ret.get() == null) { LOGGER.debug("transport map does not contain key", base); diff --git a/lib/java/src/org/apache/thrift/transport/TSaslTransport.java b/lib/java/src/org/apache/thrift/transport/TSaslTransport.java index bbd3f9a34a6..b106c7004af 100644 --- a/lib/java/src/org/apache/thrift/transport/TSaslTransport.java +++ b/lib/java/src/org/apache/thrift/transport/TSaslTransport.java @@ -19,9 +19,8 @@ package org.apache.thrift.transport; -import java.io.UnsupportedEncodingException; -import java.util.HashMap; -import java.util.Map; +import java.nio.charset.StandardCharsets; +import java.util.Objects; import javax.security.sasl.Sasl; import javax.security.sasl.SaslClient; @@ -30,6 +29,9 @@ import org.apache.thrift.EncodingUtils; import org.apache.thrift.TByteArrayOutputStream; +import org.apache.thrift.TConfiguration; +import org.apache.thrift.transport.layered.TFramedTransport; +import org.apache.thrift.transport.sasl.NegotiationStatus; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -37,7 +39,7 @@ * A superclass for SASL client/server thrift transports. A subclass need only * implement the open method. */ -abstract class TSaslTransport extends TTransport { +abstract class TSaslTransport extends TEndpointTransport { private static final Logger LOGGER = LoggerFactory.getLogger(TSaslTransport.class); @@ -51,39 +53,6 @@ protected static enum SaslRole { SERVER, CLIENT; } - /** - * Status bytes used during the initial Thrift SASL handshake. - */ - protected static enum NegotiationStatus { - START((byte)0x01), - OK((byte)0x02), - BAD((byte)0x03), - ERROR((byte)0x04), - COMPLETE((byte)0x05); - - private final byte value; - - private static final Map reverseMap = - new HashMap(); - static { - for (NegotiationStatus s : NegotiationStatus.class.getEnumConstants()) { - reverseMap.put(s.getValue(), s); - } - } - - private NegotiationStatus(byte val) { - this.value = val; - } - - public byte getValue() { - return value; - } - - public static NegotiationStatus byValue(byte val) { - return reverseMap.get(val); - } - } - /** * Transport underlying this one. */ @@ -117,7 +86,8 @@ public static NegotiationStatus byValue(byte val) { * @param underlyingTransport * The thrift transport which this transport is wrapping. */ - protected TSaslTransport(TTransport underlyingTransport) { + protected TSaslTransport(TTransport underlyingTransport) throws TTransportException { + super(Objects.isNull(underlyingTransport.getConfiguration()) ? new TConfiguration() : underlyingTransport.getConfiguration()); this.underlyingTransport = underlyingTransport; } @@ -130,7 +100,8 @@ protected TSaslTransport(TTransport underlyingTransport) { * @param underlyingTransport * The thrift transport which this transport is wrapping. */ - protected TSaslTransport(SaslClient saslClient, TTransport underlyingTransport) { + protected TSaslTransport(SaslClient saslClient, TTransport underlyingTransport) throws TTransportException { + super(Objects.isNull(underlyingTransport.getConfiguration()) ? new TConfiguration() : underlyingTransport.getConfiguration()); sasl = new SaslParticipant(saslClient); this.underlyingTransport = underlyingTransport; } @@ -158,9 +129,9 @@ protected void sendSaslMessage(NegotiationStatus status, byte[] payload) throws messageHeader[0] = status.getValue(); EncodingUtils.encodeBigEndian(payload.length, messageHeader, STATUS_BYTES); - if (LOGGER.isDebugEnabled()) - LOGGER.debug(getRole() + ": Writing message with status {} and payload length {}", - status, payload.length); + LOGGER.debug("{}: Writing message with status {} and payload length {}", + getRole(), status, payload.length); + underlyingTransport.write(messageHeader); underlyingTransport.write(payload); underlyingTransport.flush(); @@ -185,7 +156,7 @@ protected SaslResponse receiveSaslMessage() throws TTransportException { } int payloadBytes = EncodingUtils.decodeBigEndian(messageHeader, STATUS_BYTES); - if (payloadBytes < 0 || payloadBytes > 104857600 /* 100 MB */) { + if (payloadBytes < 0 || payloadBytes > getConfiguration().getMaxMessageSize() /* 100 MB */) { throw sendAndThrowMessage( NegotiationStatus.ERROR, "Invalid payload header length: " + payloadBytes); } @@ -194,17 +165,11 @@ protected SaslResponse receiveSaslMessage() throws TTransportException { underlyingTransport.readAll(payload, 0, payload.length); if (status == NegotiationStatus.BAD || status == NegotiationStatus.ERROR) { - try { - String remoteMessage = new String(payload, "UTF-8"); - throw new TTransportException("Peer indicated failure: " + remoteMessage); - } catch (UnsupportedEncodingException e) { - throw new TTransportException(e); - } + String remoteMessage = new String(payload, StandardCharsets.UTF_8); + throw new TTransportException("Peer indicated failure: " + remoteMessage); } - - if (LOGGER.isDebugEnabled()) - LOGGER.debug(getRole() + ": Received message with status {} and payload length {}", - status, payload.length); + LOGGER.debug("{}: Received message with status {} and payload length {}", + getRole(), status, payload.length); return new SaslResponse(status, payload); } @@ -224,7 +189,7 @@ protected SaslResponse receiveSaslMessage() throws TTransportException { */ protected TTransportException sendAndThrowMessage(NegotiationStatus status, String message) throws TTransportException { try { - sendSaslMessage(status, message.getBytes("UTF-8")); + sendSaslMessage(status, message.getBytes(StandardCharsets.UTF_8)); } catch (Exception e) { LOGGER.warn("Could not send failure response", e); message += "\nAlso, could not send response: " + e.toString(); @@ -316,14 +281,11 @@ public void open() throws TTransportException { underlyingTransport.close(); } } catch (TTransportException e) { - /* - * If there is no-data or no-sasl header in the stream, throw a different - * type of exception so we can handle this scenario differently. - */ + // If there is no-data or no-sasl header in the stream, + // log the failure, and clean up the underlying transport. if (!readSaslHeader && e.getType() == TTransportException.END_OF_FILE) { underlyingTransport.close(); - LOGGER.debug("No data or no sasl data in the stream"); - throw new TSaslTransportException("No data or no sasl data in the stream"); + LOGGER.debug("No data or no sasl data in the stream during negotiation"); } throw e; } @@ -401,7 +363,7 @@ public void close() { try { sasl.dispose(); } catch (SaslException e) { - // Not much we can do here. + LOGGER.warn("Failed to dispose sasl participant.", e); } } @@ -433,6 +395,12 @@ public int read(byte[] buf, int off, int len) throws TTransportException { readFrame(); } catch (SaslException e) { throw new TTransportException(e); + } catch (TTransportException transportException) { + // If there is no-data or no-sasl header in the stream, log the failure, and rethrow. + if (transportException.getType() == TTransportException.END_OF_FILE) { + LOGGER.debug("No data or no sasl data in the stream during negotiation"); + } + throw transportException; } return readBuffer.read(buf, off, len); diff --git a/lib/java/src/org/apache/thrift/transport/TServerSocket.java b/lib/java/src/org/apache/thrift/transport/TServerSocket.java index 79f7b7f49f8..eb302fd2684 100644 --- a/lib/java/src/org/apache/thrift/transport/TServerSocket.java +++ b/lib/java/src/org/apache/thrift/transport/TServerSocket.java @@ -121,18 +121,23 @@ public void listen() throws TTransportException { } } - protected TSocket acceptImpl() throws TTransportException { + @Override + public TSocket accept() throws TTransportException { if (serverSocket_ == null) { throw new TTransportException(TTransportException.NOT_OPEN, "No underlying server socket."); } + Socket result; try { - Socket result = serverSocket_.accept(); - TSocket result2 = new TSocket(result); - result2.setTimeout(clientTimeout_); - return result2; - } catch (IOException iox) { - throw new TTransportException(iox); + result = serverSocket_.accept(); + } catch (Exception e) { + throw new TTransportException(e); } + if (result == null) { + throw new TTransportException("Blocking server's accept() may not return NULL"); + } + TSocket socket = new TSocket(result); + socket.setTimeout(clientTimeout_); + return socket; } public void close() { diff --git a/lib/java/src/org/apache/thrift/transport/TServerTransport.java b/lib/java/src/org/apache/thrift/transport/TServerTransport.java index 424e4faaae2..55ef0c4c444 100644 --- a/lib/java/src/org/apache/thrift/transport/TServerTransport.java +++ b/lib/java/src/org/apache/thrift/transport/TServerTransport.java @@ -56,18 +56,18 @@ public T bindAddr(InetSocketAddress bindAddr) { public abstract void listen() throws TTransportException; - public final TTransport accept() throws TTransportException { - TTransport transport = acceptImpl(); - if (transport == null) { - throw new TTransportException("accept() may not return NULL"); - } - return transport; - } + /** + * Accept incoming connection on the server socket. When there is no incoming connection available: + * either it should block infinitely in a blocking implementation, either it should return null in + * a nonblocking implementation. + * + * @return new connection + * @throws TTransportException if IO error. + */ + public abstract TTransport accept() throws TTransportException; public abstract void close(); - protected abstract TTransport acceptImpl() throws TTransportException; - /** * Optional method implementation. This signals to the server transport * that it should break out of any accept() or listen() that it is currently diff --git a/lib/java/src/org/apache/thrift/transport/TSimpleFileTransport.java b/lib/java/src/org/apache/thrift/transport/TSimpleFileTransport.java index 42102d9e8e7..c1bbd485343 100644 --- a/lib/java/src/org/apache/thrift/transport/TSimpleFileTransport.java +++ b/lib/java/src/org/apache/thrift/transport/TSimpleFileTransport.java @@ -18,6 +18,8 @@ */ package org.apache.thrift.transport; +import org.apache.thrift.TConfiguration; + import java.io.IOException; import java.io.RandomAccessFile; @@ -25,26 +27,43 @@ /** * Basic file support for the TTransport interface */ -public final class TSimpleFileTransport extends TTransport { +public final class TSimpleFileTransport extends TEndpointTransport { + + private RandomAccessFile file = null; + private boolean readable; + private boolean writable; + private String path_; - private RandomAccessFile file = null; - private boolean readable; - private boolean writable; - private String path_; + /** + * Create a transport backed by a simple file + * + * @param path the path to the file to open/create + * @param read true to support read operations + * @param write true to support write operations + * @param openFile true to open the file on construction + * @throws TTransportException if file open fails + */ + public TSimpleFileTransport(String path, boolean read, + boolean write, boolean openFile) + throws TTransportException { + this(new TConfiguration(), path, read, write, openFile); + } /** - * Create a transport backed by a simple file - * + * Create a transport backed by a simple file + * + * @param config * @param path the path to the file to open/create * @param read true to support read operations * @param write true to support write operations * @param openFile true to open the file on construction * @throws TTransportException if file open fails */ - public TSimpleFileTransport(String path, boolean read, + public TSimpleFileTransport(TConfiguration config, String path, boolean read, boolean write, boolean openFile) throws TTransportException { + super(config); if (path.length() <= 0) { throw new TTransportException("No path specified"); } @@ -58,11 +77,11 @@ public TSimpleFileTransport(String path, boolean read, open(); } } - + /** - * Create a transport backed by a simple file + * Create a transport backed by a simple file * Implicitly opens file to conform to C++ behavior. - * + * * @param path the path to the file to open/create * @param read true to support read operations * @param write true to support write operations @@ -72,7 +91,7 @@ public TSimpleFileTransport(String path, boolean read, boolean write) throws TTransportException { this(path, read, write, true); } - + /** * Create a transport backed by a simple read only disk file (implicitly opens * file) @@ -95,7 +114,7 @@ public boolean isOpen() { } /** - * Open file if not previously opened. + * Open file if not previously opened. * * @throws TTransportException if open fails */ @@ -111,7 +130,7 @@ public void open() throws TTransportException { } catch (IOException ioe) { file = null; throw new TTransportException(ioe.getMessage()); - } + } } } @@ -131,7 +150,7 @@ public void close() { } /** - * Read up to len many bytes into buf at offset + * Read up to len many bytes into buf at offset * * @param buf houses bytes read * @param off offset into buff to begin writing to @@ -144,6 +163,7 @@ public int read(byte[] buf, int off, int len) throws TTransportException { if (!readable) { throw new TTransportException("Read operation on write only file"); } + checkReadBytesAvailable(len); int iBytesRead = 0; try { iBytesRead = file.read(buf, off, len); @@ -155,7 +175,7 @@ public int read(byte[] buf, int off, int len) throws TTransportException { } /** - * Write len many bytes from buff starting at offset + * Write len many bytes from buff starting at offset * * @param buf buffer containing bytes to write * @param off offset into buffer to begin writing from @@ -213,4 +233,4 @@ public long getFilePointer() throws TTransportException { throw new TTransportException(ex.getMessage()); } } -} \ No newline at end of file +} diff --git a/lib/java/src/org/apache/thrift/transport/TSocket.java b/lib/java/src/org/apache/thrift/transport/TSocket.java index b20b32b7815..eb73e8e76e3 100644 --- a/lib/java/src/org/apache/thrift/transport/TSocket.java +++ b/lib/java/src/org/apache/thrift/transport/TSocket.java @@ -19,6 +19,7 @@ package org.apache.thrift.transport; +import org.apache.thrift.TConfiguration; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -69,6 +70,7 @@ public class TSocket extends TIOStreamTransport { * @throws TTransportException if there is an error setting up the streams */ public TSocket(Socket socket) throws TTransportException { + super(new TConfiguration()); socket_ = socket; try { socket_.setSoLinger(false, 0); @@ -93,23 +95,36 @@ public TSocket(Socket socket) throws TTransportException { * Creates a new unconnected socket that will connect to the given host * on the given port. * + * @param config check config * @param host Remote host * @param port Remote port */ - public TSocket(String host, int port) { - this(host, port, 0); + public TSocket(TConfiguration config, String host, int port) throws TTransportException { + this(config, host, port, 0); } /** * Creates a new unconnected socket that will connect to the given host * on the given port. * + * @param host Remote host + * @param port Remote port + */ + public TSocket(String host, int port) throws TTransportException { + this(new TConfiguration(), host, port, 0); + } + + /** + * Creates a new unconnected socket that will connect to the given host + * on the given port. + * + * @param config check config * @param host Remote host * @param port Remote port * @param timeout Socket timeout and connection timeout */ - public TSocket(String host, int port, int timeout) { - this(host, port, timeout, timeout); + public TSocket(TConfiguration config, String host, int port, int timeout) throws TTransportException { + this(config, host, port, timeout, timeout); } /** @@ -117,12 +132,14 @@ public TSocket(String host, int port, int timeout) { * on the given port, with a specific connection timeout and a * specific socket timeout. * + * @param config check config * @param host Remote host * @param port Remote port * @param socketTimeout Socket timeout * @param connectTimeout Connection timeout */ - public TSocket(String host, int port, int socketTimeout, int connectTimeout) { + public TSocket(TConfiguration config, String host, int port, int socketTimeout, int connectTimeout) throws TTransportException { + super(config); host_ = host; port_ = port; socketTimeout_ = socketTimeout; diff --git a/lib/java/src/org/apache/thrift/transport/TTransport.java b/lib/java/src/org/apache/thrift/transport/TTransport.java index 73ad730ce72..ee070243dea 100644 --- a/lib/java/src/org/apache/thrift/transport/TTransport.java +++ b/lib/java/src/org/apache/thrift/transport/TTransport.java @@ -19,7 +19,10 @@ package org.apache.thrift.transport; +import org.apache.thrift.TConfiguration; + import java.io.Closeable; +import java.nio.ByteBuffer; /** * Generic class that encapsulates the I/O layer. This is basically a thin @@ -57,6 +60,26 @@ public abstract void open() */ public abstract void close(); + /** + * Reads a sequence of bytes from this channel into the given buffer. An + * attempt is made to read up to the number of bytes remaining in the buffer, + * that is, dst.remaining(), at the moment this method is invoked. Upon return + * the buffer's position will move forward the number of bytes read; its limit + * will not have changed. Subclasses are encouraged to provide a more + * efficient implementation of this method. + * + * @param dst The buffer into which bytes are to be transferred + * @return The number of bytes read, possibly zero, or -1 if the channel has + * reached end-of-stream + * @throws TTransportException if there was an error reading data + */ + public int read(ByteBuffer dst) throws TTransportException { + byte[] arr = new byte[dst.remaining()]; + int n = read(arr, 0, arr.length); + dst.put(arr, 0, n); + return n; + } + /** * Reads up to len bytes into buffer buf, starting at offset off. * @@ -118,6 +141,24 @@ public void write(byte[] buf) throws TTransportException { public abstract void write(byte[] buf, int off, int len) throws TTransportException; + /** + * Writes a sequence of bytes to the buffer. An attempt is made to write all + * remaining bytes in the buffer, that is, src.remaining(), at the moment this + * method is invoked. Upon return the buffer's position will updated; its limit + * will not have changed. Subclasses are encouraged to provide a more efficient + * implementation of this method. + * + * @param src The buffer from which bytes are to be retrieved + * @return The number of bytes written, possibly zero + * @throws TTransportException if there was an error writing data + */ + public int write(ByteBuffer src) throws TTransportException { + byte[] arr = new byte[src.remaining()]; + src.get(arr); + write(arr, 0, arr.length); + return arr.length; + } + /** * Flush any pending data out of a transport buffer. * @@ -160,4 +201,10 @@ public int getBytesRemainingInBuffer() { * @param len */ public void consumeBuffer(int len) {} + + public abstract TConfiguration getConfiguration(); + + public abstract void updateKnownMessageSize(long size) throws TTransportException; + + public abstract void checkReadBytesAvailable(long numBytes) throws TTransportException; } diff --git a/lib/java/src/org/apache/thrift/transport/TTransportFactory.java b/lib/java/src/org/apache/thrift/transport/TTransportFactory.java index 3e71630aecc..e068b4beb76 100644 --- a/lib/java/src/org/apache/thrift/transport/TTransportFactory.java +++ b/lib/java/src/org/apache/thrift/transport/TTransportFactory.java @@ -34,7 +34,7 @@ public class TTransportFactory { * @param trans The base transport * @return Wrapped Transport */ - public TTransport getTransport(TTransport trans) { + public TTransport getTransport(TTransport trans) throws TTransportException { return trans; } diff --git a/lib/java/src/org/apache/thrift/transport/TZlibTransport.java b/lib/java/src/org/apache/thrift/transport/TZlibTransport.java index e755aa53242..73b21aa3f78 100644 --- a/lib/java/src/org/apache/thrift/transport/TZlibTransport.java +++ b/lib/java/src/org/apache/thrift/transport/TZlibTransport.java @@ -18,9 +18,12 @@ */ package org.apache.thrift.transport; +import org.apache.thrift.TConfiguration; + import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; +import java.util.Objects; import java.util.zip.Deflater; import java.util.zip.DeflaterOutputStream; import java.util.zip.Inflater; @@ -38,7 +41,7 @@ public Factory() { } @Override - public TTransport getTransport(TTransport base) { + public TTransport getTransport(TTransport base) throws TTransportException { return new TZlibTransport(base); } } @@ -47,7 +50,7 @@ public TTransport getTransport(TTransport base) { * Constructs a new TZlibTransport instance. * @param transport the underlying transport to read from and write to */ - public TZlibTransport(TTransport transport) { + public TZlibTransport(TTransport transport) throws TTransportException { this(transport, Deflater.BEST_COMPRESSION); } @@ -56,7 +59,8 @@ public TZlibTransport(TTransport transport) { * @param transport the underlying transport to read from and write to * @param compressionLevel 0 for no compression, 9 for maximum compression */ - public TZlibTransport(TTransport transport, int compressionLevel) { + public TZlibTransport(TTransport transport, int compressionLevel) throws TTransportException { + super(Objects.isNull(transport.getConfiguration()) ? new TConfiguration() : transport.getConfiguration()); transport_ = transport; inputStream_ = new InflaterInputStream(new TTransportInputStream(transport_), new Inflater()); outputStream_ = new DeflaterOutputStream(new TTransportOutputStream(transport_), new Deflater(compressionLevel, false), true); diff --git a/lib/java/src/org/apache/thrift/transport/TFastFramedTransport.java b/lib/java/src/org/apache/thrift/transport/layered/TFastFramedTransport.java similarity index 72% rename from lib/java/src/org/apache/thrift/transport/TFastFramedTransport.java rename to lib/java/src/org/apache/thrift/transport/layered/TFastFramedTransport.java index d2656008421..29bf39c14d0 100644 --- a/lib/java/src/org/apache/thrift/transport/TFastFramedTransport.java +++ b/lib/java/src/org/apache/thrift/transport/layered/TFastFramedTransport.java @@ -16,7 +16,13 @@ * specific language governing permissions and limitations * under the License. */ -package org.apache.thrift.transport; +package org.apache.thrift.transport.layered; + + +import org.apache.thrift.TConfiguration; +import org.apache.thrift.transport.*; + +import java.util.Objects; /** * This transport is wire compatible with {@link TFramedTransport}, but makes @@ -27,18 +33,18 @@ * * This implementation is NOT threadsafe. */ -public class TFastFramedTransport extends TTransport { +public class TFastFramedTransport extends TLayeredTransport { public static class Factory extends TTransportFactory { private final int initialCapacity; private final int maxLength; public Factory() { - this(DEFAULT_BUF_CAPACITY, DEFAULT_MAX_LENGTH); + this(DEFAULT_BUF_CAPACITY, TConfiguration.DEFAULT_MAX_FRAME_SIZE); } public Factory(int initialCapacity) { - this(initialCapacity, DEFAULT_MAX_LENGTH); + this(initialCapacity, TConfiguration.DEFAULT_MAX_FRAME_SIZE); } public Factory(int initialCapacity, int maxLength) { @@ -47,7 +53,7 @@ public Factory(int initialCapacity, int maxLength) { } @Override - public TTransport getTransport(TTransport trans) { + public TTransport getTransport(TTransport trans) throws TTransportException { return new TFastFramedTransport(trans, initialCapacity, maxLength); @@ -58,12 +64,7 @@ public TTransport getTransport(TTransport trans) { * How big should the default read and write buffers be? */ public static final int DEFAULT_BUF_CAPACITY = 1024; - /** - * How big is the largest allowable frame? Defaults to 16MB. - */ - public static final int DEFAULT_MAX_LENGTH = 16384000; - private final TTransport underlying; private final AutoExpandingBufferWriteTransport writeBuffer; private AutoExpandingBufferReadTransport readBuffer; private final int initialBufferCapacity; @@ -75,8 +76,8 @@ public TTransport getTransport(TTransport trans) { * for initial buffer size and max frame length. * @param underlying Transport that real reads and writes will go through to. */ - public TFastFramedTransport(TTransport underlying) { - this(underlying, DEFAULT_BUF_CAPACITY, DEFAULT_MAX_LENGTH); + public TFastFramedTransport(TTransport underlying) throws TTransportException { + this(underlying, DEFAULT_BUF_CAPACITY, TConfiguration.DEFAULT_MAX_FRAME_SIZE); } /** @@ -87,8 +88,8 @@ public TFastFramedTransport(TTransport underlying) { * In practice, it's not critical to set this unless you know in advance that * your messages are going to be very large. */ - public TFastFramedTransport(TTransport underlying, int initialBufferCapacity) { - this(underlying, initialBufferCapacity, DEFAULT_MAX_LENGTH); + public TFastFramedTransport(TTransport underlying, int initialBufferCapacity) throws TTransportException { + this(underlying, initialBufferCapacity, TConfiguration.DEFAULT_MAX_FRAME_SIZE); } /** @@ -102,27 +103,29 @@ public TFastFramedTransport(TTransport underlying, int initialBufferCapacity) { * @param maxLength The max frame size you are willing to read. You can use * this parameter to limit how much memory can be allocated. */ - public TFastFramedTransport(TTransport underlying, int initialBufferCapacity, int maxLength) { - this.underlying = underlying; + public TFastFramedTransport(TTransport underlying, int initialBufferCapacity, int maxLength) throws TTransportException { + super(underlying); + TConfiguration config = Objects.isNull(underlying.getConfiguration()) ? new TConfiguration() : underlying.getConfiguration(); this.maxLength = maxLength; + config.setMaxFrameSize(maxLength); this.initialBufferCapacity = initialBufferCapacity; - writeBuffer = new AutoExpandingBufferWriteTransport(initialBufferCapacity, 1.5); - readBuffer = new AutoExpandingBufferReadTransport(initialBufferCapacity, 1.5); + readBuffer = new AutoExpandingBufferReadTransport(config, initialBufferCapacity); + writeBuffer = new AutoExpandingBufferWriteTransport(config, initialBufferCapacity, 4); } @Override public void close() { - underlying.close(); + getInnerTransport().close(); } @Override public boolean isOpen() { - return underlying.isOpen(); + return getInnerTransport().isOpen(); } @Override public void open() throws TTransportException { - underlying.open(); + getInnerTransport().open(); } @Override @@ -139,7 +142,7 @@ public int read(byte[] buf, int off, int len) throws TTransportException { } private void readFrame() throws TTransportException { - underlying.readAll(i32buf , 0, 4); + getInnerTransport().readAll(i32buf , 0, 4); int size = TFramedTransport.decodeFrameSize(i32buf); if (size < 0) { @@ -147,13 +150,13 @@ private void readFrame() throws TTransportException { throw new TTransportException(TTransportException.CORRUPTED_DATA, "Read a negative frame size (" + size + ")!"); } - if (size > maxLength) { + if (size > getInnerTransport().getConfiguration().getMaxFrameSize()) { close(); throw new TTransportException(TTransportException.CORRUPTED_DATA, "Frame size (" + size + ") larger than max length (" + maxLength + ")!"); } - readBuffer.fill(underlying, size); + readBuffer.fill(getInnerTransport(), size); } @Override @@ -166,18 +169,21 @@ public void consumeBuffer(int len) { readBuffer.consumeBuffer(len); } - public void clear() { - readBuffer = new AutoExpandingBufferReadTransport(initialBufferCapacity, 1.5); + /** + * Only clears the read buffer! + */ + public void clear() throws TTransportException { + readBuffer = new AutoExpandingBufferReadTransport(getConfiguration(), initialBufferCapacity); } @Override public void flush() throws TTransportException { - int length = writeBuffer.getPos(); - TFramedTransport.encodeFrameSize(length, i32buf); - underlying.write(i32buf, 0, 4); - underlying.write(writeBuffer.getBuf().array(), 0, length); + int payloadLength = writeBuffer.getLength() - 4; + byte[] data = writeBuffer.getBuf().array(); + TFramedTransport.encodeFrameSize(payloadLength, data); + getInnerTransport().write(data, 0, payloadLength + 4); writeBuffer.reset(); - underlying.flush(); + getInnerTransport().flush(); } @Override diff --git a/lib/java/src/org/apache/thrift/transport/TFramedTransport.java b/lib/java/src/org/apache/thrift/transport/layered/TFramedTransport.java similarity index 62% rename from lib/java/src/org/apache/thrift/transport/TFramedTransport.java rename to lib/java/src/org/apache/thrift/transport/layered/TFramedTransport.java index fa531ef35e3..10a9a1c179d 100644 --- a/lib/java/src/org/apache/thrift/transport/TFramedTransport.java +++ b/lib/java/src/org/apache/thrift/transport/layered/TFramedTransport.java @@ -17,24 +17,22 @@ * under the License. */ -package org.apache.thrift.transport; +package org.apache.thrift.transport.layered; import org.apache.thrift.TByteArrayOutputStream; +import org.apache.thrift.TConfiguration; +import org.apache.thrift.transport.TMemoryInputTransport; +import org.apache.thrift.transport.TTransport; +import org.apache.thrift.transport.TTransportException; +import org.apache.thrift.transport.TTransportFactory; + +import java.util.Objects; /** * TFramedTransport is a buffered TTransport that ensures a fully read message * every time by preceding messages with a 4-byte frame size. */ -public class TFramedTransport extends TTransport { - - protected static final int DEFAULT_MAX_LENGTH = 16384000; - - private int maxLength_; - - /** - * Underlying transport - */ - private TTransport transport_ = null; +public class TFramedTransport extends TLayeredTransport { /** * Buffer for output @@ -45,14 +43,13 @@ public class TFramedTransport extends TTransport { /** * Buffer for input */ - private final TMemoryInputTransport readBuffer_ = - new TMemoryInputTransport(new byte[0]); + private final TMemoryInputTransport readBuffer_; public static class Factory extends TTransportFactory { private int maxLength_; public Factory() { - maxLength_ = TFramedTransport.DEFAULT_MAX_LENGTH; + maxLength_ = TConfiguration.DEFAULT_MAX_FRAME_SIZE; } public Factory(int maxLength) { @@ -60,34 +57,43 @@ public Factory(int maxLength) { } @Override - public TTransport getTransport(TTransport base) { + public TTransport getTransport(TTransport base) throws TTransportException { return new TFramedTransport(base, maxLength_); } } + /** + * Something to fill in the first four bytes of the buffer + * to make room for the frame size. This allows the + * implementation to write once instead of twice. + */ + private static final byte[] sizeFiller_ = new byte[] { 0x00, 0x00, 0x00, 0x00 }; + /** * Constructor wraps around another transport */ - public TFramedTransport(TTransport transport, int maxLength) { - transport_ = transport; - maxLength_ = maxLength; + public TFramedTransport(TTransport transport, int maxLength) throws TTransportException { + super(transport); + TConfiguration _configuration = Objects.isNull(transport.getConfiguration()) ? new TConfiguration() : transport.getConfiguration(); + _configuration.setMaxFrameSize(maxLength); + writeBuffer_.write(sizeFiller_, 0, 4); + readBuffer_= new TMemoryInputTransport(_configuration, new byte[0]); } - public TFramedTransport(TTransport transport) { - transport_ = transport; - maxLength_ = TFramedTransport.DEFAULT_MAX_LENGTH; + public TFramedTransport(TTransport transport) throws TTransportException { + this(transport, TConfiguration.DEFAULT_MAX_FRAME_SIZE); } public void open() throws TTransportException { - transport_.open(); + getInnerTransport().open(); } public boolean isOpen() { - return transport_.isOpen(); + return getInnerTransport().isOpen(); } public void close() { - transport_.close(); + getInnerTransport().close(); } public int read(byte[] buf, int off, int len) throws TTransportException { @@ -129,7 +135,7 @@ public void clear() { private final byte[] i32buf = new byte[4]; private void readFrame() throws TTransportException { - transport_.readAll(i32buf, 0, 4); + getInnerTransport().readAll(i32buf, 0, 4); int size = decodeFrameSize(i32buf); if (size < 0) { @@ -137,14 +143,14 @@ private void readFrame() throws TTransportException { throw new TTransportException(TTransportException.CORRUPTED_DATA, "Read a negative frame size (" + size + ")!"); } - if (size > maxLength_) { + if (size > getInnerTransport().getConfiguration().getMaxFrameSize()) { close(); throw new TTransportException(TTransportException.CORRUPTED_DATA, - "Frame size (" + size + ") larger than max length (" + maxLength_ + ")!"); + "Frame size (" + size + ") larger than max length (" + getInnerTransport().getConfiguration().getMaxFrameSize() + ")!"); } byte[] buff = new byte[size]; - transport_.readAll(buff, 0, size); + getInnerTransport().readAll(buff, 0, size); readBuffer_.reset(buff); } @@ -155,13 +161,13 @@ public void write(byte[] buf, int off, int len) throws TTransportException { @Override public void flush() throws TTransportException { byte[] buf = writeBuffer_.get(); - int len = writeBuffer_.len(); + int len = writeBuffer_.len() - 4; // account for the prepended frame size writeBuffer_.reset(); + writeBuffer_.write(sizeFiller_, 0, 4); // make room for the next frame's size data - encodeFrameSize(len, i32buf); - transport_.write(i32buf, 0, 4); - transport_.write(buf, 0, len); - transport_.flush(); + encodeFrameSize(len, buf); // this is the frame length without the filler + getInnerTransport().write(buf, 0, len + 4); // we have to write the frame size and frame data + getInnerTransport().flush(); } public static final void encodeFrameSize(final int frameSize, final byte[] buf) { diff --git a/lib/java/src/org/apache/thrift/transport/layered/TLayeredTransport.java b/lib/java/src/org/apache/thrift/transport/layered/TLayeredTransport.java new file mode 100644 index 00000000000..69ec824eef8 --- /dev/null +++ b/lib/java/src/org/apache/thrift/transport/layered/TLayeredTransport.java @@ -0,0 +1,52 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.thrift.transport.layered; + +import org.apache.thrift.TConfiguration; +import org.apache.thrift.transport.TTransport; +import org.apache.thrift.transport.TTransportException; + +import java.util.Objects; + +public abstract class TLayeredTransport extends TTransport{ + + private TTransport innerTransport; + + public TConfiguration getConfiguration() { + return innerTransport.getConfiguration(); + } + + public TLayeredTransport(TTransport transport) + { + Objects.requireNonNull(transport, "TTransport cannot be null."); + innerTransport = transport; + } + + public void updateKnownMessageSize(long size) throws TTransportException { + innerTransport.updateKnownMessageSize(size); + } + + public void checkReadBytesAvailable(long numBytes) throws TTransportException { + innerTransport.checkReadBytesAvailable(numBytes); + } + + public TTransport getInnerTransport() { + return innerTransport; + } +} diff --git a/lib/cocoa/src/TBaseClient.m b/lib/java/src/org/apache/thrift/transport/sasl/DataFrameHeaderReader.java similarity index 55% rename from lib/cocoa/src/TBaseClient.m rename to lib/java/src/org/apache/thrift/transport/sasl/DataFrameHeaderReader.java index 249cae06666..2900df9c101 100644 --- a/lib/cocoa/src/TBaseClient.m +++ b/lib/java/src/org/apache/thrift/transport/sasl/DataFrameHeaderReader.java @@ -17,35 +17,31 @@ * under the License. */ -#import "TBaseClient.h" -#import "TApplicationError.h" +package org.apache.thrift.transport.sasl; +/** + * The header for data frame, it only contains a 4-byte payload size. + */ +public class DataFrameHeaderReader extends FixedSizeHeaderReader { + public static final int PAYLOAD_LENGTH_BYTES = 4; -@interface TBaseClient () -@end - - -@implementation TBaseClient - --(NSError *) checkIncomingMessageException:(id)inProtocol -{ - NSError *thriftError; + private int payloadSize; - SInt32 msgType = 0; - if (![inProtocol readMessageBeginReturningName:nil type:&msgType sequenceID:NULL error:&thriftError]) { - return thriftError; + @Override + protected int headerSize() { + return PAYLOAD_LENGTH_BYTES; } - if (msgType == TMessageTypeEXCEPTION) { - - thriftError = [NSError read:inProtocol]; - - [inProtocol readMessageEnd:NULL]; - - return thriftError; + @Override + protected void onComplete() throws TInvalidSaslFrameException { + payloadSize = byteBuffer.getInt(0); + if (payloadSize < 0) { + throw new TInvalidSaslFrameException("Payload size is negative: " + payloadSize); + } } - return nil; + @Override + public int payloadSize() { + return payloadSize; + } } - -@end diff --git a/lib/csharp/src/Protocol/TMessageType.cs b/lib/java/src/org/apache/thrift/transport/sasl/DataFrameReader.java similarity index 77% rename from lib/csharp/src/Protocol/TMessageType.cs rename to lib/java/src/org/apache/thrift/transport/sasl/DataFrameReader.java index c7091fede83..e6900bbc68b 100644 --- a/lib/csharp/src/Protocol/TMessageType.cs +++ b/lib/java/src/org/apache/thrift/transport/sasl/DataFrameReader.java @@ -1,4 +1,4 @@ -/** +/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information @@ -17,15 +17,14 @@ * under the License. */ -using System; +package org.apache.thrift.transport.sasl; + +/** + * Frames for thrift (serialized) messages. + */ +public class DataFrameReader extends FrameReader { -namespace Thrift.Protocol -{ - public enum TMessageType - { - Call = 1, - Reply = 2, - Exception = 3, - Oneway = 4 - } + public DataFrameReader() { + super(new DataFrameHeaderReader()); + } } diff --git a/lib/java/src/org/apache/thrift/transport/sasl/DataFrameWriter.java b/lib/java/src/org/apache/thrift/transport/sasl/DataFrameWriter.java new file mode 100644 index 00000000000..a2dd15a8cfe --- /dev/null +++ b/lib/java/src/org/apache/thrift/transport/sasl/DataFrameWriter.java @@ -0,0 +1,60 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.thrift.transport.sasl; + +import java.nio.ByteBuffer; + +import org.apache.thrift.EncodingUtils; +import org.apache.thrift.utils.StringUtils; + +import static org.apache.thrift.transport.sasl.DataFrameHeaderReader.PAYLOAD_LENGTH_BYTES; + +/** + * Write frames of thrift messages. It expects an empty/null header to be provided with a payload + * to be written out. Non empty headers are considered as error. + */ +public class DataFrameWriter extends FrameWriter { + + @Override + public void withOnlyPayload(byte[] payload, int offset, int length) { + if (!isComplete()) { + throw new IllegalStateException("Previsous write is not yet complete, with " + + frameBytes.remaining() + " bytes left."); + } + frameBytes = buildFrameWithPayload(payload, offset, length); + } + + @Override + protected ByteBuffer buildFrame(byte[] header, int headerOffset, int headerLength, + byte[] payload, int payloadOffset, int payloadLength) { + if (header != null && headerLength > 0) { + throw new IllegalArgumentException("Extra header [" + StringUtils.bytesToHexString(header) + + "] offset " + payloadOffset + " length " + payloadLength); + } + return buildFrameWithPayload(payload, payloadOffset, payloadLength); + } + + private ByteBuffer buildFrameWithPayload(byte[] payload, int offset, int length) { + byte[] bytes = new byte[PAYLOAD_LENGTH_BYTES + length]; + EncodingUtils.encodeBigEndian(length, bytes, 0); + System.arraycopy(payload, offset, bytes, PAYLOAD_LENGTH_BYTES, length); + return ByteBuffer.wrap(bytes); + } +} diff --git a/lib/java/src/org/apache/thrift/transport/sasl/FixedSizeHeaderReader.java b/lib/java/src/org/apache/thrift/transport/sasl/FixedSizeHeaderReader.java new file mode 100644 index 00000000000..1cbc0ace08a --- /dev/null +++ b/lib/java/src/org/apache/thrift/transport/sasl/FixedSizeHeaderReader.java @@ -0,0 +1,74 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.thrift.transport.sasl; + +import org.apache.thrift.transport.TTransport; +import org.apache.thrift.transport.TTransportException; +import org.apache.thrift.utils.StringUtils; + +import java.nio.ByteBuffer; + +/** + * Headers' size should be predefined. + */ +public abstract class FixedSizeHeaderReader implements FrameHeaderReader { + + protected final ByteBuffer byteBuffer = ByteBuffer.allocate(headerSize()); + + @Override + public boolean isComplete() { + return !byteBuffer.hasRemaining(); + } + + @Override + public void clear() { + byteBuffer.clear(); + } + + @Override + public byte[] toBytes() { + if (!isComplete()) { + throw new IllegalStateException("Header is not yet complete " + StringUtils.bytesToHexString(byteBuffer.array(), 0, byteBuffer.position())); + } + return byteBuffer.array(); + } + + @Override + public boolean read(TTransport transport) throws TTransportException { + FrameReader.readAvailable(transport, byteBuffer); + if (byteBuffer.hasRemaining()) { + return false; + } + onComplete(); + return true; + } + + /** + * @return Size of the header. + */ + protected abstract int headerSize(); + + /** + * Actions (e.g. validation) to carry out when the header is complete. + * + * @throws TTransportException + */ + protected abstract void onComplete() throws TTransportException; +} diff --git a/lib/java/src/org/apache/thrift/transport/sasl/FrameHeaderReader.java b/lib/java/src/org/apache/thrift/transport/sasl/FrameHeaderReader.java new file mode 100644 index 00000000000..f7c65931562 --- /dev/null +++ b/lib/java/src/org/apache/thrift/transport/sasl/FrameHeaderReader.java @@ -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. + */ + +package org.apache.thrift.transport.sasl; + +import org.apache.thrift.transport.TTransport; +import org.apache.thrift.transport.TTransportException; + +/** + * Read headers for a frame. For each frame, the header contains payload size and other metadata. + */ +public interface FrameHeaderReader { + + /** + * As the thrift sasl specification states, all sasl messages (both for negotiatiing and for + * sending data) should have a header to indicate the size of the payload. + * + * @return size of the payload. + */ + int payloadSize(); + + /** + * + * @return The received bytes for the header. + * @throws IllegalStateException if isComplete returns false. + */ + byte[] toBytes(); + + /** + * @return true if this header has all its fields set. + */ + boolean isComplete(); + + /** + * Clear the header and make it available to read a new header. + */ + void clear(); + + /** + * (Nonblocking) Read fields from underlying transport layer. + * + * @param transport underlying transport. + * @return true if header is complete after read. + * @throws TSaslNegotiationException if fail to read a valid header of a sasl negotiation message. + * @throws TTransportException if io error. + */ + boolean read(TTransport transport) throws TSaslNegotiationException, TTransportException; +} diff --git a/lib/java/src/org/apache/thrift/transport/sasl/FrameReader.java b/lib/java/src/org/apache/thrift/transport/sasl/FrameReader.java new file mode 100644 index 00000000000..acb4b738d7d --- /dev/null +++ b/lib/java/src/org/apache/thrift/transport/sasl/FrameReader.java @@ -0,0 +1,162 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.thrift.transport.sasl; + +import org.apache.thrift.transport.TEOFException; +import org.apache.thrift.transport.TTransport; +import org.apache.thrift.transport.TTransportException; + +import java.nio.ByteBuffer; + +/** + * Read frames from a transport. Each frame has a header and a payload. A header will indicate + * the size of the payload and other informations about how to decode payload. + * Implementations should subclass it by providing a header reader implementation. + * + * @param Header type. + */ +public abstract class FrameReader { + private final T header; + private ByteBuffer payload; + + protected FrameReader(T header) { + this.header = header; + } + + /** + * (Nonblocking) Read available bytes out of the transport without blocking to wait for incoming + * data. + * + * @param transport TTransport + * @return true if current frame is complete after read. + * @throws TSaslNegotiationException if fail to read back a valid sasl negotiation message. + * @throws TTransportException if io error. + */ + public boolean read(TTransport transport) throws TSaslNegotiationException, TTransportException { + if (!header.isComplete()) { + if (readHeader(transport)) { + payload = ByteBuffer.allocate(header.payloadSize()); + } else { + return false; + } + } + if (header.payloadSize() == 0) { + return true; + } + return readPayload(transport); + } + + /** + * (Nonblocking) Try to read available header bytes from transport. + * + * @return true if header is complete after read. + * @throws TSaslNegotiationException if fail to read back a validd sasl negotiation header. + * @throws TTransportException if io error. + */ + private boolean readHeader(TTransport transport) throws TSaslNegotiationException, TTransportException { + return header.read(transport); + } + + /** + * (Nonblocking) Try to read available + * + * @param transport underlying transport. + * @return true if payload is complete after read. + * @throws TTransportException if io error. + */ + private boolean readPayload(TTransport transport) throws TTransportException { + readAvailable(transport, payload); + return payload.hasRemaining(); + } + + /** + * + * @return header of the frame + */ + public T getHeader() { + return header; + } + + /** + * + * @return number of bytes of the header + */ + public int getHeaderSize() { + return header.toBytes().length; + } + + /** + * + * @return byte array of the payload + */ + public byte[] getPayload() { + return payload.array(); + } + + /** + * + * @return size of the payload + */ + public int getPayloadSize() { + return header.payloadSize(); + } + + /** + * + * @return true if the reader has fully read a frame + */ + public boolean isComplete() { + return !(payload == null || payload.hasRemaining()); + } + + /** + * Reset the state of the reader so that it can be reused to read a new frame. + */ + public void clear() { + header.clear(); + payload = null; + } + + /** + * Read immediately available bytes from the transport into the byte buffer. + * + * @param transport TTransport + * @param recipient ByteBuffer + * @return number of bytes read out of the transport + * @throws TTransportException if io error + */ + static int readAvailable(TTransport transport, ByteBuffer recipient) throws TTransportException { + if (!recipient.hasRemaining()) { + throw new IllegalStateException("Trying to fill a full recipient with " + recipient.limit() + + " bytes"); + } + int currentPosition = recipient.position(); + byte[] bytes = recipient.array(); + int offset = recipient.arrayOffset() + currentPosition; + int expectedLength = recipient.remaining(); + int got = transport.read(bytes, offset, expectedLength); + if (got < 0) { + throw new TEOFException("Transport is closed, while trying to read " + expectedLength + + " bytes"); + } + recipient.position(currentPosition + got); + return got; + } +} diff --git a/lib/java/src/org/apache/thrift/transport/sasl/FrameWriter.java b/lib/java/src/org/apache/thrift/transport/sasl/FrameWriter.java new file mode 100644 index 00000000000..4357f13e161 --- /dev/null +++ b/lib/java/src/org/apache/thrift/transport/sasl/FrameWriter.java @@ -0,0 +1,123 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.thrift.transport.sasl; + +import java.io.IOException; +import java.nio.ByteBuffer; + +import org.apache.thrift.transport.TNonblockingTransport; +import org.apache.thrift.transport.TTransportException; + +/** + * Write frame (header and payload) to transport in a nonblocking way. + */ +public abstract class FrameWriter { + + protected ByteBuffer frameBytes; + + /** + * Provide (maybe empty) header and payload to the frame. This can be called only when isComplete + * returns true (last frame has been written out). + * + * @param header Some extra header bytes (without the 4 bytes for payload length), which will be + * the start of the frame. It can be empty, depending on the message format + * @param payload Payload as a byte array + * @throws IllegalStateException if it is called when isComplete returns false + * @throws IllegalArgumentException if header or payload is invalid + */ + public void withHeaderAndPayload(byte[] header, byte[] payload) { + if (payload == null) { + payload = new byte[0]; + } + if (header == null) { + withOnlyPayload(payload); + } else { + withHeaderAndPayload(header, 0, header.length, payload, 0, payload.length); + } + } + + /** + * Provide extra header and payload to the frame. + * + * @param header byte array containing the extra header + * @param headerOffset starting offset of the header portition + * @param headerLength length of the extra header + * @param payload byte array containing the payload + * @param payloadOffset starting offset of the payload portion + * @param payloadLength length of the payload + * @throws IllegalStateException if preivous frame is not yet complete (isComplete returns fals) + * @throws IllegalArgumentException if header or payload is invalid + */ + public void withHeaderAndPayload(byte[] header, int headerOffset, int headerLength, + byte[] payload, int payloadOffset, int payloadLength) { + if (!isComplete()) { + throw new IllegalStateException("Previsous write is not yet complete, with " + + frameBytes.remaining() + " bytes left."); + } + frameBytes = buildFrame(header, headerOffset, headerLength, payload, payloadOffset, payloadLength); + } + + /** + * Provide only payload to the frame. Throws UnsupportedOperationException if the frame expects + * a header. + * + * @param payload payload as a byte array + */ + public void withOnlyPayload(byte[] payload) { + withOnlyPayload(payload, 0, payload.length); + } + + /** + * Provide only payload to the frame. Throws UnsupportedOperationException if the frame expects + * a header. + * + * @param payload The underlying byte array as a recipient of the payload + * @param offset The offset in the byte array starting from where the payload is located + * @param length The length of the payload + */ + public abstract void withOnlyPayload(byte[] payload, int offset, int length); + + protected abstract ByteBuffer buildFrame(byte[] header, int headerOffset, int headerLength, + byte[] payload, int payloadOffset, int payloadLength); + + /** + * Nonblocking write to the underlying transport. + * + * @throws TTransportException + */ + public void write(TNonblockingTransport transport) throws TTransportException { + transport.write(frameBytes); + } + + /** + * + * @return true when no more data needs to be written out + */ + public boolean isComplete() { + return frameBytes == null || !frameBytes.hasRemaining(); + } + + /** + * Release the byte buffer. + */ + public void clear() { + frameBytes = null; + } +} diff --git a/lib/java/src/org/apache/thrift/transport/sasl/NegotiationStatus.java b/lib/java/src/org/apache/thrift/transport/sasl/NegotiationStatus.java new file mode 100644 index 00000000000..ad704a0a1be --- /dev/null +++ b/lib/java/src/org/apache/thrift/transport/sasl/NegotiationStatus.java @@ -0,0 +1,61 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.thrift.transport.sasl; + +import java.util.HashMap; +import java.util.Map; + +import static org.apache.thrift.transport.sasl.TSaslNegotiationException.ErrorType.PROTOCOL_ERROR; + +/** + * Status bytes used during the initial Thrift SASL handshake. + */ +public enum NegotiationStatus { + START((byte)0x01), + OK((byte)0x02), + BAD((byte)0x03), + ERROR((byte)0x04), + COMPLETE((byte)0x05); + + private static final Map reverseMap = new HashMap<>(); + + static { + for (NegotiationStatus s : NegotiationStatus.values()) { + reverseMap.put(s.getValue(), s); + } + } + + private final byte value; + + NegotiationStatus(byte val) { + this.value = val; + } + + public byte getValue() { + return value; + } + + public static NegotiationStatus byValue(byte val) throws TSaslNegotiationException { + if (!reverseMap.containsKey(val)) { + throw new TSaslNegotiationException(PROTOCOL_ERROR, "Invalid status " + val); + } + return reverseMap.get(val); + } +} diff --git a/lib/java/src/org/apache/thrift/transport/sasl/NonblockingSaslHandler.java b/lib/java/src/org/apache/thrift/transport/sasl/NonblockingSaslHandler.java new file mode 100644 index 00000000000..d73c3ec18db --- /dev/null +++ b/lib/java/src/org/apache/thrift/transport/sasl/NonblockingSaslHandler.java @@ -0,0 +1,527 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.thrift.transport.sasl; + +import java.nio.channels.SelectionKey; +import java.nio.charset.StandardCharsets; + +import javax.security.sasl.SaslServer; + +import org.apache.thrift.TByteArrayOutputStream; +import org.apache.thrift.TProcessor; +import org.apache.thrift.protocol.TProtocol; +import org.apache.thrift.protocol.TProtocolFactory; +import org.apache.thrift.server.ServerContext; +import org.apache.thrift.server.TServerEventHandler; +import org.apache.thrift.transport.TMemoryTransport; +import org.apache.thrift.transport.TNonblockingTransport; +import org.apache.thrift.transport.TTransportException; +import org.apache.thrift.transport.sasl.TSaslNegotiationException.ErrorType; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import static org.apache.thrift.transport.sasl.NegotiationStatus.COMPLETE; +import static org.apache.thrift.transport.sasl.NegotiationStatus.OK; + +/** + * State machine managing one sasl connection in a nonblocking way. + */ +public class NonblockingSaslHandler { + private static final Logger LOGGER = LoggerFactory.getLogger(NonblockingSaslHandler.class); + + private static final int INTEREST_NONE = 0; + private static final int INTEREST_READ = SelectionKey.OP_READ; + private static final int INTEREST_WRITE = SelectionKey.OP_WRITE; + + // Tracking the current running phase + private Phase currentPhase = Phase.INITIIALIIZING; + // Tracking the next phase on the next invocation of the state machine. + // It should be the same as current phase if current phase is not yet finished. + // Otherwise, if it is different from current phase, the statemachine is in a transition state: + // current phase is done, and next phase is not yet started. + private Phase nextPhase = currentPhase; + + // Underlying nonblocking transport + private SelectionKey selectionKey; + private TNonblockingTransport underlyingTransport; + + // APIs for intercepting event / customizing behaviors: + // Factories (decorating the base implementations) & EventHandler (intercepting) + private TSaslServerFactory saslServerFactory; + private TSaslProcessorFactory processorFactory; + private TProtocolFactory inputProtocolFactory; + private TProtocolFactory outputProtocolFactory; + private TServerEventHandler eventHandler; + private ServerContext serverContext; + // It turns out the event handler implementation in hive sometimes creates a null ServerContext. + // In order to know whether TServerEventHandler#createContext is called we use such a flag. + private boolean serverContextCreated = false; + + // Wrapper around sasl server + private ServerSaslPeer saslPeer; + + // Sasl negotiation io + private SaslNegotiationFrameReader saslResponse; + private SaslNegotiationFrameWriter saslChallenge; + // IO for request from and response to the socket + private DataFrameReader requestReader; + private DataFrameWriter responseWriter; + // If sasl is negotiated for integrity/confidentiality protection + private boolean dataProtected; + + public NonblockingSaslHandler(SelectionKey selectionKey, TNonblockingTransport underlyingTransport, + TSaslServerFactory saslServerFactory, TSaslProcessorFactory processorFactory, + TProtocolFactory inputProtocolFactory, TProtocolFactory outputProtocolFactory, + TServerEventHandler eventHandler) { + this.selectionKey = selectionKey; + this.underlyingTransport = underlyingTransport; + this.saslServerFactory = saslServerFactory; + this.processorFactory = processorFactory; + this.inputProtocolFactory = inputProtocolFactory; + this.outputProtocolFactory = outputProtocolFactory; + this.eventHandler = eventHandler; + + saslResponse = new SaslNegotiationFrameReader(); + saslChallenge = new SaslNegotiationFrameWriter(); + requestReader = new DataFrameReader(); + responseWriter = new DataFrameWriter(); + } + + /** + * Get current phase of the state machine. + * + * @return current phase. + */ + public Phase getCurrentPhase() { + return currentPhase; + } + + /** + * Get next phase of the state machine. + * It is different from current phase iff current phase is done (and next phase not yet started). + * + * @return next phase. + */ + public Phase getNextPhase() { + return nextPhase; + } + + /** + * + * @return underlying nonblocking socket + */ + public TNonblockingTransport getUnderlyingTransport() { + return underlyingTransport; + } + + /** + * + * @return SaslServer instance + */ + public SaslServer getSaslServer() { + return saslPeer.getSaslServer(); + } + + /** + * + * @return true if current phase is done. + */ + public boolean isCurrentPhaseDone() { + return currentPhase != nextPhase; + } + + /** + * Run state machine. + * + * @throws IllegalStateException if current state is already done. + */ + public void runCurrentPhase() { + currentPhase.runStateMachine(this); + } + + /** + * When current phase is intrested in read selection, calling this will run the current phase and + * its following phases if the following ones are interested to read, until there is nothing + * available in the underlying transport. + * + * @throws IllegalStateException if is called in an irrelevant phase. + */ + public void handleRead() { + handleOps(INTEREST_READ); + } + + /** + * Similiar to handleRead. But it is for write ops. + * + * @throws IllegalStateException if it is called in an irrelevant phase. + */ + public void handleWrite() { + handleOps(INTEREST_WRITE); + } + + private void handleOps(int interestOps) { + if (currentPhase.selectionInterest != interestOps) { + throw new IllegalStateException("Current phase " + currentPhase + " but got interest " + + interestOps); + } + runCurrentPhase(); + if (isCurrentPhaseDone() && nextPhase.selectionInterest == interestOps) { + stepToNextPhase(); + handleOps(interestOps); + } + } + + /** + * When current phase is finished, it's expected to call this method first before running the + * state machine again. + * By calling this, "next phase" is marked as started (and not done), thus is ready to run. + * + * @throws IllegalArgumentException if current phase is not yet done. + */ + public void stepToNextPhase() { + if (!isCurrentPhaseDone()) { + throw new IllegalArgumentException("Not yet done with current phase: " + currentPhase); + } + LOGGER.debug("Switch phase {} to {}", currentPhase, nextPhase); + switch (nextPhase) { + case INITIIALIIZING: + throw new IllegalStateException("INITIALIZING cannot be the next phase of " + currentPhase); + default: + } + // If next phase's interest is not the same as current, nor the same as the selection key, + // we need to change interest on the selector. + if (!(nextPhase.selectionInterest == currentPhase.selectionInterest || + nextPhase.selectionInterest == selectionKey.interestOps())) { + changeSelectionInterest(nextPhase.selectionInterest); + } + currentPhase = nextPhase; + } + + private void changeSelectionInterest(int selectionInterest) { + selectionKey.interestOps(selectionInterest); + } + + // sasl negotiaion failure handling + private void failSaslNegotiation(TSaslNegotiationException e) { + LOGGER.error("Sasl negotiation failed", e); + String errorMsg = e.getDetails(); + saslChallenge.withHeaderAndPayload(new byte[]{e.getErrorType().code.getValue()}, + errorMsg.getBytes(StandardCharsets.UTF_8)); + nextPhase = Phase.WRITING_FAILURE_MESSAGE; + } + + private void fail(Exception e) { + LOGGER.error("Failed io in " + currentPhase, e); + nextPhase = Phase.CLOSING; + } + + private void failIO(TTransportException e) { + StringBuilder errorMsg = new StringBuilder("IO failure ") + .append(e.getType()) + .append(" in ") + .append(currentPhase); + if (e.getMessage() != null) { + errorMsg.append(": ").append(e.getMessage()); + } + LOGGER.error(errorMsg.toString(), e); + nextPhase = Phase.CLOSING; + } + + // Read handlings + + private void handleInitializing() { + try { + saslResponse.read(underlyingTransport); + if (saslResponse.isComplete()) { + SaslNegotiationHeaderReader startHeader = saslResponse.getHeader(); + if (startHeader.getStatus() != NegotiationStatus.START) { + throw new TInvalidSaslFrameException("Expecting START status but got " + startHeader.getStatus()); + } + String mechanism = new String(saslResponse.getPayload(), StandardCharsets.UTF_8); + saslPeer = saslServerFactory.getSaslPeer(mechanism); + saslResponse.clear(); + nextPhase = Phase.READING_SASL_RESPONSE; + } + } catch (TSaslNegotiationException e) { + failSaslNegotiation(e); + } catch (TTransportException e) { + failIO(e); + } + } + + private void handleReadingSaslResponse() { + try { + saslResponse.read(underlyingTransport); + if (saslResponse.isComplete()) { + nextPhase = Phase.EVALUATING_SASL_RESPONSE; + } + } catch (TSaslNegotiationException e) { + failSaslNegotiation(e); + } catch (TTransportException e) { + failIO(e); + } + } + + private void handleReadingRequest() { + try { + requestReader.read(underlyingTransport); + if (requestReader.isComplete()) { + nextPhase = Phase.PROCESSING; + } + } catch (TTransportException e) { + failIO(e); + } + } + + // Computation executions + + private void executeEvaluatingSaslResponse() { + if (!(saslResponse.getHeader().getStatus() == OK || saslResponse.getHeader().getStatus() == COMPLETE)) { + String error = "Expect status OK or COMPLETE, but got " + saslResponse.getHeader().getStatus(); + failSaslNegotiation(new TSaslNegotiationException(ErrorType.PROTOCOL_ERROR, error)); + return; + } + try { + byte[] response = saslResponse.getPayload(); + saslResponse.clear(); + byte[] newChallenge = saslPeer.evaluate(response); + if (saslPeer.isAuthenticated()) { + dataProtected = saslPeer.isDataProtected(); + saslChallenge.withHeaderAndPayload(new byte[]{COMPLETE.getValue()}, newChallenge); + nextPhase = Phase.WRITING_SUCCESS_MESSAGE; + } else { + saslChallenge.withHeaderAndPayload(new byte[]{OK.getValue()}, newChallenge); + nextPhase = Phase.WRITING_SASL_CHALLENGE; + } + } catch (TSaslNegotiationException e) { + failSaslNegotiation(e); + } + } + + private void executeProcessing() { + try { + byte[] inputPayload = requestReader.getPayload(); + requestReader.clear(); + byte[] rawInput = dataProtected ? saslPeer.unwrap(inputPayload) : inputPayload; + TMemoryTransport memoryTransport = new TMemoryTransport(rawInput); + TProtocol requestProtocol = inputProtocolFactory.getProtocol(memoryTransport); + TProtocol responseProtocol = outputProtocolFactory.getProtocol(memoryTransport); + + if (eventHandler != null) { + if (!serverContextCreated) { + serverContext = eventHandler.createContext(requestProtocol, responseProtocol); + serverContextCreated = true; + } + eventHandler.processContext(serverContext, memoryTransport, memoryTransport); + } + + TProcessor processor = processorFactory.getProcessor(this); + processor.process(requestProtocol, responseProtocol); + TByteArrayOutputStream rawOutput = memoryTransport.getOutput(); + if (rawOutput.len() == 0) { + // This is a oneway request, no response to send back. Waiting for next incoming request. + nextPhase = Phase.READING_REQUEST; + return; + } + if (dataProtected) { + byte[] outputPayload = saslPeer.wrap(rawOutput.get(), 0, rawOutput.len()); + responseWriter.withOnlyPayload(outputPayload); + } else { + responseWriter.withOnlyPayload(rawOutput.get(), 0 ,rawOutput.len()); + } + nextPhase = Phase.WRITING_RESPONSE; + } catch (TTransportException e) { + failIO(e); + } catch (Exception e) { + fail(e); + } + } + + // Write handlings + + private void handleWritingSaslChallenge() { + try { + saslChallenge.write(underlyingTransport); + if (saslChallenge.isComplete()) { + saslChallenge.clear(); + nextPhase = Phase.READING_SASL_RESPONSE; + } + } catch (TTransportException e) { + fail(e); + } + } + + private void handleWritingSuccessMessage() { + try { + saslChallenge.write(underlyingTransport); + if (saslChallenge.isComplete()) { + LOGGER.debug("Authentication is done."); + saslChallenge = null; + saslResponse = null; + nextPhase = Phase.READING_REQUEST; + } + } catch (TTransportException e) { + fail(e); + } + } + + private void handleWritingFailureMessage() { + try { + saslChallenge.write(underlyingTransport); + if (saslChallenge.isComplete()) { + nextPhase = Phase.CLOSING; + } + } catch (TTransportException e) { + fail(e); + } + } + + private void handleWritingResponse() { + try { + responseWriter.write(underlyingTransport); + if (responseWriter.isComplete()) { + responseWriter.clear(); + nextPhase = Phase.READING_REQUEST; + } + } catch (TTransportException e) { + fail(e); + } + } + + /** + * Release all the resources managed by this state machine (connection, selection and sasl server). + * To avoid being blocked, this should be invoked in the network thread that manages the selector. + */ + public void close() { + underlyingTransport.close(); + selectionKey.cancel(); + if (saslPeer != null) { + saslPeer.dispose(); + } + if (serverContextCreated) { + eventHandler.deleteContext(serverContext, + inputProtocolFactory.getProtocol(underlyingTransport), + outputProtocolFactory.getProtocol(underlyingTransport)); + } + nextPhase = Phase.CLOSED; + currentPhase = Phase.CLOSED; + LOGGER.trace("Connection closed: {}", underlyingTransport); + } + + public enum Phase { + INITIIALIIZING(INTEREST_READ) { + @Override + void unsafeRun(NonblockingSaslHandler statemachine) { + statemachine.handleInitializing(); + } + }, + READING_SASL_RESPONSE(INTEREST_READ) { + @Override + void unsafeRun(NonblockingSaslHandler statemachine) { + statemachine.handleReadingSaslResponse(); + } + }, + EVALUATING_SASL_RESPONSE(INTEREST_NONE) { + @Override + void unsafeRun(NonblockingSaslHandler statemachine) { + statemachine.executeEvaluatingSaslResponse(); + } + }, + WRITING_SASL_CHALLENGE(INTEREST_WRITE) { + @Override + void unsafeRun(NonblockingSaslHandler statemachine) { + statemachine.handleWritingSaslChallenge(); + } + }, + WRITING_SUCCESS_MESSAGE(INTEREST_WRITE) { + @Override + void unsafeRun(NonblockingSaslHandler statemachine) { + statemachine.handleWritingSuccessMessage(); + } + }, + WRITING_FAILURE_MESSAGE(INTEREST_WRITE) { + @Override + void unsafeRun(NonblockingSaslHandler statemachine) { + statemachine.handleWritingFailureMessage(); + } + }, + READING_REQUEST(INTEREST_READ) { + @Override + void unsafeRun(NonblockingSaslHandler statemachine) { + statemachine.handleReadingRequest(); + } + }, + PROCESSING(INTEREST_NONE) { + @Override + void unsafeRun(NonblockingSaslHandler statemachine) { + statemachine.executeProcessing(); + } + }, + WRITING_RESPONSE(INTEREST_WRITE) { + @Override + void unsafeRun(NonblockingSaslHandler statemachine) { + statemachine.handleWritingResponse(); + } + }, + CLOSING(INTEREST_NONE) { + @Override + void unsafeRun(NonblockingSaslHandler statemachine) { + statemachine.close(); + } + }, + CLOSED(INTEREST_NONE) { + @Override + void unsafeRun(NonblockingSaslHandler statemachine) { + // Do nothing. + } + } + ; + + // The interest on the selection key during the phase + private int selectionInterest; + + Phase(int selectionInterest) { + this.selectionInterest = selectionInterest; + } + + /** + * Provide the execution to run for the state machine in current phase. The execution should + * return the next phase after running on the state machine. + * + * @param statemachine The state machine to run. + * @throws IllegalArgumentException if the state machine's current phase is different. + * @throws IllegalStateException if the state machine' current phase is already done. + */ + void runStateMachine(NonblockingSaslHandler statemachine) { + if (statemachine.currentPhase != this) { + throw new IllegalArgumentException("State machine is " + statemachine.currentPhase + + " but is expected to be " + this); + } + if (statemachine.isCurrentPhaseDone()) { + throw new IllegalStateException("State machine should step into " + statemachine.nextPhase); + } + unsafeRun(statemachine); + } + + // Run the state machine without checkiing its own phase + // It should not be called direcly by users. + abstract void unsafeRun(NonblockingSaslHandler statemachine); + } +} diff --git a/lib/java/src/org/apache/thrift/transport/sasl/SaslNegotiationFrameReader.java b/lib/java/src/org/apache/thrift/transport/sasl/SaslNegotiationFrameReader.java new file mode 100644 index 00000000000..01c17283697 --- /dev/null +++ b/lib/java/src/org/apache/thrift/transport/sasl/SaslNegotiationFrameReader.java @@ -0,0 +1,30 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.thrift.transport.sasl; + +/** + * Read frames for sasl negotiatiions. + */ +public class SaslNegotiationFrameReader extends FrameReader { + + public SaslNegotiationFrameReader() { + super(new SaslNegotiationHeaderReader()); + } +} diff --git a/lib/java/src/org/apache/thrift/transport/sasl/SaslNegotiationFrameWriter.java b/lib/java/src/org/apache/thrift/transport/sasl/SaslNegotiationFrameWriter.java new file mode 100644 index 00000000000..1e9ad1570f9 --- /dev/null +++ b/lib/java/src/org/apache/thrift/transport/sasl/SaslNegotiationFrameWriter.java @@ -0,0 +1,56 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.thrift.transport.sasl; + +import java.nio.ByteBuffer; + +import org.apache.thrift.EncodingUtils; +import org.apache.thrift.utils.StringUtils; + +import static org.apache.thrift.transport.sasl.SaslNegotiationHeaderReader.PAYLOAD_LENGTH_BYTES; +import static org.apache.thrift.transport.sasl.SaslNegotiationHeaderReader.STATUS_BYTES; + +/** + * Writer for sasl negotiation frames. It expect a status byte as header with a payload to be + * written out (any header whose size is not equal to 1 would be considered as error). + */ +public class SaslNegotiationFrameWriter extends FrameWriter { + + public static final int HEADER_BYTES = STATUS_BYTES + PAYLOAD_LENGTH_BYTES; + + @Override + public void withOnlyPayload(byte[] payload, int offset, int length) { + throw new UnsupportedOperationException("Status byte is expected for sasl frame header."); + } + + @Override + protected ByteBuffer buildFrame(byte[] header, int headerOffset, int headerLength, + byte[] payload, int payloadOffset, int payloadLength) { + if (header == null || headerLength != STATUS_BYTES) { + throw new IllegalArgumentException("Header " + StringUtils.bytesToHexString(header) + + " does not have expected length " + STATUS_BYTES); + } + byte[] bytes = new byte[HEADER_BYTES + payloadLength]; + System.arraycopy(header, headerOffset, bytes, 0, STATUS_BYTES); + EncodingUtils.encodeBigEndian(payloadLength, bytes, STATUS_BYTES); + System.arraycopy(payload, payloadOffset, bytes, HEADER_BYTES, payloadLength); + return ByteBuffer.wrap(bytes); + } +} diff --git a/lib/java/src/org/apache/thrift/transport/sasl/SaslNegotiationHeaderReader.java b/lib/java/src/org/apache/thrift/transport/sasl/SaslNegotiationHeaderReader.java new file mode 100644 index 00000000000..2d76ddb2998 --- /dev/null +++ b/lib/java/src/org/apache/thrift/transport/sasl/SaslNegotiationHeaderReader.java @@ -0,0 +1,57 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.thrift.transport.sasl; + +import static org.apache.thrift.transport.sasl.TSaslNegotiationException.ErrorType.PROTOCOL_ERROR; + +/** + * Header for sasl negotiation frames. It contains status byte of negotiation and a 4-byte integer + * (payload size). + */ +public class SaslNegotiationHeaderReader extends FixedSizeHeaderReader { + public static final int STATUS_BYTES = 1; + public static final int PAYLOAD_LENGTH_BYTES = 4; + + private NegotiationStatus negotiationStatus; + private int payloadSize; + + @Override + protected int headerSize() { + return STATUS_BYTES + PAYLOAD_LENGTH_BYTES; + } + + @Override + protected void onComplete() throws TSaslNegotiationException { + negotiationStatus = NegotiationStatus.byValue(byteBuffer.get(0)); + payloadSize = byteBuffer.getInt(1); + if (payloadSize < 0) { + throw new TSaslNegotiationException(PROTOCOL_ERROR, "Payload size is negative: " + payloadSize); + } + } + + @Override + public int payloadSize() { + return payloadSize; + } + + public NegotiationStatus getStatus() { + return negotiationStatus; + } +} diff --git a/lib/java/src/org/apache/thrift/transport/sasl/SaslPeer.java b/lib/java/src/org/apache/thrift/transport/sasl/SaslPeer.java new file mode 100644 index 00000000000..8f81380441c --- /dev/null +++ b/lib/java/src/org/apache/thrift/transport/sasl/SaslPeer.java @@ -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. + */ + +package org.apache.thrift.transport.sasl; + +import org.apache.thrift.transport.TTransportException; + +/** + * A peer in a sasl negotiation. + */ +public interface SaslPeer { + + /** + * Evaluate and validate the negotiation message (response/challenge) received from peer. + * + * @param negotiationMessage response/challenge received from peer. + * @return new response/challenge to send to peer, can be null if authentication becomes success. + * @throws TSaslNegotiationException if sasl authentication fails. + */ + byte[] evaluate(byte[] negotiationMessage) throws TSaslNegotiationException; + + /** + * @return true if authentication is done. + */ + boolean isAuthenticated(); + + /** + * This method can only be called when the negotiation is complete (isAuthenticated returns true). + * Otherwise it will throw IllegalStateExceptiion. + * + * @return if the qop requires some integrity/confidential protection. + * @throws IllegalStateException if negotiation is not yet complete. + */ + boolean isDataProtected(); + + /** + * Wrap raw bytes to protect it. + * + * @param data raw bytes. + * @param offset the start position of the content to wrap. + * @param length the length of the content to wrap. + * @return bytes with protection to send to peer. + * @throws TTransportException if failure. + */ + byte[] wrap(byte[] data, int offset, int length) throws TTransportException; + + /** + * Wrap the whole byte array. + * + * @param data raw bytes. + * @return wrapped bytes. + * @throws TTransportException if failure. + */ + default byte[] wrap(byte[] data) throws TTransportException { + return wrap(data, 0, data.length); + } + + /** + * Unwrap protected data to raw bytes. + * + * @param data protected data received from peer. + * @param offset the start position of the content to unwrap. + * @param length the length of the content to unwrap. + * @return raw bytes. + * @throws TTransportException if failed. + */ + byte[] unwrap(byte[] data, int offset, int length) throws TTransportException; + + /** + * Unwrap the whole byte array. + * + * @param data wrapped bytes. + * @return raw bytes. + * @throws TTransportException if failure. + */ + default byte[] unwrap(byte[] data) throws TTransportException { + return unwrap(data, 0, data.length); + } + + /** + * Close this peer and release resources. + */ + void dispose(); +} diff --git a/lib/java/src/org/apache/thrift/transport/sasl/ServerSaslPeer.java b/lib/java/src/org/apache/thrift/transport/sasl/ServerSaslPeer.java new file mode 100644 index 00000000000..31992e5fc47 --- /dev/null +++ b/lib/java/src/org/apache/thrift/transport/sasl/ServerSaslPeer.java @@ -0,0 +1,108 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.thrift.transport.sasl; + +import javax.security.sasl.Sasl; +import javax.security.sasl.SaslException; +import javax.security.sasl.SaslServer; + +import org.apache.thrift.transport.TTransportException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import static org.apache.thrift.transport.sasl.TSaslNegotiationException.ErrorType.AUTHENTICATION_FAILURE; + +/** + * Server side sasl peer, a wrapper around SaslServer to provide some handy methods. + */ +public class ServerSaslPeer implements SaslPeer { + private static final Logger LOGGER = LoggerFactory.getLogger(ServerSaslPeer.class); + + private static final String QOP_AUTH_INT = "auth-int"; + private static final String QOP_AUTH_CONF = "auth-conf"; + + private final SaslServer saslServer; + + public ServerSaslPeer(SaslServer saslServer) { + this.saslServer = saslServer; + } + + @Override + public byte[] evaluate(byte[] negotiationMessage) throws TSaslNegotiationException { + try { + return saslServer.evaluateResponse(negotiationMessage); + } catch (SaslException e) { + throw new TSaslNegotiationException(AUTHENTICATION_FAILURE, + "Authentication failed with " + saslServer.getMechanismName(), e); + } + } + + @Override + public boolean isAuthenticated() { + return saslServer.isComplete(); + } + + @Override + public boolean isDataProtected() { + Object qop = saslServer.getNegotiatedProperty(Sasl.QOP); + if (qop == null) { + return false; + } + for (String word : qop.toString().split("\\s*,\\s*")) { + String lowerCaseWord = word.toLowerCase(); + if (QOP_AUTH_INT.equals(lowerCaseWord) || QOP_AUTH_CONF.equals(lowerCaseWord)) { + return true; + } + } + return false; + } + + @Override + public byte[] wrap(byte[] data, int offset, int length) throws TTransportException { + try { + return saslServer.wrap(data, offset, length); + } catch (SaslException e) { + throw new TTransportException("Failed to wrap data", e); + } + } + + @Override + public byte[] unwrap(byte[] data, int offset, int length) throws TTransportException { + try { + return saslServer.unwrap(data, offset, length); + } catch (SaslException e) { + throw new TTransportException(TTransportException.CORRUPTED_DATA, "Failed to unwrap data", e); + } + } + + @Override + public void dispose() { + try { + saslServer.dispose(); + } catch (Exception e) { + LOGGER.warn("Failed to close sasl server " + saslServer.getMechanismName(), e); + } + } + + SaslServer getSaslServer() { + return saslServer; + } + +} diff --git a/lib/cocoa/src/TProcessorFactory.h b/lib/java/src/org/apache/thrift/transport/sasl/TBaseSaslProcessorFactory.java similarity index 67% rename from lib/cocoa/src/TProcessorFactory.h rename to lib/java/src/org/apache/thrift/transport/sasl/TBaseSaslProcessorFactory.java index 85020a59346..c08884c22ea 100644 --- a/lib/cocoa/src/TProcessorFactory.h +++ b/lib/java/src/org/apache/thrift/transport/sasl/TBaseSaslProcessorFactory.java @@ -17,17 +17,20 @@ * under the License. */ -#import -#import "TProcessor.h" +package org.apache.thrift.transport.sasl; -NS_ASSUME_NONNULL_BEGIN +import org.apache.thrift.TProcessor; +public class TBaseSaslProcessorFactory implements TSaslProcessorFactory { -@protocol TProcessorFactory + private final TProcessor processor; --(id) processorForTransport:(id)transport; + public TBaseSaslProcessorFactory(TProcessor processor) { + this.processor = processor; + } -@end - - -NS_ASSUME_NONNULL_END + @Override + public TProcessor getProcessor(NonblockingSaslHandler saslHandler) { + return processor; + } +} diff --git a/lib/java/src/org/apache/thrift/transport/sasl/TInvalidSaslFrameException.java b/lib/java/src/org/apache/thrift/transport/sasl/TInvalidSaslFrameException.java new file mode 100644 index 00000000000..ff57ea5c435 --- /dev/null +++ b/lib/java/src/org/apache/thrift/transport/sasl/TInvalidSaslFrameException.java @@ -0,0 +1,30 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.thrift.transport.sasl; + +/** + * Got an invalid frame that does not respect the thrift sasl protocol. + */ +public class TInvalidSaslFrameException extends TSaslNegotiationException { + + public TInvalidSaslFrameException(String message) { + super(ErrorType.PROTOCOL_ERROR, message); + } +} diff --git a/lib/java/src/org/apache/thrift/transport/sasl/TSaslNegotiationException.java b/lib/java/src/org/apache/thrift/transport/sasl/TSaslNegotiationException.java new file mode 100644 index 00000000000..9b1fa060ed5 --- /dev/null +++ b/lib/java/src/org/apache/thrift/transport/sasl/TSaslNegotiationException.java @@ -0,0 +1,76 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.thrift.transport.sasl; + +import org.apache.thrift.transport.TTransportException; + +/** + * Exception for sasl negotiation errors. + */ +public class TSaslNegotiationException extends TTransportException { + + private final ErrorType error; + + public TSaslNegotiationException(ErrorType error, String summary) { + super(summary); + this.error = error; + } + + public TSaslNegotiationException(ErrorType error, String summary, Throwable cause) { + super(summary, cause); + this.error = error; + } + + public ErrorType getErrorType() { + return error; + } + + /** + * @return Errory type plus the message. + */ + public String getSummary() { + return error.name() + ": " + getMessage(); + } + + /** + * @return Summary and eventually the cause's message. + */ + public String getDetails() { + return getCause() == null ? getSummary() : getSummary() + "\nReason: " + getCause().getMessage(); + } + + public enum ErrorType { + // Unexpected system internal error during negotiation (e.g. sasl initialization failure) + INTERNAL_ERROR(NegotiationStatus.ERROR), + // Cannot read correct sasl frames from the connection => Send "ERROR" status byte to peer + PROTOCOL_ERROR(NegotiationStatus.ERROR), + // Peer is using unsupported sasl mechanisms => Send "BAD" status byte to peer + MECHANISME_MISMATCH(NegotiationStatus.BAD), + // Sasl authentication failure => Send "BAD" status byte to peer + AUTHENTICATION_FAILURE(NegotiationStatus.BAD), + ; + + public final NegotiationStatus code; + + ErrorType(NegotiationStatus code) { + this.code = code; + } + } +} diff --git a/lib/java/src/org/apache/thrift/transport/sasl/TSaslProcessorFactory.java b/lib/java/src/org/apache/thrift/transport/sasl/TSaslProcessorFactory.java new file mode 100644 index 00000000000..877d0496f2a --- /dev/null +++ b/lib/java/src/org/apache/thrift/transport/sasl/TSaslProcessorFactory.java @@ -0,0 +1,32 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.thrift.transport.sasl; + +import org.apache.thrift.TException; +import org.apache.thrift.TProcessor; + +/** + * Get processor for a given state machine, so that users can customize the behavior of a TProcessor + * by interacting with the state machine. + */ +public interface TSaslProcessorFactory { + + TProcessor getProcessor(NonblockingSaslHandler saslHandler) throws TException; +} diff --git a/lib/cocoa/src/protocol/TBase.h b/lib/java/src/org/apache/thrift/transport/sasl/TSaslServerDefinition.java similarity index 53% rename from lib/cocoa/src/protocol/TBase.h rename to lib/java/src/org/apache/thrift/transport/sasl/TSaslServerDefinition.java index 9935d506842..5486641d8e7 100644 --- a/lib/cocoa/src/protocol/TBase.h +++ b/lib/java/src/org/apache/thrift/transport/sasl/TSaslServerDefinition.java @@ -17,30 +17,27 @@ * under the License. */ -#import +package org.apache.thrift.transport.sasl; -#import "TProtocol.h" - -@protocol TBase - -/** - * De-serialize object from the given input protocol - * - * @param inProtocol protocol used for reading - */ --(BOOL) read:(id )inProtocol error:(NSError **)error; +import javax.security.auth.callback.CallbackHandler; +import java.util.Map; /** - * Serialize object to the given protocol - * - * @param outProtocol output protocol used for writing + * Contains all the parameters used to define a SASL server implementation. */ --(BOOL) write:(id )outProtocol error:(NSError **)error; - - -/** - * Validate required fields - */ --(BOOL) validate:(NSError *__autoreleasing *)__thriftError; - -@end +public class TSaslServerDefinition { + public final String mechanism; + public final String protocol; + public final String serverName; + public final Map props; + public final CallbackHandler cbh; + + public TSaslServerDefinition(String mechanism, String protocol, String serverName, + Map props, CallbackHandler cbh) { + this.mechanism = mechanism; + this.protocol = protocol; + this.serverName = serverName; + this.props = props; + this.cbh = cbh; + } +} diff --git a/lib/java/src/org/apache/thrift/transport/sasl/TSaslServerFactory.java b/lib/java/src/org/apache/thrift/transport/sasl/TSaslServerFactory.java new file mode 100644 index 00000000000..06cf534b610 --- /dev/null +++ b/lib/java/src/org/apache/thrift/transport/sasl/TSaslServerFactory.java @@ -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. + */ + +package org.apache.thrift.transport.sasl; + +import java.util.HashMap; +import java.util.Map; + +import javax.security.auth.callback.CallbackHandler; +import javax.security.sasl.Sasl; +import javax.security.sasl.SaslException; +import javax.security.sasl.SaslServer; + +import static org.apache.thrift.transport.sasl.TSaslNegotiationException.ErrorType.MECHANISME_MISMATCH; +import static org.apache.thrift.transport.sasl.TSaslNegotiationException.ErrorType.PROTOCOL_ERROR; + +/** + * Factory to create sasl server. Users can extend this class to customize the SaslServer creation. + */ +public class TSaslServerFactory { + + private final Map saslMechanisms; + + public TSaslServerFactory() { + this.saslMechanisms = new HashMap<>(); + } + + public void addSaslMechanism(String mechanism, String protocol, String serverName, + Map props, CallbackHandler cbh) { + TSaslServerDefinition definition = new TSaslServerDefinition(mechanism, protocol, serverName, + props, cbh); + saslMechanisms.put(definition.mechanism, definition); + } + + public ServerSaslPeer getSaslPeer(String mechanism) throws TSaslNegotiationException { + if (!saslMechanisms.containsKey(mechanism)) { + throw new TSaslNegotiationException(MECHANISME_MISMATCH, "Unsupported mechanism " + mechanism); + } + TSaslServerDefinition saslDef = saslMechanisms.get(mechanism); + try { + SaslServer saslServer = Sasl.createSaslServer(saslDef.mechanism, saslDef.protocol, + saslDef.serverName, saslDef.props, saslDef.cbh); + return new ServerSaslPeer(saslServer); + } catch (SaslException e) { + throw new TSaslNegotiationException(PROTOCOL_ERROR, "Fail to create sasl server " + mechanism, e); + } + } +} diff --git a/lib/java/src/org/apache/thrift/utils/StringUtils.java b/lib/java/src/org/apache/thrift/utils/StringUtils.java new file mode 100644 index 00000000000..9b9671b6999 --- /dev/null +++ b/lib/java/src/org/apache/thrift/utils/StringUtils.java @@ -0,0 +1,69 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.thrift.utils; + +public final class StringUtils { + + private StringUtils() { + // Utility class. + } + + private static final char[] HEX_CHARS = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'}; + + /** + * Stringify a byte array to the hex representation for each byte. + * + * @param bytes + * @return hex string. + */ + public static String bytesToHexString(byte[] bytes) { + if (bytes == null) { + return null; + } + return bytesToHexString(bytes, 0, bytes.length); + } + + /** + * Stringify a portion of the byte array. + * + * @param bytes byte array. + * @param offset portion start. + * @param length portion length. + * @return hex string. + */ + public static String bytesToHexString(byte[] bytes, int offset, int length) { + if (length < 0) { + throw new IllegalArgumentException("Negative length " + length); + } + if (offset < 0) { + throw new IndexOutOfBoundsException("Negative start offset " + offset); + } + if (length > bytes.length - offset) { + throw new IndexOutOfBoundsException("Invalid range, bytes.length: " + bytes.length + " offset: " + offset + " length: " + length); + } + char[] chars = new char[length * 2]; + for (int i = 0; i < length; i++) { + int unsignedInt = bytes[i + offset] & 0xFF; + chars[2 * i] = HEX_CHARS[unsignedInt >>> 4]; + chars[2 * i + 1] = HEX_CHARS[unsignedInt & 0x0F]; + } + return new String(chars); + } +} diff --git a/lib/java/test/org/apache/thrift/Fixtures.java b/lib/java/test/org/apache/thrift/Fixtures.java index 81671d8de56..61f40a590f9 100644 --- a/lib/java/test/org/apache/thrift/Fixtures.java +++ b/lib/java/test/org/apache/thrift/Fixtures.java @@ -20,6 +20,7 @@ package org.apache.thrift; import java.nio.ByteBuffer; +import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; @@ -267,7 +268,7 @@ public class Fixtures { oneOfEach.setInteger64((long) 6000 * 1000 * 1000); oneOfEach.setDouble_precision(Math.PI); oneOfEach.setSome_characters("JSON THIS! \"\1"); - oneOfEach.setZomg_unicode(new String(kUnicodeBytes, "UTF-8")); + oneOfEach.setZomg_unicode(new String(kUnicodeBytes, StandardCharsets.UTF_8)); oneOfEach.setBase64(ByteBuffer.wrap("base64".getBytes())); // byte, i16, and i64 lists are populated by default constructor diff --git a/lib/java/test/org/apache/thrift/TestMultiplexedProcessor.java b/lib/java/test/org/apache/thrift/TestMultiplexedProcessor.java index 01776ca3952..85e7966bf2a 100644 --- a/lib/java/test/org/apache/thrift/TestMultiplexedProcessor.java +++ b/lib/java/test/org/apache/thrift/TestMultiplexedProcessor.java @@ -57,13 +57,12 @@ public void testNoSuchService() throws TException { static class StubProcessor implements TProcessor { @Override - public boolean process(TProtocol in, TProtocol out) throws TException { + public void process(TProtocol in, TProtocol out) throws TException { TMessage msg = in.readMessageBegin(); if (!"func".equals(msg.name) || msg.type!=TMessageType.CALL || msg.seqid!=42) { throw new TException("incorrect parameters"); } out.writeMessageBegin(new TMessage("func", TMessageType.REPLY, 42)); - return true; } } diff --git a/lib/java/test/org/apache/thrift/TestOptionals.java b/lib/java/test/org/apache/thrift/TestOptionals.java index d1591ee2cf2..f3d4cfcf674 100644 --- a/lib/java/test/org/apache/thrift/TestOptionals.java +++ b/lib/java/test/org/apache/thrift/TestOptionals.java @@ -34,8 +34,8 @@ public void testEncodingUtils() throws Exception { assertEquals(true, EncodingUtils.testBit((short)0x8, 3)); assertEquals(false, EncodingUtils.testBit((short)0x8, 4)); - assertEquals((short)Short.MIN_VALUE, EncodingUtils.setBit((short)0, 15, true)); - assertEquals((short)0, EncodingUtils.setBit((short)Short.MIN_VALUE, 15, false)); + assertEquals(Short.MIN_VALUE, EncodingUtils.setBit((short)0, 15, true)); + assertEquals((short)0, EncodingUtils.setBit(Short.MIN_VALUE, 15, false)); assertEquals(true, EncodingUtils.testBit(Short.MIN_VALUE, 15)); assertEquals(false, EncodingUtils.testBit(Short.MIN_VALUE, 14)); } diff --git a/lib/java/test/org/apache/thrift/TestReuse.java b/lib/java/test/org/apache/thrift/TestReuse.java index b44abd0d209..2482d392547 100644 --- a/lib/java/test/org/apache/thrift/TestReuse.java +++ b/lib/java/test/org/apache/thrift/TestReuse.java @@ -35,21 +35,21 @@ public void testReuseObject() throws Exception { Reuse ru1 = new Reuse(); HashSet hs1 = new HashSet(); - byte[] serBytes; + byte[] serBytes; String st1 = new String("string1"); String st2 = new String("string2"); ru1.setVal1(11); ru1.setVal2(hs1); ru1.addToVal2(st1); - + serBytes = binarySerializer.serialize(ru1); // update hash set after serialization hs1.add(st2); binaryDeserializer.deserialize(ru1, serBytes); - + assertTrue( ru1.getVal2() == hs1 ); assertTrue( hs1.size() == 2 ); } diff --git a/lib/java/test/org/apache/thrift/TestShortStack.java b/lib/java/test/org/apache/thrift/TestShortStack.java deleted file mode 100644 index 07831e526f0..00000000000 --- a/lib/java/test/org/apache/thrift/TestShortStack.java +++ /dev/null @@ -1,116 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -package org.apache.thrift; - -import java.util.Stack; - -import junit.framework.TestCase; - -public class TestShortStack extends TestCase { - private static final int NUM_TRIALS = 5; - private static final int NUM_REPS = 10000000; - - public void testOps() throws Exception { - ShortStack s = new ShortStack(10); - s.push((short)10); - s.push((short)11); - s.push((short)12); - assertEquals((short)12, s.peek()); - assertEquals((short)12, s.peek()); - assertEquals((short)12, s.pop()); - assertEquals((short)11, s.pop()); - s.push((short)40); - assertEquals((short)40, s.peek()); - assertEquals((short)40, s.pop()); - assertEquals((short)10, s.peek()); - assertEquals((short)10, s.pop()); - try { - s.peek(); - fail("should have thrown an exception!"); - } catch (Exception e) { - // yay - } - - try { - s.pop(); - fail("should have thrown an exception!"); - } catch (Exception e) { - // yay - } - } - - public void testGrow() throws Exception { - ShortStack s = new ShortStack(1); - s.push((short)1); - s.push((short)1); - s.push((short)1); - s.push((short)1); - s.push((short)1); - } - - public static void main(String[] args) throws Exception { - for (int trial = 0; trial < NUM_TRIALS; trial++) { - long start = System.currentTimeMillis(); - ShortStack s = new ShortStack(10); - for (int rep = 0; rep < NUM_REPS; rep++) { - s.push((short)1); - s.push((short)11); - s.push((short)111); - s.pop(); - s.pop(); - s.push((short)12); - s.push((short)121); - s.push((short)1211); - s.push((short)12111); - s.pop(); - s.pop(); - s.pop(); - s.pop(); - s.push((short)5); - s.pop(); - s.pop(); - } - long end = System.currentTimeMillis(); - System.out.println("ShortStack: " + (end-start)); - - start = System.currentTimeMillis(); - Stack stdStack = new Stack(); - for (int rep = 0; rep < NUM_REPS; rep++) { - stdStack.push((short)1); - stdStack.push((short)11); - stdStack.push((short)111); - stdStack.pop(); - stdStack.pop(); - stdStack.push((short)12); - stdStack.push((short)121); - stdStack.push((short)1211); - stdStack.push((short)12111); - stdStack.pop(); - stdStack.pop(); - stdStack.pop(); - stdStack.pop(); - stdStack.push((short)5); - stdStack.pop(); - stdStack.pop(); - } - end = System.currentTimeMillis(); - System.out.println("Built-in stack: " + (end-start)); - } - } -} diff --git a/lib/java/test/org/apache/thrift/async/TestTAsyncClientManager.java b/lib/java/test/org/apache/thrift/async/TestTAsyncClientManager.java index c483cf24ee5..0a20e522bea 100644 --- a/lib/java/test/org/apache/thrift/async/TestTAsyncClientManager.java +++ b/lib/java/test/org/apache/thrift/async/TestTAsyncClientManager.java @@ -40,6 +40,7 @@ import org.apache.thrift.transport.TNonblockingServerSocket; import org.apache.thrift.transport.TNonblockingSocket; +import org.apache.thrift.transport.TTransportException; import thrift.test.CompactProtoTestStruct; import thrift.test.ExceptionWithAMap; import thrift.test.Srv; @@ -233,7 +234,7 @@ public void testParallelCalls() throws Exception { assertEquals(numThreads * numCallsPerThread, numSuccesses); } - private Srv.AsyncClient getClient() throws IOException { + private Srv.AsyncClient getClient() throws IOException, TTransportException { TNonblockingSocket clientSocket = new TNonblockingSocket(ServerTestBase.HOST, ServerTestBase.PORT); return new Srv.AsyncClient(new TBinaryProtocol.Factory(), clientManager_, clientSocket); } diff --git a/lib/java/test/org/apache/thrift/protocol/ProtocolTestBase.java b/lib/java/test/org/apache/thrift/protocol/ProtocolTestBase.java index 0386d839371..52b107441d1 100644 --- a/lib/java/test/org/apache/thrift/protocol/ProtocolTestBase.java +++ b/lib/java/test/org/apache/thrift/protocol/ProtocolTestBase.java @@ -18,24 +18,26 @@ */ package org.apache.thrift.protocol; +import java.lang.Exception; +import java.lang.Integer; +import java.lang.String; import java.nio.ByteBuffer; import java.util.Arrays; +import java.util.HashMap; import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.stream.Collectors; +import java.util.stream.Stream; import junit.framework.TestCase; -import org.apache.thrift.Fixtures; -import org.apache.thrift.TBase; -import org.apache.thrift.TDeserializer; -import org.apache.thrift.TException; -import org.apache.thrift.TSerializer; +import org.apache.thrift.*; +import org.apache.thrift.server.ServerTestBase; import org.apache.thrift.transport.TMemoryBuffer; -import thrift.test.CompactProtoTestStruct; -import thrift.test.HolyMoley; -import thrift.test.Nesting; -import thrift.test.OneOfEach; -import thrift.test.Srv; +import org.apache.thrift.transport.TTransportException; +import thrift.test.*; public abstract class ProtocolTestBase extends TestCase { @@ -409,7 +411,7 @@ protected void benchmark() throws Exception { } long serEnd = System.currentTimeMillis(); long serElapsed = serEnd - serStart; - System.out.println("Ser:\t" + serElapsed + "ms\t" + System.out.println("Ser:\t" + serElapsed + "ms\t" + ((double)serElapsed / NUM_REPS) + "ms per serialization"); HolyMoley cpts = new HolyMoley(); @@ -420,8 +422,109 @@ protected void benchmark() throws Exception { } long deserEnd = System.currentTimeMillis(); long deserElapsed = deserEnd - deserStart; - System.out.println("Des:\t" + deserElapsed + "ms\t" + System.out.println("Des:\t" + deserElapsed + "ms\t" + ((double)deserElapsed / NUM_REPS) + "ms per deserialization"); } } + + private ServerTestBase.TestHandler testHandler = new ServerTestBase.TestHandler() { + @Override + public String testString(String thing) { + thing = thing + " Apache Thrift Java " + thing; + return thing; + } + + @Override + public List testList(List thing) { + thing.addAll(thing); + thing.addAll(thing); + return thing; + } + + @Override + public Set testSet(Set thing) { + thing.addAll(thing.stream().map( x -> x + 100).collect(Collectors.toSet())); + return thing; + } + + @Override + public Map testStringMap(Map thing) { + thing.put("a", "123"); + thing.put(" x y ", " with spaces "); + thing.put("same", "same"); + thing.put("0", "numeric key"); + thing.put("1", ""); + thing.put("ok", "2355555"); + thing.put("end", "0"); + return thing; + } + }; + + private TProtocol initConfig(int maxSize) throws TException{ + TConfiguration config = TConfiguration.custom().setMaxMessageSize(maxSize).build(); + TMemoryBuffer bufferTrans = new TMemoryBuffer(config, 0); + return getFactory().getProtocol(bufferTrans); + } + + public void testReadCheckMaxMessageRequestForString() throws TException{ + TProtocol clientOutProto = initConfig(15); + TProtocol clientInProto = initConfig(15); + ThriftTest.Client testClient = new ThriftTest.Client(clientInProto, clientOutProto); + ThriftTest.Processor testProcessor = new ThriftTest.Processor(testHandler); + try { + testClient.send_testString("test"); + testProcessor.process(clientOutProto, clientInProto); + String result = testClient.recv_testString(); + System.out.println("----result: "+result); + } catch (TException e) { + assertEquals("MaxMessageSize reached", e.getMessage()); + } + } + + public void testReadCheckMaxMessageRequestForList() throws TException{ + TProtocol clientOutProto = initConfig(15); + TProtocol clientInProto = initConfig(15); + ThriftTest.Client testClient = new ThriftTest.Client(clientInProto, clientOutProto); + ThriftTest.Processor testProcessor = new ThriftTest.Processor(testHandler); + try { + testClient.send_testList(Arrays.asList(1, 23242346, 888888, 90)); + testProcessor.process(clientOutProto, clientInProto); + testClient.recv_testList(); + fail("Limitations not achieved as expected"); + } catch (TTransportException e) { + assertEquals("MaxMessageSize reached", e.getMessage()); + } + } + + public void testReadCheckMaxMessageRequestForMap() throws TException{ + TProtocol clientOutProto = initConfig(13); + TProtocol clientInProto = initConfig(13); + ThriftTest.Client testClient = new ThriftTest.Client(clientInProto, clientOutProto); + ThriftTest.Processor testProcessor = new ThriftTest.Processor(testHandler); + Map thing = new HashMap<>(); + thing.put("key", "Thrift"); + try { + testClient.send_testStringMap(thing); + testProcessor.process(clientOutProto, clientInProto); + testClient.recv_testStringMap(); + fail("Limitations not achieved as expected"); + } catch (TTransportException e) { + assertEquals("MaxMessageSize reached", e.getMessage()); + } + } + + public void testReadCheckMaxMessageRequestForSet() throws TException{ + TProtocol clientOutProto = initConfig(10); + TProtocol clientInProto = initConfig(10); + ThriftTest.Client testClient = new ThriftTest.Client(clientInProto, clientOutProto); + ThriftTest.Processor testProcessor = new ThriftTest.Processor(testHandler); + try { + testClient.send_testSet(Stream.of(234, 0, 987087, 45, 88888888, 9).collect(Collectors.toSet())); + testProcessor.process(clientOutProto, clientInProto); + testClient.recv_testSet(); + fail("Limitations not achieved as expected"); + } catch (TTransportException e) { + assertEquals("MaxMessageSize reached", e.getMessage()); + } + } } diff --git a/lib/csharp/test/Multiplex/Multiplex.Test.Common.cs b/lib/java/test/org/apache/thrift/protocol/TestShortStack.java similarity index 59% rename from lib/csharp/test/Multiplex/Multiplex.Test.Common.cs rename to lib/java/test/org/apache/thrift/protocol/TestShortStack.java index a687852d91c..c8e78eee6a5 100644 --- a/lib/csharp/test/Multiplex/Multiplex.Test.Common.cs +++ b/lib/java/test/org/apache/thrift/protocol/TestShortStack.java @@ -16,25 +16,27 @@ * specific language governing permissions and limitations * under the License. */ +package org.apache.thrift.protocol; -// Distributed under the Thrift Software License -// -// See accompanying file LICENSE or visit the Thrift site at: -// http://developers.facebook.com/thrift/ -using System; -using System.Collections.Generic; -using Thrift.Collections; -using Thrift.Transport; -using Thrift.Protocol; -using Thrift.Server; +import junit.framework.TestCase; -namespace Test.Multiplex -{ - public class Constants - { - public const string NAME_BENCHMARKSERVICE = "BenchmarkService"; - public const string NAME_AGGR = "Aggr"; +public class TestShortStack extends TestCase { + + public void testOps() throws Exception { + ShortStack s = new ShortStack(1); + s.push((short)10); + s.push((short)11); + s.push((short)12); + assertEquals((short)12, s.pop()); + assertEquals((short)11, s.pop()); + s.push((short)40); + assertEquals((short)40, s.pop()); + assertEquals((short)10, s.pop()); + try { + s.pop(); + fail("should have thrown an exception!"); + } catch (Exception e) { + // yay } + } } - - diff --git a/lib/cocoa/src/TProcessor.h b/lib/java/test/org/apache/thrift/protocol/TestTBinaryProtocol.java similarity index 68% rename from lib/cocoa/src/TProcessor.h rename to lib/java/test/org/apache/thrift/protocol/TestTBinaryProtocol.java index 20c72e2e1a4..67220b0f5d9 100644 --- a/lib/cocoa/src/TProcessor.h +++ b/lib/java/test/org/apache/thrift/protocol/TestTBinaryProtocol.java @@ -17,19 +17,22 @@ * under the License. */ -#import -#import "TProtocol.h" -NS_ASSUME_NONNULL_BEGIN +package org.apache.thrift.protocol; +import org.apache.thrift.TDeserializer; +import org.apache.thrift.TException; +import thrift.test.Bonk; -@protocol TProcessor +public class TestTBinaryProtocol extends ProtocolTestBase { + @Override + protected TProtocolFactory getFactory() { + return new TBinaryProtocol.Factory(); + } --(BOOL) processOnInputProtocol:(id )inProtocol - outputProtocol:(id )outProtocol - error:(NSError **)error; + @Override + protected boolean canBeUsedNaked() { + return true; + } -@end - - -NS_ASSUME_NONNULL_END +} diff --git a/lib/java/test/org/apache/thrift/protocol/TestTField.java b/lib/java/test/org/apache/thrift/protocol/TestTField.java new file mode 100644 index 00000000000..f72c25972ae --- /dev/null +++ b/lib/java/test/org/apache/thrift/protocol/TestTField.java @@ -0,0 +1,60 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.thrift.protocol; + +import junit.framework.TestCase; +import static org.junit.Assert.assertNotEquals; + +public abstract class TestTField extends TestCase { + + public void testConstructor() { + TField uut = new TField(); + assertEquals("", uut.name); + assertEquals(TType.STOP, uut.type); + assertEquals(0, uut.id); + + uut = new TField("foo", TType.VOID, (short)42); + assertEquals("foo", uut.name); + assertEquals(TType.VOID, uut.type); + assertEquals(42, uut.id); + } + + public void testEquality() { + TField uut1 = new TField(); + TField uut2 = new TField(); + assertEquals(uut1, uut2); + assertEquals(uut1.hashCode(), uut2.hashCode()); + + uut1 = new TField("foo", TType.I32, (short)1); + uut2 = new TField("foo", TType.I32, (short)2); + assertNotEquals(uut1, uut2); + assertNotEquals(uut1.hashCode(), uut2.hashCode()); + + uut1 = new TField("foo", TType.VOID, (short)1); + uut2 = new TField("foo", TType.I32, (short)1); + assertNotEquals(uut1, uut2); + assertNotEquals(uut1.hashCode(), uut2.hashCode()); + + uut1 = new TField("foo", TType.VOID, (short)5); + uut2 = new TField("bar", TType.I32, (short)5); + assertEquals(uut1, uut2); // name field is ignored + assertEquals(uut1.hashCode(), uut2.hashCode()); + } + +} diff --git a/lib/java/test/org/apache/thrift/protocol/TestTJSONProtocol.java b/lib/java/test/org/apache/thrift/protocol/TestTJSONProtocol.java index 13207495f7f..c2ca1fa7aaf 100644 --- a/lib/java/test/org/apache/thrift/protocol/TestTJSONProtocol.java +++ b/lib/java/test/org/apache/thrift/protocol/TestTJSONProtocol.java @@ -18,7 +18,7 @@ */ package org.apache.thrift.protocol; -import java.io.IOException; +import java.nio.charset.StandardCharsets; import org.apache.thrift.TException; import org.apache.thrift.protocol.TJSONProtocol; @@ -35,13 +35,13 @@ protected boolean canBeUsedNaked() { return false; } - public void testEscapedUnicode() throws TException, IOException { + public void testEscapedUnicode() throws TException { String jsonString = "\"hello unicode \\u0e01\\ud834\\udd1e world\""; String expectedString = "hello unicode \u0e01\ud834\udd1e world"; TMemoryBuffer buffer = new TMemoryBuffer(1000); TJSONProtocol protocol = new TJSONProtocol(buffer); - buffer.write(jsonString.getBytes("UTF-8")); + buffer.write(jsonString.getBytes(StandardCharsets.UTF_8)); assertEquals(expectedString, protocol.readString()); } diff --git a/lib/java/test/org/apache/thrift/protocol/TestTSimpleJSONProtocol.java b/lib/java/test/org/apache/thrift/protocol/TestTSimpleJSONProtocol.java index b8c4657489a..171a487ead0 100644 --- a/lib/java/test/org/apache/thrift/protocol/TestTSimpleJSONProtocol.java +++ b/lib/java/test/org/apache/thrift/protocol/TestTSimpleJSONProtocol.java @@ -18,7 +18,7 @@ */ package org.apache.thrift.protocol; -import java.io.UnsupportedEncodingException; +import java.nio.charset.StandardCharsets; import junit.framework.TestCase; @@ -40,11 +40,7 @@ protected void setUp() throws Exception { } private String bufToString() { - try { - return buf.toString("UTF-8"); - } catch (UnsupportedEncodingException e) { - throw new RuntimeException(e); - } + return buf.toString(StandardCharsets.UTF_8); } public void testHolyMoley() throws TException { @@ -84,7 +80,7 @@ public void testSanePartsOfCompactProtoTestStruct() throws TException { struct.unsetDouble_byte_map(); struct.unsetString_byte_map(); struct.write(proto); - assertEquals("{\"a_byte\":127,\"a_i16\":32000,\"a_i32\":1000000000,\"a_i64\":1099511627775,\"a_double\":5.6789,\"a_string\":\"my string\",\"a_binary\":\"\\u0000\\u0001\\u0002\\u0003\\u0004\\u0005\\u0006\\u0007\\b\",\"true_field\":1,\"false_field\":0,\"empty_struct_field\":{},\"byte_list\":[-127,-1,0,1,127],\"i16_list\":[-1,0,1,32767],\"i32_list\":[-1,0,255,65535,16777215,2147483647],\"i64_list\":[-1,0,255,65535,16777215,4294967295,1099511627775,281474976710655,72057594037927935,9223372036854775807],\"double_list\":[0.1,0.2,0.3],\"string_list\":[\"first\",\"second\",\"third\"],\"boolean_list\":[1,1,1,0,0,0],\"struct_list\":[{},{}],\"i32_set\":[1,2,3],\"boolean_set\":[0,1],\"struct_set\":[{}],\"byte_byte_map\":{\"1\":2},\"boolean_byte_map\":{\"0\":0,\"1\":1},\"byte_i16_map\":{\"1\":1,\"2\":-1,\"3\":32767},\"byte_i32_map\":{\"1\":1,\"2\":-1,\"3\":2147483647},\"byte_i64_map\":{\"1\":1,\"2\":-1,\"3\":9223372036854775807},\"byte_double_map\":{\"1\":0.1,\"2\":-0.1,\"3\":1000000.1},\"byte_string_map\":{\"1\":\"\",\"2\":\"blah\",\"3\":\"loooooooooooooong string\"},\"byte_boolean_map\":{\"1\":1,\"2\":0},\"byte_map_map\":{\"0\":{},\"1\":{\"1\":1},\"2\":{\"1\":1,\"2\":2}},\"byte_set_map\":{\"0\":[],\"1\":[1],\"2\":[1,2]},\"byte_list_map\":{\"0\":[],\"1\":[1],\"2\":[1,2]}}", bufToString()); + assertEquals("{\"a_byte\":127,\"a_i16\":32000,\"a_i32\":1000000000,\"a_i64\":1099511627775,\"a_double\":5.6789,\"a_string\":\"my string\",\"a_binary\":\"\\u0000\\u0001\\u0002\\u0003\\u0004\\u0005\\u0006\\u0007\\b\",\"true_field\":1,\"false_field\":0,\"empty_struct_field\":{},\"byte_list\":[-127,-1,0,1,127],\"i16_list\":[-1,0,1,32767],\"i32_list\":[-1,0,255,65535,16777215,2147483647],\"i64_list\":[-1,0,255,65535,16777215,4294967295,1099511627775,281474976710655,72057594037927935,9223372036854775807],\"double_list\":[0.1,0.2,0.3],\"string_list\":[\"first\",\"second\",\"third\"],\"boolean_list\":[1,1,1,0,0,0],\"struct_list\":[{},{}],\"i32_set\":[1,2,3],\"boolean_set\":[0,1],\"struct_set\":[{}],\"byte_byte_map\":{\"1\":2},\"boolean_byte_map\":{\"0\":0,\"1\":1},\"byte_i16_map\":{\"1\":1,\"2\":-1,\"3\":32767},\"byte_i32_map\":{\"1\":1,\"2\":-1,\"3\":2147483647},\"byte_i64_map\":{\"1\":1,\"2\":-1,\"3\":9223372036854775807},\"byte_double_map\":{\"1\":0.1,\"2\":-0.1,\"3\":1000000.1},\"byte_string_map\":{\"1\":\"\",\"2\":\"blah\",\"3\":\"loooooooooooooong string\"},\"byte_boolean_map\":{\"1\":1,\"2\":0},\"byte_map_map\":{\"0\":{},\"1\":{\"1\":1},\"2\":{\"1\":1,\"2\":2}},\"byte_set_map\":{\"0\":[],\"1\":[1],\"2\":[1,2]},\"byte_list_map\":{\"0\":[],\"1\":[1],\"2\":[1,2]},\"field500\":500,\"field5000\":5000,\"field20000\":20000}", bufToString()); } public void testThrowsOnCollectionKeys() throws TException { diff --git a/lib/java/test/org/apache/thrift/scheme/TestStandardScheme.java b/lib/java/test/org/apache/thrift/scheme/TestStandardScheme.java index 33f229e88cb..43e40c22306 100644 --- a/lib/java/test/org/apache/thrift/scheme/TestStandardScheme.java +++ b/lib/java/test/org/apache/thrift/scheme/TestStandardScheme.java @@ -12,6 +12,7 @@ import org.apache.thrift.transport.TMemoryBuffer; import org.apache.thrift.transport.TTransport; +import org.apache.thrift.transport.TTransportException; import thrift.test.HolyMoley; import thrift.test.Nesting; import thrift.test.OneOfEach; @@ -20,6 +21,9 @@ public class TestStandardScheme extends TestCase { TSerializer serializer = new TSerializer(); TDeserializer deserializer = new TDeserializer(); + public TestStandardScheme() throws TTransportException { + } + /** * This tests whether the Standard Scheme properly reads structs serialized * using an older version of thrift. diff --git a/lib/java/test/org/apache/thrift/server/ServerTestBase.java b/lib/java/test/org/apache/thrift/server/ServerTestBase.java index 1dee22d1717..e2bf96a1aef 100644 --- a/lib/java/test/org/apache/thrift/server/ServerTestBase.java +++ b/lib/java/test/org/apache/thrift/server/ServerTestBase.java @@ -37,11 +37,12 @@ import org.apache.thrift.protocol.TCompactProtocol; import org.apache.thrift.protocol.TProtocol; import org.apache.thrift.protocol.TProtocolFactory; -import org.apache.thrift.transport.TFramedTransport; +import org.apache.thrift.transport.TTransportException; +import org.apache.thrift.transport.layered.TFramedTransport; import org.apache.thrift.transport.TSocket; import org.apache.thrift.transport.TTransport; import org.apache.thrift.transport.TTransportFactory; -import org.apache.thrift.transport.TFramedTransport.Factory; +import org.apache.thrift.transport.layered.TFramedTransport.Factory; import thrift.test.Insanity; import thrift.test.Numberz; @@ -94,9 +95,13 @@ public double testDouble(double thing) { public ByteBuffer testBinary(ByteBuffer thing) { StringBuilder sb = new StringBuilder(thing.remaining() * 3); thing.mark(); - while (thing.remaining() > 0) { + int limit = 0; // limit output to keep the log size sane + while ((thing.remaining() > 0) && (++limit < 1024)) { sb.append(String.format("%02X ", thing.get())); } + if(thing.remaining() > 0) { + sb.append("..."); // indicate we have more date + } System.out.print("testBinary(" + sb.toString() + ")\n"); thing.reset(); return thing; @@ -199,7 +204,7 @@ public Map> testInsanity(Insanity argument) { System.out.print("testInsanity()\n"); HashMap first_map = new HashMap(); - HashMap second_map = new HashMap();; + HashMap second_map = new HashMap(); first_map.put(Numberz.TWO, argument); first_map.put(Numberz.THREE, argument); @@ -218,7 +223,7 @@ public Map> testInsanity(Insanity argument) { public Xtruct testMulti(byte arg0, int arg1, long arg2, Map arg3, Numberz arg4, long arg5) { System.out.print("testMulti()\n"); - Xtruct hello = new Xtruct();; + Xtruct hello = new Xtruct(); hello.string_thing = "Hello2"; hello.byte_thing = arg0; hello.i32_thing = arg1; @@ -264,7 +269,7 @@ public Xtruct testMultiException(String arg0, String arg1) throws Xception, Xcep } public void testOneway(int sleepFor) { - System.out.println("testOneway(" + Integer.toString(sleepFor) + + System.out.println("testOneway(" + sleepFor + ") => sleeping..."); try { Thread.sleep(sleepFor * SLEEP_DELAY); @@ -529,7 +534,7 @@ public CallCountingTransportFactory(Factory factory) { } @Override - public TTransport getTransport(TTransport trans) { + public TTransport getTransport(TTransport trans) throws TTransportException { count++; return factory.getTransport(trans); } diff --git a/lib/java/test/org/apache/thrift/server/TestNonblockingServer.java b/lib/java/test/org/apache/thrift/server/TestNonblockingServer.java index 3df3bd827ae..2c779080eb4 100644 --- a/lib/java/test/org/apache/thrift/server/TestNonblockingServer.java +++ b/lib/java/test/org/apache/thrift/server/TestNonblockingServer.java @@ -23,7 +23,7 @@ import org.apache.thrift.protocol.TProtocol; import org.apache.thrift.protocol.TProtocolFactory; import org.apache.thrift.server.TNonblockingServer.Args; -import org.apache.thrift.transport.TFramedTransport; +import org.apache.thrift.transport.layered.TFramedTransport; import org.apache.thrift.transport.TNonblockingServerSocket; import org.apache.thrift.transport.TSocket; import org.apache.thrift.transport.TTransport; diff --git a/lib/java/test/org/apache/thrift/server/TestSaslNonblockingServer.java b/lib/java/test/org/apache/thrift/server/TestSaslNonblockingServer.java new file mode 100644 index 00000000000..d0a674674c8 --- /dev/null +++ b/lib/java/test/org/apache/thrift/server/TestSaslNonblockingServer.java @@ -0,0 +1,96 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.thrift.server; + +import org.apache.thrift.TProcessor; +import org.apache.thrift.protocol.TBinaryProtocol; +import org.apache.thrift.protocol.TProtocolFactory; +import org.apache.thrift.transport.TNonblockingServerSocket; +import org.apache.thrift.transport.TNonblockingServerTransport; +import org.apache.thrift.transport.TSaslClientTransport; +import org.apache.thrift.transport.TSocket; +import org.apache.thrift.transport.TTransportException; +import org.apache.thrift.transport.TTransportFactory; +import org.apache.thrift.transport.TestTSaslTransports; +import org.apache.thrift.transport.TestTSaslTransports.TestSaslCallbackHandler; +import org.apache.thrift.transport.sasl.TSaslNegotiationException; +import thrift.test.ThriftTest; + +import static org.apache.thrift.transport.sasl.TSaslNegotiationException.ErrorType.AUTHENTICATION_FAILURE; + +public class TestSaslNonblockingServer extends TestTSaslTransports.TestTSaslTransportsWithServer { + + private TSaslNonblockingServer server; + + @Override + public void startServer(TProcessor processor, TProtocolFactory protoFactory, TTransportFactory factory) + throws Exception { + TNonblockingServerTransport serverSocket = new TNonblockingServerSocket( + new TNonblockingServerSocket.NonblockingAbstractServerSocketArgs().port(PORT)); + TSaslNonblockingServer.Args args = new TSaslNonblockingServer.Args(serverSocket) + .processor(processor) + .transportFactory(factory) + .protocolFactory(protoFactory) + .addSaslMechanism(TestTSaslTransports.WRAPPED_MECHANISM, TestTSaslTransports.SERVICE, + TestTSaslTransports.HOST, TestTSaslTransports.WRAPPED_PROPS, + new TestSaslCallbackHandler(TestTSaslTransports.PASSWORD)); + server = new TSaslNonblockingServer(args); + server.serve(); + } + + @Override + public void stopServer() throws Exception { + server.shutdown(); + } + + @Override + public void testIt() throws Exception { + super.testIt(); + } + + public void testBadPassword() throws Exception { + TProtocolFactory protocolFactory = new TBinaryProtocol.Factory(); + TProcessor processor = new ThriftTest.Processor<>(new TestHandler()); + startServer(processor, protocolFactory); + + TSocket socket = new TSocket(HOST, PORT); + socket.setTimeout(SOCKET_TIMEOUT); + TSaslClientTransport client = new TSaslClientTransport(TestTSaslTransports.WRAPPED_MECHANISM, + TestTSaslTransports.PRINCIPAL, TestTSaslTransports.SERVICE, TestTSaslTransports.HOST, + TestTSaslTransports.WRAPPED_PROPS, new TestSaslCallbackHandler("bad_password"), socket); + try { + client.open(); + fail("Client should fail with sasl negotiation."); + } catch (TTransportException error) { + TSaslNegotiationException serverSideError = new TSaslNegotiationException(AUTHENTICATION_FAILURE, + "Authentication failed with " + TestTSaslTransports.WRAPPED_MECHANISM); + assertTrue("Server should return error message \"" + serverSideError.getSummary() + "\"", + error.getMessage().contains(serverSideError.getSummary())); + } finally { + stopServer(); + client.close(); + } + } + + @Override + public void testTransportFactory() { + // This test is irrelevant here, so skipped. + } +} diff --git a/lib/java/test/org/apache/thrift/server/TestThreadPoolServer.java b/lib/java/test/org/apache/thrift/server/TestThreadPoolServer.java new file mode 100644 index 00000000000..4c84dc18d5d --- /dev/null +++ b/lib/java/test/org/apache/thrift/server/TestThreadPoolServer.java @@ -0,0 +1,68 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.thrift.server; + +import org.apache.thrift.protocol.TBinaryProtocol; +import org.apache.thrift.transport.TServerSocket; +import org.apache.thrift.transport.TServerTransport; +import org.apache.thrift.transport.TSocket; +import org.junit.Assert; +import org.junit.Test; +import thrift.test.ThriftTest; + +import java.util.concurrent.ThreadPoolExecutor; + +public class TestThreadPoolServer { + + /** + * Test server is shut down properly even with some open clients. + */ + @Test + public void testStopServerWithOpenClient() throws Exception { + TServerSocket serverSocket = new TServerSocket(0, 3000); + TThreadPoolServer server = buildServer(serverSocket); + Thread serverThread = new Thread(() -> server.serve()); + serverThread.start(); + try (TSocket client = new TSocket("localhost", serverSocket.getServerSocket().getLocalPort())) { + client.open(); + Thread.sleep(1000); + // There is a thread listening to the client + Assert.assertEquals(1, ((ThreadPoolExecutor) server.getExecutorService()).getActiveCount()); + + // Trigger the server to stop, but it does not wait + server.stop(); + Assert.assertTrue(server.waitForShutdown()); + + // After server is stopped, the executor thread pool should be shut down + Assert.assertTrue("Server thread pool should be terminated", server.getExecutorService().isTerminated()); + + // TODO: The socket is actually closed (timeout) but the client code + // ignores the timeout Exception and maintains the socket open state + Assert.assertTrue("Client should be closed after server shutdown", client.isOpen()); + } + } + + private TThreadPoolServer buildServer(TServerTransport serverSocket) { + TThreadPoolServer.Args args = new TThreadPoolServer.Args(serverSocket) + .protocolFactory(new TBinaryProtocol.Factory()) + .processor(new ThriftTest.Processor<>(new ServerTestBase.TestHandler())); + return new TThreadPoolServer(args); + } +} diff --git a/lib/java/test/org/apache/thrift/test/SerializationBenchmark.java b/lib/java/test/org/apache/thrift/test/SerializationBenchmark.java index 2b0db313243..de22556f83a 100644 --- a/lib/java/test/org/apache/thrift/test/SerializationBenchmark.java +++ b/lib/java/test/org/apache/thrift/test/SerializationBenchmark.java @@ -22,6 +22,7 @@ import org.apache.thrift.Fixtures; import org.apache.thrift.TBase; +import org.apache.thrift.TConfiguration; import org.apache.thrift.protocol.TBinaryProtocol; import org.apache.thrift.protocol.TProtocol; import org.apache.thrift.protocol.TProtocolFactory; @@ -34,34 +35,37 @@ public class SerializationBenchmark { private final static int HOW_MANY = 10000000; - + public static void main(String[] args) throws Exception { TProtocolFactory factory = new TBinaryProtocol.Factory(); testSerialization(factory, Fixtures.oneOfEach); testDeserialization(factory, Fixtures.oneOfEach, OneOfEach.class); } - + public static void testSerialization(TProtocolFactory factory, TBase object) throws Exception { TTransport trans = new TTransport() { public void write(byte[] bin, int x, int y) throws TTransportException {} + public TConfiguration getConfiguration() {return new TConfiguration(); } + public void updateKnownMessageSize(long size) throws TTransportException {} + public void checkReadBytesAvailable(long numBytes) throws TTransportException {} public int read(byte[] bin, int x, int y) throws TTransportException {return 0;} public void close() {} public void open() {} public boolean isOpen() {return true;} }; - + TProtocol proto = factory.getProtocol(trans); - + long startTime = System.currentTimeMillis(); for (int i = 0; i < HOW_MANY; i++) { object.write(proto); } long endTime = System.currentTimeMillis(); - + System.out.println("Serialization test time: " + (endTime - startTime) + " ms"); } - + public static void testDeserialization(TProtocolFactory factory, T object, Class klass) throws Exception { TMemoryBuffer buf = new TMemoryBuffer(0); object.write(factory.getProtocol(buf)); @@ -71,10 +75,10 @@ public static void testDeserialization(TProtocolFactory factor long startTime = System.currentTimeMillis(); for (int i = 0; i < HOW_MANY; i++) { T o2 = klass.newInstance(); - o2.read(factory.getProtocol(new TMemoryInputTransport(serialized))); + o2.read(factory.getProtocol(new TMemoryInputTransport(new TConfiguration(), serialized))); } long endTime = System.currentTimeMillis(); System.out.println("Deserialization test time: " + (endTime - startTime) + " ms"); } -} \ No newline at end of file +} diff --git a/lib/java/test/org/apache/thrift/test/TestClient.java b/lib/java/test/org/apache/thrift/test/TestClient.java index feaa97220ad..2861a1d6b19 100644 --- a/lib/java/test/org/apache/thrift/test/TestClient.java +++ b/lib/java/test/org/apache/thrift/test/TestClient.java @@ -27,6 +27,7 @@ import java.util.Map; import java.util.Set; +import org.apache.http.impl.client.HttpClients; import org.apache.thrift.TApplicationException; import org.apache.thrift.TException; import org.apache.thrift.TSerializer; @@ -36,13 +37,14 @@ import org.apache.thrift.protocol.TMultiplexedProtocol; import org.apache.thrift.protocol.TProtocol; import org.apache.thrift.protocol.TSimpleJSONProtocol; -import org.apache.thrift.transport.TFastFramedTransport; -import org.apache.thrift.transport.TFramedTransport; +import org.apache.thrift.transport.layered.TFastFramedTransport; +import org.apache.thrift.transport.layered.TFramedTransport; import org.apache.thrift.transport.THttpClient; import org.apache.thrift.transport.TSSLTransportFactory; import org.apache.thrift.transport.TSocket; import org.apache.thrift.transport.TTransport; import org.apache.thrift.transport.TTransportException; +import org.apache.thrift.transport.TZlibTransport; // Generated code import thrift.test.Insanity; @@ -76,6 +78,8 @@ public static void main(String [] args) { String protocol_type = "binary"; String transport_type = "buffered"; boolean ssl = false; + boolean zlib = false; + boolean http_client = false; int socketTimeout = 1000; @@ -99,14 +103,19 @@ public static void main(String [] args) { transport_type.trim(); } else if (args[i].equals("--ssl")) { ssl = true; + } else if (args[i].equals("--zlib")) { + zlib = true; + } else if (args[i].equals("--client")) { + http_client = true; } else if (args[i].equals("--help")) { System.out.println("Allowed options:"); System.out.println(" --help\t\t\tProduce help message"); System.out.println(" --host=arg (=" + host + ")\tHost to connect"); System.out.println(" --port=arg (=" + port + ")\tPort number to connect"); - System.out.println(" --transport=arg (=" + transport_type + ")\n\t\t\t\tTransport: buffered, framed, fastframed, http"); + System.out.println(" --transport=arg (=" + transport_type + ")\n\t\t\t\tTransport: buffered, framed, fastframed, http, zlib"); System.out.println(" --protocol=arg (=" + protocol_type + ")\tProtocol: binary, compact, json, multi, multic, multij"); System.out.println(" --ssl\t\t\tEncrypted Transport using SSL"); + System.out.println(" --zlib\t\t\tCompressed Transport using Zlib"); System.out.println(" --testloops[--n]=arg (=" + numTests + ")\tNumber of Tests"); System.exit(0); } @@ -130,6 +139,7 @@ public static void main(String [] args) { } else if (transport_type.equals("framed")) { } else if (transport_type.equals("fastframed")) { } else if (transport_type.equals("http")) { + } else if (transport_type.equals("zlib")) { } else { throw new Exception("Unknown transport type! " + transport_type); } @@ -145,8 +155,13 @@ public static void main(String [] args) { try { if (transport_type.equals("http")) { - String url = "http://" + host + ":" + port + "/service"; - transport = new THttpClient(url); + String url = "http://" + host + ":" + port + "/test/service"; + if (http_client == true) { + + transport = new THttpClient(url, HttpClients.createDefault()); + } else { + transport = new THttpClient(url); + } } else { TSocket socket = null; if (ssl == true) { @@ -156,11 +171,18 @@ public static void main(String [] args) { } socket.setTimeout(socketTimeout); transport = socket; - if (transport_type.equals("buffered")) { - } else if (transport_type.equals("framed")) { - transport = new TFramedTransport(transport); - } else if (transport_type.equals("fastframed")) { - transport = new TFastFramedTransport(transport); + if (transport_type.equals("zlib")) { + transport = new TZlibTransport(transport); + } else { + if (transport_type.equals("buffered")) { + } else if (transport_type.equals("framed")) { + transport = new TFramedTransport(transport); + } else if (transport_type.equals("fastframed")) { + transport = new TFastFramedTransport(transport); + } + if (zlib) { + transport = new TZlibTransport(transport); + } } } } catch (Exception x) { @@ -752,14 +774,19 @@ public static void main(String [] args) { testClient.testOneway(3); long onewayElapsedMillis = (System.nanoTime() - startOneway) / 1000000; if (onewayElapsedMillis > 200) { - System.out.println("Oneway test failed: took " + - Long.toString(onewayElapsedMillis) + + System.out.println("Oneway test took too long to execute failed: took " + + onewayElapsedMillis + "ms"); - System.out.printf("*** FAILURE ***\n"); + System.out.println("oneway calls are 'fire and forget' and therefore should not cause blocking."); + System.out.println("Some transports (HTTP) have a required response, and typically this failure"); + System.out.println("means the transport response was delayed until after the execution"); + System.out.println("of the RPC. The server should post the transport response immediately and"); + System.out.println("before executing the RPC."); + System.out.println("*** FAILURE ***"); returnCode |= ERR_BASETYPES; } else { - System.out.println("Success - took " + - Long.toString(onewayElapsedMillis) + + System.out.println("Success - fire and forget only took " + + onewayElapsedMillis + "ms"); } diff --git a/lib/java/test/org/apache/thrift/test/TestServer.java b/lib/java/test/org/apache/thrift/test/TestServer.java index 1f3e555d977..386f2b60bf4 100644 --- a/lib/java/test/org/apache/thrift/test/TestServer.java +++ b/lib/java/test/org/apache/thrift/test/TestServer.java @@ -19,29 +19,22 @@ package org.apache.thrift.test; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Set; - import org.apache.thrift.protocol.TBinaryProtocol; import org.apache.thrift.protocol.TCompactProtocol; import org.apache.thrift.protocol.TJSONProtocol; import org.apache.thrift.protocol.TProtocol; import org.apache.thrift.protocol.TProtocolFactory; -import org.apache.thrift.protocol.TMultiplexedProtocol; import org.apache.thrift.server.ServerContext; import org.apache.thrift.server.TServer; -import org.apache.thrift.server.TServer.Args; import org.apache.thrift.server.TSimpleServer; import org.apache.thrift.server.TThreadPoolServer; import org.apache.thrift.server.ServerTestBase.TestHandler; import org.apache.thrift.server.TServerEventHandler; import org.apache.thrift.server.TThreadedSelectorServer; import org.apache.thrift.server.TNonblockingServer; -import org.apache.thrift.transport.TFramedTransport; -import org.apache.thrift.transport.TFastFramedTransport; +import org.apache.thrift.transport.layered.TFramedTransport; +import org.apache.thrift.transport.layered.TFastFramedTransport; +import org.apache.thrift.transport.TZlibTransport; import org.apache.thrift.transport.TServerSocket; import org.apache.thrift.transport.TSSLTransportFactory; import org.apache.thrift.transport.TTransport; @@ -49,14 +42,8 @@ import org.apache.thrift.transport.TNonblockingServerSocket; import org.apache.thrift.TMultiplexedProcessor; -import thrift.test.Insanity; -import thrift.test.Numberz; import thrift.test.SecondService; import thrift.test.ThriftTest; -import thrift.test.Xception; -import thrift.test.Xception2; -import thrift.test.Xtruct; -import thrift.test.Xtruct2; public class TestServer { @@ -94,6 +81,24 @@ public void setConnectionId(int connectionId) { this.connectionId = connectionId; } + @Override + public T unwrap(Class iface) { + try { + if (isWrapperFor(iface)) { + return iface.cast(this); + } else { + throw new RuntimeException("The context is not a wrapper for " + iface.getName()); + } + } catch (Exception e) { + throw new RuntimeException("The context is not a wrapper and does not implement the interface"); + } + } + + @Override + public boolean isWrapperFor(Class iface) { + return iface.isInstance(this); + } + } static class TestServerEventHandler implements TServerEventHandler { @@ -112,12 +117,12 @@ public ServerContext createContext(TProtocol input, TProtocol output) { } public void deleteContext(ServerContext serverContext, TProtocol input, TProtocol output) { - TestServerContext ctx = (TestServerContext)serverContext; + TestServerContext ctx = serverContext.unwrap(TestServerContext.class); System.out.println("TServerEventHandler.deleteContext - connection #"+ctx.getConnectionId()+" terminated"); } public void processContext(ServerContext serverContext, TTransport inputTransport, TTransport outputTransport) { - TestServerContext ctx = (TestServerContext)serverContext; + TestServerContext ctx = serverContext.unwrap(TestServerContext.class); System.out.println("TServerEventHandler.processContext - connection #"+ctx.getConnectionId()+" is ready to process next request"); } @@ -127,6 +132,7 @@ public static void main(String [] args) { try { int port = 9090; boolean ssl = false; + boolean zlib = false; String transport_type = "buffered"; String protocol_type = "binary"; String server_type = "thread-pool"; @@ -150,6 +156,8 @@ public static void main(String [] args) { transport_type.trim(); } else if (args[i].equals("--ssl")) { ssl = true; + } else if (args[i].equals("--zlib")) { + zlib = true; } else if (args[i].startsWith("--string-limit")) { string_limit = Integer.valueOf(args[i].split("=")[1]); } else if (args[i].startsWith("--container-limit")) { @@ -158,9 +166,10 @@ public static void main(String [] args) { System.out.println("Allowed options:"); System.out.println(" --help\t\t\tProduce help message"); System.out.println(" --port=arg (=" + port + ")\tPort number to connect"); - System.out.println(" --transport=arg (=" + transport_type + ")\n\t\t\t\tTransport: buffered, framed, fastframed"); + System.out.println(" --transport=arg (=" + transport_type + ")\n\t\t\t\tTransport: buffered, framed, fastframed, zlib"); System.out.println(" --protocol=arg (=" + protocol_type + ")\tProtocol: binary, compact, json, multi, multic, multij"); System.out.println(" --ssl\t\t\tEncrypted Transport using SSL"); + System.out.println(" --zlib\t\t\tCompressed Transport using Zlib"); System.out.println(" --server-type=arg (=" + server_type +")\n\t\t\t\tType of server: simple, thread-pool, nonblocking, threaded-selector"); System.out.println(" --string-limit=arg (=" + string_limit + ")\tString read length limit"); System.out.println(" --container-limit=arg (=" + container_limit + ")\tContainer read length limit"); @@ -198,6 +207,7 @@ public static void main(String [] args) { if (transport_type.equals("buffered")) { } else if (transport_type.equals("framed")) { } else if (transport_type.equals("fastframed")) { + } else if (transport_type.equals("zlib")) { } else { throw new Exception("Unknown transport type! " + transport_type); } @@ -229,6 +239,8 @@ public static void main(String [] args) { tTransportFactory = new TFramedTransport.Factory(); } else if (transport_type.equals("fastframed")) { tTransportFactory = new TFastFramedTransport.Factory(); + } else if (transport_type.equals("zlib")) { + tTransportFactory = new TZlibTransport.Factory(); } else { // .equals("buffered") => default value tTransportFactory = new TTransportFactory(); } diff --git a/lib/java/test/org/apache/thrift/test/TestServlet.java b/lib/java/test/org/apache/thrift/test/TestServlet.java new file mode 100644 index 00000000000..e63109da8ad --- /dev/null +++ b/lib/java/test/org/apache/thrift/test/TestServlet.java @@ -0,0 +1,52 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.thrift.test; + +import org.apache.thrift.TProcessor; +import org.apache.thrift.protocol.TCompactProtocol; +import org.apache.thrift.protocol.TProtocolFactory; +import org.apache.thrift.server.ServerTestBase.TestHandler; +import org.apache.thrift.server.TExtensibleServlet; + +import thrift.test.ThriftTest; + +@SuppressWarnings("serial") +public class TestServlet extends TExtensibleServlet { + + @Override + protected TProtocolFactory getInProtocolFactory(){ + TProtocolFactory tProtocolFactory = new TCompactProtocol.Factory(); + return tProtocolFactory; + } + + @Override + protected TProtocolFactory getOutProtocolFactory(){ + TProtocolFactory tProtocolFactory = new TCompactProtocol.Factory(); + return tProtocolFactory; + } + + @SuppressWarnings({"rawtypes", "unchecked"}) + @Override + protected TProcessor getProcessor(){ + TestHandler testHandler = new TestHandler(); + ThriftTest.Processor testProcessor = new ThriftTest.Processor(testHandler); + return testProcessor; + } +} diff --git a/lib/java/test/org/apache/thrift/test/TestTServletServer.java b/lib/java/test/org/apache/thrift/test/TestTServletServer.java new file mode 100644 index 00000000000..93e7944f820 --- /dev/null +++ b/lib/java/test/org/apache/thrift/test/TestTServletServer.java @@ -0,0 +1,52 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.thrift.test; + +import org.apache.catalina.core.StandardContext; +import org.apache.catalina.startup.Tomcat; +import org.apache.catalina.startup.Tomcat.FixContextListener; + + +/** + * run tomcat for test TServlet + */ +public class TestTServletServer { + + static final int port = 9090; + + public static void main(String [] args) throws Exception{ + Tomcat tomcat = new Tomcat(); + tomcat.setPort( port ); + tomcat.setBaseDir(System.getProperty("user.dir")+"\\build"); + tomcat.getHost().setAutoDeploy( false ); + + String contextPath = "/test"; + StandardContext context = new StandardContext(); + context.setPath( contextPath ); + context.addLifecycleListener( new FixContextListener() ); + tomcat.getHost().addChild( context ); + + tomcat.addServlet( contextPath, "testServlet", new TestServlet() ); + context.addServletMappingDecoded( "/service", "testServlet"); + tomcat.start(); + tomcat.getServer().await(); + } + +} diff --git a/lib/java/test/org/apache/thrift/transport/ReadCountingTransport.java b/lib/java/test/org/apache/thrift/transport/ReadCountingTransport.java index 3c749f9fb02..1b2073cb527 100644 --- a/lib/java/test/org/apache/thrift/transport/ReadCountingTransport.java +++ b/lib/java/test/org/apache/thrift/transport/ReadCountingTransport.java @@ -19,6 +19,8 @@ package org.apache.thrift.transport; +import org.apache.thrift.TConfiguration; + public class ReadCountingTransport extends TTransport { public int readCount = 0; private TTransport trans; @@ -58,4 +60,19 @@ public void write(byte[] buf, int off, int len) throws TTransportException { throw new TTransportException(TTransportException.NOT_OPEN, "Transport is closed"); } } + + @Override + public TConfiguration getConfiguration() { + return trans.getConfiguration(); + } + + @Override + public void updateKnownMessageSize(long size) throws TTransportException { + trans.updateKnownMessageSize(size); + } + + @Override + public void checkReadBytesAvailable(long numBytes) throws TTransportException { + trans.checkReadBytesAvailable(numBytes); + } } diff --git a/lib/java/test/org/apache/thrift/transport/TestAutoExpandingBuffer.java b/lib/java/test/org/apache/thrift/transport/TestAutoExpandingBuffer.java index 337dcf8cb04..c35348953eb 100644 --- a/lib/java/test/org/apache/thrift/transport/TestAutoExpandingBuffer.java +++ b/lib/java/test/org/apache/thrift/transport/TestAutoExpandingBuffer.java @@ -23,7 +23,7 @@ public class TestAutoExpandingBuffer extends TestCase { public void testExpands() throws Exception { // has expected initial capacity - AutoExpandingBuffer b = new AutoExpandingBuffer(10, 1.5); + AutoExpandingBuffer b = new AutoExpandingBuffer(10); assertEquals(10, b.array().length); // doesn't shrink diff --git a/lib/java/test/org/apache/thrift/transport/TestAutoExpandingBufferReadTransport.java b/lib/java/test/org/apache/thrift/transport/TestAutoExpandingBufferReadTransport.java index 2e1f9472437..b635e60fa01 100644 --- a/lib/java/test/org/apache/thrift/transport/TestAutoExpandingBufferReadTransport.java +++ b/lib/java/test/org/apache/thrift/transport/TestAutoExpandingBufferReadTransport.java @@ -21,6 +21,7 @@ import java.nio.ByteBuffer; import junit.framework.TestCase; +import org.apache.thrift.TConfiguration; public class TestAutoExpandingBufferReadTransport extends TestCase { private static final byte[] HUNDRED_BYTES = new byte[100]; @@ -32,9 +33,9 @@ public class TestAutoExpandingBufferReadTransport extends TestCase { } public void testIt() throws Exception { - AutoExpandingBufferReadTransport t = new AutoExpandingBufferReadTransport(150, 1.5); + AutoExpandingBufferReadTransport t = new AutoExpandingBufferReadTransport(new TConfiguration(), 150); - TMemoryInputTransport membuf = new TMemoryInputTransport(HUNDRED_BYTES); + TMemoryInputTransport membuf = new TMemoryInputTransport(new TConfiguration(), HUNDRED_BYTES); t.fill(membuf, 100); assertEquals(100, t.getBytesRemainingInBuffer()); diff --git a/lib/java/test/org/apache/thrift/transport/TestAutoExpandingBufferWriteTransport.java b/lib/java/test/org/apache/thrift/transport/TestAutoExpandingBufferWriteTransport.java index d5f239da4d8..c3e021ccd35 100644 --- a/lib/java/test/org/apache/thrift/transport/TestAutoExpandingBufferWriteTransport.java +++ b/lib/java/test/org/apache/thrift/transport/TestAutoExpandingBufferWriteTransport.java @@ -20,25 +20,54 @@ import java.nio.ByteBuffer; -import junit.framework.TestCase; +import org.apache.thrift.TConfiguration; +import org.junit.Test; +import static org.junit.Assert.*; -public class TestAutoExpandingBufferWriteTransport extends TestCase { +public class TestAutoExpandingBufferWriteTransport { + private TConfiguration config = new TConfiguration(); + + @Test public void testIt() throws Exception { - AutoExpandingBufferWriteTransport t = new AutoExpandingBufferWriteTransport(1, 1.5); + AutoExpandingBufferWriteTransport t = new AutoExpandingBufferWriteTransport(config, 1, 0); + assertEquals(0, t.getLength()); assertEquals(1, t.getBuf().array().length); byte[] b1 = new byte[]{1,2,3}; t.write(b1); - assertEquals(3, t.getPos()); + assertEquals(3, t.getLength()); assertTrue(t.getBuf().array().length >= 3); assertEquals(ByteBuffer.wrap(b1), ByteBuffer.wrap(t.getBuf().array(), 0, 3)); t.reset(); + assertEquals(0, t.getLength()); assertTrue(t.getBuf().array().length >= 3); - assertEquals(0, t.getPos()); byte[] b2 = new byte[]{4,5}; t.write(b2); - assertEquals(2, t.getPos()); + assertEquals(2, t.getLength()); assertEquals(ByteBuffer.wrap(b2), ByteBuffer.wrap(t.getBuf().array(), 0, 2)); + + AutoExpandingBufferWriteTransport uut = new AutoExpandingBufferWriteTransport(config, 8, 4); + assertEquals(4, uut.getLength()); + assertEquals(8, uut.getBuf().array().length); + uut.write(b1); + assertEquals(7, uut.getLength()); + assertEquals(8, uut.getBuf().array().length); + assertEquals(ByteBuffer.wrap(b1), ByteBuffer.wrap(uut.getBuf().array(), 4, 3)); + } + + @Test(expected = IllegalArgumentException.class) + public void testBadInitialSize() throws IllegalArgumentException, TTransportException { + new AutoExpandingBufferWriteTransport(config, 0, 0); + } + + @Test(expected = IllegalArgumentException.class) + public void testBadFrontReserveSize() throws IllegalArgumentException, TTransportException { + new AutoExpandingBufferWriteTransport(config, 4, -1); + } + + @Test(expected = IllegalArgumentException.class) + public void testTooSmallFrontReserveSize() throws IllegalArgumentException, TTransportException { + new AutoExpandingBufferWriteTransport(config, 4, 5); } } diff --git a/lib/java/src/org/apache/thrift/transport/TSaslTransportException.java b/lib/java/test/org/apache/thrift/transport/TestNonblockingServerSocket.java similarity index 63% rename from lib/java/src/org/apache/thrift/transport/TSaslTransportException.java rename to lib/java/test/org/apache/thrift/transport/TestNonblockingServerSocket.java index 90189f4a75b..6b28dfd51af 100644 --- a/lib/java/src/org/apache/thrift/transport/TSaslTransportException.java +++ b/lib/java/test/org/apache/thrift/transport/TestNonblockingServerSocket.java @@ -19,25 +19,18 @@ package org.apache.thrift.transport; -/* - * This exception is used to track exceptions in TSaslTransport - * that does not have Sasl signature in their stream. - */ -public class TSaslTransportException extends TTransportException { - - public TSaslTransportException() { - super(); - } +import org.junit.Assert; +import org.junit.Test; - public TSaslTransportException(String message) { - super(message); - } +import java.nio.channels.ServerSocketChannel; - public TSaslTransportException(Throwable cause) { - super(cause); - } +public class TestNonblockingServerSocket { - public TSaslTransportException(String message, Throwable cause) { - super(message, cause); + @Test + public void testSocketChannelBlockingMode() throws TTransportException { + try (TNonblockingServerSocket nonblockingServer = new TNonblockingServerSocket(0)){ + ServerSocketChannel socketChannel = nonblockingServer.getServerSocketChannel(); + Assert.assertFalse("Socket channel should be nonblocking", socketChannel.isBlocking()); + } } } diff --git a/lib/java/test/org/apache/thrift/transport/TestTByteBuffer.java b/lib/java/test/org/apache/thrift/transport/TestTByteBuffer.java index a73075b58cd..bdc0a848af2 100644 --- a/lib/java/test/org/apache/thrift/transport/TestTByteBuffer.java +++ b/lib/java/test/org/apache/thrift/transport/TestTByteBuffer.java @@ -1,7 +1,7 @@ package org.apache.thrift.transport; import junit.framework.TestCase; -import org.apache.commons.codec.Charsets; +import java.nio.charset.StandardCharsets; import org.apache.thrift.TException; import java.nio.ByteBuffer; @@ -9,25 +9,25 @@ public class TestTByteBuffer extends TestCase { public void testReadWrite() throws Exception { final TByteBuffer byteBuffer = new TByteBuffer(ByteBuffer.allocate(16)); - byteBuffer.write("Hello World".getBytes(Charsets.UTF_8)); - assertEquals("Hello World", new String(byteBuffer.flip().toByteArray(), Charsets.UTF_8)); + byteBuffer.write("Hello World".getBytes(StandardCharsets.UTF_8)); + assertEquals("Hello World", new String(byteBuffer.flip().toByteArray(), StandardCharsets.UTF_8)); } public void testReuseReadWrite() throws Exception { final TByteBuffer byteBuffer = new TByteBuffer(ByteBuffer.allocate(16)); - byteBuffer.write("Hello World".getBytes(Charsets.UTF_8)); - assertEquals("Hello World", new String(byteBuffer.flip().toByteArray(), Charsets.UTF_8)); + byteBuffer.write("Hello World".getBytes(StandardCharsets.UTF_8)); + assertEquals("Hello World", new String(byteBuffer.flip().toByteArray(), StandardCharsets.UTF_8)); byteBuffer.clear(); - byteBuffer.write("Goodbye Horses".getBytes(Charsets.UTF_8)); - assertEquals("Goodbye Horses", new String(byteBuffer.flip().toByteArray(), Charsets.UTF_8)); + byteBuffer.write("Goodbye Horses".getBytes(StandardCharsets.UTF_8)); + assertEquals("Goodbye Horses", new String(byteBuffer.flip().toByteArray(), StandardCharsets.UTF_8)); } public void testOverflow() throws Exception { final TByteBuffer byteBuffer = new TByteBuffer(ByteBuffer.allocate(4)); try { - byteBuffer.write("Hello World".getBytes(Charsets.UTF_8)); + byteBuffer.write("Hello World".getBytes(StandardCharsets.UTF_8)); fail("Expected write operation to fail with TTransportException"); } catch (TTransportException e) { assertEquals("Not enough room in output buffer", e.getMessage()); diff --git a/lib/java/test/org/apache/thrift/transport/TestTFastFramedTransport.java b/lib/java/test/org/apache/thrift/transport/TestTFastFramedTransport.java index 06ee206664c..6fa23807bc9 100644 --- a/lib/java/test/org/apache/thrift/transport/TestTFastFramedTransport.java +++ b/lib/java/test/org/apache/thrift/transport/TestTFastFramedTransport.java @@ -18,16 +18,18 @@ */ package org.apache.thrift.transport; +import org.apache.thrift.transport.layered.TFastFramedTransport; + public class TestTFastFramedTransport extends TestTFramedTransport { protected final static int INITIAL_CAPACITY = 50; @Override - protected TTransport getTransport(TTransport underlying) { + protected TTransport getTransport(TTransport underlying) throws TTransportException { return new TFastFramedTransport(underlying, INITIAL_CAPACITY, 10 * 1024 * 1024); } @Override - protected TTransport getTransport(TTransport underlying, int maxLength) { + protected TTransport getTransport(TTransport underlying, int maxLength) throws TTransportException { return new TFastFramedTransport(underlying, INITIAL_CAPACITY, maxLength); } } diff --git a/lib/java/test/org/apache/thrift/transport/TestTFramedTransport.java b/lib/java/test/org/apache/thrift/transport/TestTFramedTransport.java index 7e889d68d56..fc3dd5b6ba8 100644 --- a/lib/java/test/org/apache/thrift/transport/TestTFramedTransport.java +++ b/lib/java/test/org/apache/thrift/transport/TestTFramedTransport.java @@ -27,14 +27,16 @@ import java.util.Arrays; import junit.framework.TestCase; +import org.apache.thrift.transport.layered.TFastFramedTransport; +import org.apache.thrift.transport.layered.TFramedTransport; public class TestTFramedTransport extends TestCase { - protected TTransport getTransport(TTransport underlying) { + protected TTransport getTransport(TTransport underlying) throws TTransportException { return new TFramedTransport(underlying); } - protected TTransport getTransport(TTransport underlying, int maxLength) { + protected TTransport getTransport(TTransport underlying, int maxLength) throws TTransportException { return new TFramedTransport(underlying, maxLength); } @@ -73,6 +75,7 @@ public void testRead() throws IOException, TTransportException { assertEquals(30, trans.read(new byte[30], 0, 30)); assertEquals(2, countTrans.readCount); + // Known message size exceeded readBuf = new byte[220]; assertEquals(220, trans.read(readBuf, 0, 220)); assertTrue(Arrays.equals(readBuf, byteSequence(0, 219))); @@ -125,11 +128,11 @@ public void testWrite() throws TTransportException, IOException { assertEquals(0, countingTrans.writeCount); trans.flush(); - assertEquals(2, countingTrans.writeCount); + assertEquals(1, countingTrans.writeCount); trans.write(byteSequence(0, 245)); trans.flush(); - assertEquals(4, countingTrans.writeCount); + assertEquals(2, countingTrans.writeCount); DataInputStream din = new DataInputStream(new ByteArrayInputStream(baos.toByteArray())); assertEquals(256, din.readInt()); @@ -149,8 +152,8 @@ public void testDirectRead() throws IOException, TTransportException { DataOutputStream dos = new DataOutputStream(baos); dos.writeInt(50); dos.write(byteSequence(0, 49)); - dos.writeInt(75); - dos.write(byteSequence(125, 200)); + dos.writeInt(50); + dos.write(byteSequence(125, 175)); TMemoryBuffer membuf = new TMemoryBuffer(0); membuf.write(baos.toByteArray()); @@ -177,10 +180,11 @@ public void testDirectRead() throws IOException, TTransportException { assertEquals(0, trans.getBytesRemainingInBuffer()); assertEquals(50, trans.getBufferPosition()); + // Known message size exceeded trans.read(readBuf, 0, 10); assertEquals(4, countTrans.readCount); assertTrue(Arrays.equals(readBuf, byteSequence(125,134))); - assertEquals(65, trans.getBytesRemainingInBuffer()); + assertEquals(40, trans.getBytesRemainingInBuffer()); assertEquals(10, trans.getBufferPosition()); } diff --git a/lib/java/test/org/apache/thrift/transport/TestTIOStreamTransport.java b/lib/java/test/org/apache/thrift/transport/TestTIOStreamTransport.java new file mode 100644 index 00000000000..5965446f897 --- /dev/null +++ b/lib/java/test/org/apache/thrift/transport/TestTIOStreamTransport.java @@ -0,0 +1,62 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.thrift.transport; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.InputStream; +import java.io.OutputStream; + +import junit.framework.TestCase; + +public class TestTIOStreamTransport extends TestCase { + + // THRIFT-5022 + public void testOpenClose_2streams() throws TTransportException { + byte[] dummy = {20}; // So the input stream isn't EOF immediately. + InputStream input = new ByteArrayInputStream(dummy); + OutputStream output = new ByteArrayOutputStream(); + TTransport transport = new TIOStreamTransport(input, output); + runOpenClose(transport); + } + + // THRIFT-5022 + public void testOpenClose_1input() throws TTransportException { + byte[] dummy = {20}; + InputStream input = new ByteArrayInputStream(dummy); + TTransport transport = new TIOStreamTransport(input); + runOpenClose(transport); + } + + // THRIFT-5022 + public void testIOpenClose_1output() throws TTransportException { + OutputStream output = new ByteArrayOutputStream(); + TTransport transport = new TIOStreamTransport(output); + runOpenClose(transport); + } + + private void runOpenClose(TTransport transport) throws TTransportException { + transport.open(); + boolean b1 = transport.isOpen(); + assertTrue(b1); + transport.close(); + boolean b2 = transport.isOpen(); + assertFalse(b2); + } +} diff --git a/lib/java/test/org/apache/thrift/transport/TestTMemoryInputTransport.java b/lib/java/test/org/apache/thrift/transport/TestTMemoryInputTransport.java index 273145bd237..bd9443683f9 100644 --- a/lib/java/test/org/apache/thrift/transport/TestTMemoryInputTransport.java +++ b/lib/java/test/org/apache/thrift/transport/TestTMemoryInputTransport.java @@ -61,7 +61,7 @@ public void testReused() throws Exception { assertEquals(3, trans.getBytesRemainingInBuffer()); } - public void testWithOffsetAndLength() throws Exception { + public void testWithOffsetAndLength() throws TTransportException { byte[] input_buf = new byte[]{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; TMemoryInputTransport trans = new TMemoryInputTransport(input_buf, 1, 3); assertEquals(1, trans.getBufferPosition()); diff --git a/lib/java/test/org/apache/thrift/transport/TestTMemoryTransport.java b/lib/java/test/org/apache/thrift/transport/TestTMemoryTransport.java new file mode 100644 index 00000000000..2e20ffea7b1 --- /dev/null +++ b/lib/java/test/org/apache/thrift/transport/TestTMemoryTransport.java @@ -0,0 +1,65 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.thrift.transport; + +import org.apache.thrift.TByteArrayOutputStream; +import org.junit.Assert; +import org.junit.Test; + +import java.nio.ByteBuffer; +import java.util.Random; + +public class TestTMemoryTransport { + + @Test + public void testReadBatches() throws TTransportException { + byte[] inputBytes = {0x10, 0x7A, (byte) 0xBF, (byte) 0xFE, 0x53, (byte) 0x82, (byte) 0xFF}; + TMemoryTransport transport = new TMemoryTransport(inputBytes); + byte[] read = new byte[inputBytes.length]; + int firstBatch = new Random().nextInt(inputBytes.length); + int secondBatch = inputBytes.length - firstBatch; + transport.read(read, 0, firstBatch); + transport.read(read, firstBatch, secondBatch); + boolean equal = true; + for (int i = 0; i < inputBytes.length; i++) { + equal = equal && inputBytes[i] == read[i]; + } + Assert.assertEquals(ByteBuffer.wrap(inputBytes), ByteBuffer.wrap(read)); + } + + @Test (expected = TTransportException.class) + public void testReadMoreThanRemaining() throws TTransportException { + TMemoryTransport transport = new TMemoryTransport(new byte[] {0x00, 0x32}); + byte[] read = new byte[3]; + transport.read(read, 0, 3); + } + + @Test + public void testWrite() throws TTransportException { + TMemoryTransport transport = new TMemoryTransport(new byte[0]); + byte[] output1 = {0x72, 0x56, 0x29, (byte) 0xAF, (byte) 0x9B}; + transport.write(output1); + byte[] output2 = {(byte) 0x83, 0x10, 0x00}; + transport.write(output2, 0, 2); + byte[] expected = {0x72, 0x56, 0x29, (byte) 0xAF, (byte) 0x9B, (byte) 0x83, 0x10}; + TByteArrayOutputStream outputByteArray = transport.getOutput(); + Assert.assertEquals(ByteBuffer.wrap(expected), ByteBuffer.wrap(outputByteArray.get(), 0, outputByteArray.len())); + } +} diff --git a/lib/java/test/org/apache/thrift/transport/TestTSaslTransports.java b/lib/java/test/org/apache/thrift/transport/TestTSaslTransports.java index 788395ff356..d384d7fa3a0 100644 --- a/lib/java/test/org/apache/thrift/transport/TestTSaslTransports.java +++ b/lib/java/test/org/apache/thrift/transport/TestTSaslTransports.java @@ -20,6 +20,7 @@ package org.apache.thrift.transport; import java.io.IOException; +import java.nio.charset.StandardCharsets; import java.util.HashMap; import java.util.Map; @@ -39,6 +40,7 @@ import junit.framework.TestCase; +import org.apache.thrift.TConfiguration; import org.apache.thrift.TProcessor; import org.apache.thrift.protocol.TProtocolFactory; import org.apache.thrift.server.ServerTestBase; @@ -52,17 +54,17 @@ public class TestTSaslTransports extends TestCase { private static final Logger LOGGER = LoggerFactory.getLogger(TestTSaslTransports.class); - private static final String HOST = "localhost"; - private static final String SERVICE = "thrift-test"; - private static final String PRINCIPAL = "thrift-test-principal"; - private static final String PASSWORD = "super secret password"; - private static final String REALM = "thrift-test-realm"; + public static final String HOST = "localhost"; + public static final String SERVICE = "thrift-test"; + public static final String PRINCIPAL = "thrift-test-principal"; + public static final String PASSWORD = "super secret password"; + public static final String REALM = "thrift-test-realm"; - private static final String UNWRAPPED_MECHANISM = "CRAM-MD5"; - private static final Map UNWRAPPED_PROPS = null; + public static final String UNWRAPPED_MECHANISM = "CRAM-MD5"; + public static final Map UNWRAPPED_PROPS = null; - private static final String WRAPPED_MECHANISM = "DIGEST-MD5"; - private static final Map WRAPPED_PROPS = new HashMap(); + public static final String WRAPPED_MECHANISM = "DIGEST-MD5"; + public static final Map WRAPPED_PROPS = new HashMap(); static { WRAPPED_PROPS.put(Sasl.QOP, "auth-int"); @@ -79,7 +81,7 @@ public class TestTSaslTransports extends TestCase { + "'We hold these truths to be self-evident, that all men are created equal.'"; - private static class TestSaslCallbackHandler implements CallbackHandler { + public static class TestSaslCallbackHandler implements CallbackHandler { private final String password; public TestSaslCallbackHandler(String password) { @@ -264,7 +266,7 @@ public void testWithServer() throws Exception { new TestTSaslTransportsWithServer().testIt(); } - private static class TestTSaslTransportsWithServer extends ServerTestBase { + public static class TestTSaslTransportsWithServer extends ServerTestBase { private Thread serverThread; private TServer server; @@ -332,12 +334,8 @@ public byte[] evaluateChallenge(byte[] challenge) throws SaslException { throw new SaslException("Already complete!"); } - try { - hasProvidedInitialResponse = true; - return username.getBytes("UTF-8"); - } catch (IOException e) { - throw new SaslException(e.toString()); - } + hasProvidedInitialResponse = true; + return username.getBytes(StandardCharsets.UTF_8); } public boolean isComplete() { return hasProvidedInitialResponse; } public byte[] unwrap(byte[] incoming, int offset, int len) { @@ -354,11 +352,7 @@ private static class AnonymousServer implements SaslServer { private String user; public String getMechanismName() { return "ANONYMOUS"; } public byte[] evaluateResponse(byte[] response) throws SaslException { - try { - this.user = new String(response, "UTF-8"); - } catch (IOException e) { - throw new SaslException(e.toString()); - } + this.user = new String(response, StandardCharsets.UTF_8); return null; } public boolean isComplete() { return user != null; } @@ -416,9 +410,10 @@ public SaslAnonymousProvider() { private static class MockTTransport extends TTransport { byte[] badHeader = null; - private TMemoryInputTransport readBuffer = new TMemoryInputTransport(); + private TMemoryInputTransport readBuffer; - public MockTTransport(int mode) { + public MockTTransport(int mode) throws TTransportException { + readBuffer = new TMemoryInputTransport(); if (mode==1) { // Invalid status byte badHeader = new byte[] { (byte)0xFF, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x05 }; @@ -450,25 +445,41 @@ public int read(byte[] buf, int off, int len) throws TTransportException { @Override public void write(byte[] buf, int off, int len) throws TTransportException {} + + @Override + public TConfiguration getConfiguration() { + return readBuffer.getConfiguration(); + } + + @Override + public void updateKnownMessageSize(long size) throws TTransportException { + readBuffer.updateKnownMessageSize(size); + } + + @Override + public void checkReadBytesAvailable(long numBytes) throws TTransportException { + readBuffer.checkReadBytesAvailable(numBytes); + } } public void testBadHeader() { - TSaslTransport saslTransport = new TSaslServerTransport(new MockTTransport(1)); + TSaslTransport saslTransport; try { + saslTransport = new TSaslServerTransport(new MockTTransport(1)); saslTransport.receiveSaslMessage(); fail("Should have gotten an error due to incorrect status byte value."); } catch (TTransportException e) { assertEquals(e.getMessage(), "Invalid status -1"); } - saslTransport = new TSaslServerTransport(new MockTTransport(2)); try { + saslTransport = new TSaslServerTransport(new MockTTransport(2)); saslTransport.receiveSaslMessage(); fail("Should have gotten an error due to negative payload length."); } catch (TTransportException e) { assertEquals(e.getMessage(), "Invalid payload header length: -1"); } - saslTransport = new TSaslServerTransport(new MockTTransport(3)); try { + saslTransport = new TSaslServerTransport(new MockTTransport(3)); saslTransport.receiveSaslMessage(); fail("Should have gotten an error due to bogus (large) payload length."); } catch (TTransportException e) { diff --git a/lib/java/test/org/apache/thrift/transport/TestTZlibTransport.java b/lib/java/test/org/apache/thrift/transport/TestTZlibTransport.java index 3d7f9c1c9a7..1b3970012d8 100644 --- a/lib/java/test/org/apache/thrift/transport/TestTZlibTransport.java +++ b/lib/java/test/org/apache/thrift/transport/TestTZlibTransport.java @@ -33,7 +33,7 @@ public class TestTZlibTransport extends TestCase { - protected TTransport getTransport(TTransport underlying) { + protected TTransport getTransport(TTransport underlying) throws TTransportException { return new TZlibTransport(underlying); } diff --git a/lib/java/test/org/apache/thrift/transport/WriteCountingTransport.java b/lib/java/test/org/apache/thrift/transport/WriteCountingTransport.java index 358f5c6ebe4..50b778b898d 100644 --- a/lib/java/test/org/apache/thrift/transport/WriteCountingTransport.java +++ b/lib/java/test/org/apache/thrift/transport/WriteCountingTransport.java @@ -19,6 +19,8 @@ package org.apache.thrift.transport; +import org.apache.thrift.TConfiguration; + public class WriteCountingTransport extends TTransport { public int writeCount = 0; private final TTransport trans; @@ -51,4 +53,19 @@ public void write(byte[] buf, int off, int len) throws TTransportException { public void flush() throws TTransportException { trans.flush(); } -} \ No newline at end of file + + @Override + public TConfiguration getConfiguration() { + return trans.getConfiguration(); + } + + @Override + public void updateKnownMessageSize(long size) throws TTransportException { + trans.updateKnownMessageSize(size); + } + + @Override + public void checkReadBytesAvailable(long numBytes) throws TTransportException { + trans.checkReadBytesAvailable(numBytes); + } +} diff --git a/lib/java/test/org/apache/thrift/transport/sasl/TestDataFrameReader.java b/lib/java/test/org/apache/thrift/transport/sasl/TestDataFrameReader.java new file mode 100644 index 00000000000..9ae0e1ead0d --- /dev/null +++ b/lib/java/test/org/apache/thrift/transport/sasl/TestDataFrameReader.java @@ -0,0 +1,61 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.thrift.transport.sasl; + +import org.apache.thrift.transport.TMemoryInputTransport; +import org.apache.thrift.transport.TTransportException; +import org.junit.Assert; +import org.junit.Test; + +import java.nio.ByteBuffer; + +public class TestDataFrameReader { + + @Test + public void testRead() throws TTransportException { + // Prepare data + int payloadSize = 23; + ByteBuffer buffer = ByteBuffer.allocate(DataFrameHeaderReader.PAYLOAD_LENGTH_BYTES + payloadSize); + buffer.putInt(payloadSize); + for (int i = 0; i < payloadSize; i++) { + buffer.put((byte) i); + } + buffer.rewind(); + + TMemoryInputTransport transport = new TMemoryInputTransport(); + DataFrameReader dataFrameReader = new DataFrameReader(); + // No bytes received. + dataFrameReader.read(transport); + Assert.assertFalse("No bytes received", dataFrameReader.isComplete()); + Assert.assertFalse("No bytes received", dataFrameReader.getHeader().isComplete()); + // Payload size (header) and part of the payload are received. + transport.reset(buffer.array(), 0, 6); + dataFrameReader.read(transport); + Assert.assertFalse("Only header is complete", dataFrameReader.isComplete()); + Assert.assertTrue("Header should be complete", dataFrameReader.getHeader().isComplete()); + Assert.assertEquals("Payload size should be " + payloadSize, payloadSize, dataFrameReader.getHeader().payloadSize()); + // Read the rest of payload. + transport.reset(buffer.array(), 6, 21); + dataFrameReader.read(transport); + Assert.assertTrue("Reader should be complete", dataFrameReader.isComplete()); + buffer.position(DataFrameHeaderReader.PAYLOAD_LENGTH_BYTES); + Assert.assertEquals("Payload should be the same as from the transport", buffer, ByteBuffer.wrap(dataFrameReader.getPayload())); + } +} diff --git a/lib/java/test/org/apache/thrift/transport/sasl/TestDataFrameWriter.java b/lib/java/test/org/apache/thrift/transport/sasl/TestDataFrameWriter.java new file mode 100644 index 00000000000..60fe5c96b2a --- /dev/null +++ b/lib/java/test/org/apache/thrift/transport/sasl/TestDataFrameWriter.java @@ -0,0 +1,101 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.thrift.transport.sasl; + +import java.io.IOException; +import java.nio.ByteBuffer; + +import org.apache.thrift.EncodingUtils; +import org.apache.thrift.transport.TNonblockingTransport; +import org.junit.Assert; +import org.junit.Test; +import org.mockito.Mockito; +import org.mockito.invocation.InvocationOnMock; +import org.mockito.stubbing.Answer; + +import static org.apache.thrift.transport.sasl.DataFrameHeaderReader.PAYLOAD_LENGTH_BYTES; + +public class TestDataFrameWriter { + + private static final byte[] BYTES = new byte[]{0x32, 0x2A, (byte) 0xE1, 0x18, (byte) 0x90, 0x75}; + + @Test + public void testProvideEntireByteArrayAsPayload() { + DataFrameWriter frameWriter = new DataFrameWriter(); + frameWriter.withOnlyPayload(BYTES); + byte[] expectedBytes = new byte[BYTES.length + PAYLOAD_LENGTH_BYTES]; + EncodingUtils.encodeBigEndian(BYTES.length, expectedBytes); + System.arraycopy(BYTES, 0, expectedBytes, PAYLOAD_LENGTH_BYTES, BYTES.length); + Assert.assertEquals(ByteBuffer.wrap(expectedBytes), frameWriter.frameBytes); + } + + @Test + public void testProvideByteArrayPortionAsPayload() { + DataFrameWriter frameWriter = new DataFrameWriter(); + int portionOffset = 2; + int portionLength = 3; + frameWriter.withOnlyPayload(BYTES, portionOffset, portionLength); + byte[] expectedBytes = new byte[portionLength + PAYLOAD_LENGTH_BYTES]; + EncodingUtils.encodeBigEndian(portionLength, expectedBytes); + System.arraycopy(BYTES, portionOffset, expectedBytes, PAYLOAD_LENGTH_BYTES, portionLength); + Assert.assertEquals(ByteBuffer.wrap(expectedBytes), frameWriter.frameBytes); + } + + @Test(expected = IllegalArgumentException.class) + public void testProvideHeaderAndPayload() { + DataFrameWriter frameWriter = new DataFrameWriter(); + frameWriter.withHeaderAndPayload(new byte[1], new byte[1]); + } + + @Test(expected = IllegalStateException.class) + public void testProvidePayloadToIncompleteFrame() { + DataFrameWriter frameWriter = new DataFrameWriter(); + frameWriter.withOnlyPayload(BYTES); + frameWriter.withOnlyPayload(new byte[1]); + } + + @Test + public void testWrite() throws Exception { + DataFrameWriter frameWriter = new DataFrameWriter(); + frameWriter.withOnlyPayload(BYTES); + // Slow socket which writes one byte per call. + TNonblockingTransport transport = Mockito.mock(TNonblockingTransport.class); + SlowWriting slowWriting = new SlowWriting(); + Mockito.when(transport.write(frameWriter.frameBytes)).thenAnswer(slowWriting); + frameWriter.write(transport); + while (slowWriting.written < frameWriter.frameBytes.limit()) { + Assert.assertFalse("Frame writer should not be complete", frameWriter.isComplete()); + frameWriter.write(transport); + } + Assert.assertTrue("Frame writer should be complete", frameWriter.isComplete()); + } + + private static class SlowWriting implements Answer { + int written = 0; + + @Override + public Integer answer(InvocationOnMock invocation) throws Throwable { + ByteBuffer bytes = (ByteBuffer) invocation.getArguments()[0]; + bytes.get(); + written ++; + return 1; + } + } +} diff --git a/lib/java/test/org/apache/thrift/transport/sasl/TestSaslNegotiationFrameReader.java b/lib/java/test/org/apache/thrift/transport/sasl/TestSaslNegotiationFrameReader.java new file mode 100644 index 00000000000..f2abbe65877 --- /dev/null +++ b/lib/java/test/org/apache/thrift/transport/sasl/TestSaslNegotiationFrameReader.java @@ -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. + */ + +package org.apache.thrift.transport.sasl; + +import org.apache.thrift.transport.TMemoryInputTransport; +import org.apache.thrift.transport.TTransportException; +import org.junit.Assert; +import org.junit.Test; + +import java.nio.ByteBuffer; + +public class TestSaslNegotiationFrameReader { + + @Test + public void testRead() throws TTransportException { + TMemoryInputTransport transport = new TMemoryInputTransport(); + SaslNegotiationFrameReader negotiationReader = new SaslNegotiationFrameReader(); + // No bytes received + negotiationReader.read(transport); + Assert.assertFalse("No bytes received", negotiationReader.isComplete()); + Assert.assertFalse("No bytes received", negotiationReader.getHeader().isComplete()); + // Read header + ByteBuffer buffer = ByteBuffer.allocate(5); + buffer.put(0, NegotiationStatus.OK.getValue()); + buffer.putInt(1, 10); + transport.reset(buffer.array()); + negotiationReader.read(transport); + Assert.assertFalse("Only header is complete", negotiationReader.isComplete()); + Assert.assertTrue("Header should be complete", negotiationReader.getHeader().isComplete()); + Assert.assertEquals("Payload size should be 10", 10, negotiationReader.getHeader().payloadSize()); + // Read payload + transport.reset(new byte[20]); + negotiationReader.read(transport); + Assert.assertTrue("Reader should be complete", negotiationReader.isComplete()); + Assert.assertEquals("Payload length should be 10", 10, negotiationReader.getPayload().length); + } + + @Test (expected = TSaslNegotiationException.class) + public void testReadInvalidNegotiationStatus() throws TTransportException { + byte[] bytes = new byte[5]; + // Invalid status byte. + bytes[0] = -1; + TMemoryInputTransport transport = new TMemoryInputTransport(bytes); + SaslNegotiationFrameReader negotiationReader = new SaslNegotiationFrameReader(); + negotiationReader.read(transport); + } +} diff --git a/lib/java/test/org/apache/thrift/transport/sasl/TestSaslNegotiationFrameWriter.java b/lib/java/test/org/apache/thrift/transport/sasl/TestSaslNegotiationFrameWriter.java new file mode 100644 index 00000000000..ce7ff295c30 --- /dev/null +++ b/lib/java/test/org/apache/thrift/transport/sasl/TestSaslNegotiationFrameWriter.java @@ -0,0 +1,56 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.thrift.transport.sasl; + +import java.nio.ByteBuffer; + +import org.apache.thrift.EncodingUtils; +import org.junit.Assert; +import org.junit.Test; + +import static org.apache.thrift.transport.sasl.SaslNegotiationFrameWriter.HEADER_BYTES; + +public class TestSaslNegotiationFrameWriter { + + private static final byte[] PAYLOAD = {0x11, 0x08, 0x3F, 0x58, 0x73, 0x22, 0x00, (byte) 0xFF}; + + @Test + public void testWithHeaderAndPayload() { + SaslNegotiationFrameWriter frameWriter = new SaslNegotiationFrameWriter(); + frameWriter.withHeaderAndPayload(new byte[] {NegotiationStatus.OK.getValue()}, PAYLOAD); + byte[] expectedBytes = new byte[HEADER_BYTES + PAYLOAD.length]; + expectedBytes[0] = NegotiationStatus.OK.getValue(); + EncodingUtils.encodeBigEndian(PAYLOAD.length, expectedBytes, 1); + System.arraycopy(PAYLOAD, 0, expectedBytes, HEADER_BYTES, PAYLOAD.length); + Assert.assertEquals(ByteBuffer.wrap(expectedBytes), frameWriter.frameBytes); + } + + @Test(expected = IllegalArgumentException.class) + public void testWithInvalidHeaderLength() { + SaslNegotiationFrameWriter frameWriter = new SaslNegotiationFrameWriter(); + frameWriter.withHeaderAndPayload(new byte[5], 0, 2, PAYLOAD, 0, 1); + } + + @Test(expected = UnsupportedOperationException.class) + public void testWithOnlyPayload() { + SaslNegotiationFrameWriter frameWriter = new SaslNegotiationFrameWriter(); + frameWriter.withOnlyPayload(new byte[0]); + } +} diff --git a/lib/java/test/org/apache/thrift/utils/TestStringUtils.java b/lib/java/test/org/apache/thrift/utils/TestStringUtils.java new file mode 100644 index 00000000000..3224e77b357 --- /dev/null +++ b/lib/java/test/org/apache/thrift/utils/TestStringUtils.java @@ -0,0 +1,59 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.thrift.utils; + +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; + +public class TestStringUtils { + + @Test + public void testToHexString() { + byte[] bytes = {0x00, 0x1A, (byte) 0xEF, (byte) 0xAB, (byte) 0x92}; + Assert.assertEquals("001AEFAB92", StringUtils.bytesToHexString(bytes)); + Assert.assertEquals("EFAB92", StringUtils.bytesToHexString(bytes, 2, 3)); + Assert.assertNull(StringUtils.bytesToHexString(null)); + } + + + private byte[] bytes; + + @Before + public void setUp() throws Exception { + bytes = new byte[]{1, 2, 3, 4, 5}; + } + + @Test(expected = IllegalArgumentException.class) + public void testNegativeLength() { + StringUtils.bytesToHexString(bytes, 0, -1); + } + + @Test(expected = IndexOutOfBoundsException.class) + public void testNegativeStartOffset() { + StringUtils.bytesToHexString(bytes, -1, 1); + } + + @Test(expected = IndexOutOfBoundsException.class) + public void testInvalidRange() { + StringUtils.bytesToHexString(bytes, 5, 1); + } + +} diff --git a/lib/javame/src/org/apache/thrift/protocol/TProtocolUtil.java b/lib/javame/src/org/apache/thrift/protocol/TProtocolUtil.java index 9bf10f67ea4..c327448efbf 100644 --- a/lib/javame/src/org/apache/thrift/protocol/TProtocolUtil.java +++ b/lib/javame/src/org/apache/thrift/protocol/TProtocolUtil.java @@ -152,7 +152,8 @@ public static void skip(TProtocol prot, byte type, int maxDepth) break; } default: - break; + throw new TProtocolException(TProtocolException.INVALID_DATA, + "Unrecognized type " + type); } } } diff --git a/lib/js/CMakeLists.txt b/lib/js/CMakeLists.txt new file mode 100644 index 00000000000..c312a21f5c3 --- /dev/null +++ b/lib/js/CMakeLists.txt @@ -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. +# + +if(NOT JAVASCRIPT_INSTALL_DIR) + if(IS_ABSOLUTE "${LIB_INSTALL_DIR}") + set(JAVASCRIPT_INSTALL_DIR "${LIB_INSTALL_DIR}/js") + else() + set(JAVASCRIPT_INSTALL_DIR "${CMAKE_INSTALL_PREFIX}/${LIB_INSTALL_DIR}/js") + endif() +endif() + +if(IS_ABSOLUTE "${DOC_INSTALL_DIR}") + set(JAVASCRIPT_DOC_INSTALL_DIR "${DOC_INSTALL_DIR}/js") +else() + set(JAVASCRIPT_DOC_INSTALL_DIR "${CMAKE_INSTALL_PREFIX}/${DOC_INSTALL_DIR}/js") +endif() + +add_custom_target(ThriftJavascriptPreDeps ALL + COMMENT "Installing Javascript dependencies using npm" + DEPENDS copy-thrift-compiler + COMMAND npm install + WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" +) + +add_custom_target(ThriftJavascript ALL + COMMENT "Building Javascript library using npx Grunt wrapper" + DEPENDS ThriftJavascriptPreDeps + COMMAND npx grunt + WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" +) + +install(DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/dist/" + DESTINATION "${JAVASCRIPT_INSTALL_DIR}" + FILES_MATCHING PATTERN "thrift*.js") +install(DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/doc/" + DESTINATION "${JAVASCRIPT_DOC_INSTALL_DIR}") diff --git a/lib/js/Gruntfile.js b/lib/js/Gruntfile.js index bb7691a0a02..4421f0cb475 100644 --- a/lib/js/Gruntfile.js +++ b/lib/js/Gruntfile.js @@ -39,27 +39,22 @@ module.exports = function(grunt) { }, shell: { InstallThriftJS: { - command: 'mkdir -p test/build/js/lib; cp src/thrift.js test/build/js/thrift.js' + command: 'cp src/thrift.js test/build/js/thrift.js' }, InstallThriftNodeJSDep: { - command: 'cd ../..; npm install' + command: 'cd ../.. && npm install' }, InstallTestLibs: { - command: 'cd test; ant download_jslibs' + command: 'cd test && ant download_jslibs' }, ThriftGen: { command: [ - 'mkdir -p test/gen-js', - '../../compiler/cpp/thrift -gen js --out test/gen-js ../../test/ThriftTest.thrift', - '../../compiler/cpp/thrift -gen js --out test/gen-js ../../test/JsDeepConstructorTest.thrift', - 'mkdir -p test/gen-js-jquery', - '../../compiler/cpp/thrift -gen js:jquery --out test/gen-js-jquery ../../test/ThriftTest.thrift', - 'mkdir -p test/gen-nodejs', - '../../compiler/cpp/thrift -gen js:node --out test/gen-nodejs ../../test/ThriftTest.thrift', - 'mkdir -p test/gen-js-es6', - '../../compiler/cpp/thrift -gen js:es6 --out test/gen-js-es6 ../../test/ThriftTest.thrift', - 'mkdir -p test/gen-nodejs-es6', - '../../compiler/cpp/thrift -gen js:node,es6 --out ./test/gen-nodejs-es6 ../../test/ThriftTest.thrift', + '"../../compiler/cpp/thrift" -gen js --out test/gen-js ../../test/ThriftTest.thrift', + '"../../compiler/cpp/thrift" -gen js --out test/gen-js ../../test/JsDeepConstructorTest.thrift', + '"../../compiler/cpp/thrift" -gen js:jquery --out test/gen-js-jquery ../../test/ThriftTest.thrift', + '"../../compiler/cpp/thrift" -gen js:node --out test/gen-nodejs ../../test/ThriftTest.thrift', + '"../../compiler/cpp/thrift" -gen js:es6 --out test/gen-js-es6 ../../test/ThriftTest.thrift', + '"../../compiler/cpp/thrift" -gen js:node,es6 --out ./test/gen-nodejs-es6 ../../test/ThriftTest.thrift', ].join(' && ') }, ThriftGenJQ: { @@ -68,6 +63,16 @@ module.exports = function(grunt) { ThriftGenDeepConstructor: { command: '../../compiler/cpp/thrift -gen js -o test ../../test/JsDeepConstructorTest.thrift' }, + ThriftBrowserifyNodeInt64: { + command: [ + './node_modules/browserify/bin/cmd.js ./node_modules/node-int64/Int64.js -s Int64 -o test/build/js/lib/Int64.js', + './node_modules/browserify/bin/cmd.js ../nodejs/lib/thrift/int64_util.js -s Int64Util -o test/build/js/lib/Int64Util.js', + './node_modules/browserify/bin/cmd.js ./node_modules/json-int64/index.js -s JSONInt64 -o test/build/js/lib/JSONInt64.js' + ].join(' && ') + }, + ThriftGenInt64: { + command: '../../compiler/cpp/thrift -gen js -o test ../../test/Int64Test.thrift' + }, ThriftGenDoubleConstants: { command: '../../compiler/cpp/thrift -gen js -o test ../../test/DoubleConstantsTest.thrift' }, @@ -147,6 +152,18 @@ module.exports = function(grunt) { }, } }, + ThriftJS_Int64: { + options: { + urls: [ + 'http://localhost:8089/test-int64.html' + ], + puppeteer: { + headless: true, + args: ['--no-sandbox'], + ignoreHTTPSErrors: true, + }, + } + }, ThriftWS: { options: { urls: [ @@ -218,7 +235,7 @@ module.exports = function(grunt) { } }, jshint: { - // The main thrift library file. not es6 yet :( + // The main Thrift library file. not es6 yet :( lib: { src: ['src/**/*.js'], }, @@ -261,32 +278,55 @@ module.exports = function(grunt) { grunt.loadNpmTasks('grunt-jsdoc'); grunt.loadNpmTasks('grunt-shell-spawn'); - grunt.registerTask('wait', 'Wait just one second for server to start', function () { + grunt.registerTask('wait', 'Wait just one second for the server to start', function () { var done = this.async(); setTimeout(function() { done(true); }, 1000); }); + grunt.registerTask('CreateDirectories', 'Creating required local directories', function () { + grunt.file.mkdir('test/build/js/lib'); + grunt.file.mkdir('test/gen-js'); + grunt.file.mkdir('test/gen-js-jquery'); + grunt.file.mkdir('test/gen-nodejs'); + grunt.file.mkdir('test/gen-js-es6'); + grunt.file.mkdir('test/gen-nodejs-es6'); +}); + grunt.registerTask('installAndGenerate', [ - 'shell:InstallThriftJS', 'shell:InstallThriftNodeJSDep', 'shell:ThriftGen', + 'CreateDirectories', + 'shell:InstallThriftJS', + 'shell:InstallThriftNodeJSDep', + 'shell:ThriftGen', 'shell:ThriftGenDeepConstructor', + 'shell:ThriftGenDoubleConstants', 'shell:InstallTestLibs', + 'shell:ThriftBrowserifyNodeInt64', + 'shell:ThriftGenInt64' ]); grunt.registerTask('test', [ 'installAndGenerate', 'jshint', - 'shell:ThriftTestServer', 'shell:ThriftTestServer_TLS', - 'shell:ThriftTestServerES6', 'shell:ThriftTestServerES6_TLS', + 'shell:ThriftTestServer', + 'shell:ThriftTestServer_TLS', + 'shell:ThriftTestServerES6', + 'shell:ThriftTestServerES6_TLS', 'wait', 'qunit:ThriftDeepConstructor', - 'qunit:ThriftJS', 'qunit:ThriftJS_TLS', + 'qunit:ThriftJS', + 'qunit:ThriftJS_TLS', + 'qunit:ThriftJS_DoubleRendering', 'qunit:ThriftWS', - 'qunit:ThriftJSJQ', 'qunit:ThriftJSJQ_TLS', + 'qunit:ThriftJSJQ', + 'qunit:ThriftJSJQ_TLS', 'qunit:ThriftWSES6', - 'shell:ThriftTestServer:kill', 'shell:ThriftTestServer_TLS:kill', - 'shell:ThriftTestServerES6:kill', 'shell:ThriftTestServerES6_TLS:kill', + 'qunit:ThriftJS_Int64', + 'shell:ThriftTestServer:kill', + 'shell:ThriftTestServer_TLS:kill', + 'shell:ThriftTestServerES6:kill', + 'shell:ThriftTestServerES6_TLS:kill', ]); grunt.registerTask('default', ['test', 'concat', 'uglify', 'jsdoc']); }; diff --git a/lib/js/Makefile.am b/lib/js/Makefile.am index 9ea20a4ab8d..4906d7ddbcc 100644 --- a/lib/js/Makefile.am +++ b/lib/js/Makefile.am @@ -18,13 +18,46 @@ # # Make sure this doesn't fail if ant is not configured. -# We call install twice to work around npm issues +# We call npm install twice to work around older npm issues +# (note these issues may no longer be present, but it is ok) # + if HAVE_NPM + SUBDIRS = test -check-local: all +prereq: $(NPM) install || $(NPM) install $(NPM) list + +check-local: prereq all ./node_modules/.bin/grunt + +doc: prereq + ./node_modules/.bin/grunt jsdoc + endif + +clean-local: + $(RM) -r dist + $(RM) -r doc + $(RM) -r node_modules + $(RM) -r test/build/ + $(RM) -r test/gen-*/ + +dist-hook: + $(RM) -r $(distdir)/dist/ + $(RM) -r $(distdir)/doc/ + $(RM) -r $(distdir)/node_modules/ + $(RM) -r $(distdir)/test/build/ + $(RM) -r $(distdir)/test/gen-*/ + +EXTRA_DIST = \ + coding_standards.md \ + Gruntfile.js \ + package.json \ + package-lock.json \ + CMakeLists.txt \ + README.md \ + src \ + test diff --git a/lib/js/README.md b/lib/js/README.md index 9d51e2a3ca3..5c7a1c23b84 100644 --- a/lib/js/README.md +++ b/lib/js/README.md @@ -138,3 +138,8 @@ TypeScript definition files can also be generated by running: thrift --gen js:ts file.thrift +# Breaking Changes + +## 0.13.0 + +1. 64-bit integer constants are now generatd using node-int64 e.g.: var x = new Int64("7fffffffffffffff"); diff --git a/lib/js/package-lock.json b/lib/js/package-lock.json index 63d81c3aa1c..3f054cdbc0a 100644 --- a/lib/js/package-lock.json +++ b/lib/js/package-lock.json @@ -1,56 +1,61 @@ { "name": "thrift", - "version": "1.0.0", + "version": "0.14.0", "lockfileVersion": 1, "requires": true, "dependencies": { - "abbrev": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", - "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==" + "@babel/parser": { + "version": "7.8.4", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.8.4.tgz", + "integrity": "sha512-0fKu/QqildpXmPVaRBoXOlyBb3MC+J0A66x97qEfLOMkn3u6nfY5esWogQwi/K0BjASYy4DbnsEWnpNL6qT5Mw==", + "dev": true }, - "agent-base": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-4.2.1.tgz", - "integrity": "sha512-JVwXMr9nHYTUXsBFKUqhJwvlcYU/blreOEUkhNR2eXZIvwd+c+o5V4MgDPKWnMS/56awN3TRzIP+KoPn+roQtg==", + "JSONStream": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/JSONStream/-/JSONStream-1.3.5.tgz", + "integrity": "sha512-E+iruNOY8VV9s4JEbe1aNEm6MiszPRr/UfcHMz0TQh1BXSxHK+ASV1R6W4HpjBhSeS+54PIsAMCBmwD06LLsqQ==", "dev": true, "requires": { - "es6-promisify": "^5.0.0" + "jsonparse": "^1.2.0", + "through": ">=2.2.7 <3" } }, - "ajv": { - "version": "5.5.2", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-5.5.2.tgz", - "integrity": "sha1-c7Xuyj+rZT49P5Qis0GtQiBdyWU=", + "abbrev": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", + "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==", + "dev": true + }, + "acorn": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.1.0.tgz", + "integrity": "sha512-kL5CuoXA/dgxlBbVrflsflzQ3PAas7RYZB52NOm/6839iVYJgKMJ3cQJD+t2i5+qFa8h3MDpEOJiS64E8JLnSQ==", + "dev": true + }, + "acorn-node": { + "version": "1.8.2", + "resolved": "https://registry.npmjs.org/acorn-node/-/acorn-node-1.8.2.tgz", + "integrity": "sha512-8mt+fslDufLYntIoPAaIMUe/lrbrehIiwmR3t2k9LljIzoigEPF27eLk2hy8zSGzmR/ogr7zbRKINMo1u0yh5A==", "dev": true, - "optional": true, "requires": { - "co": "^4.6.0", - "fast-deep-equal": "^1.0.0", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.3.0" + "acorn": "^7.0.0", + "acorn-walk": "^7.0.0", + "xtend": "^4.0.2" } }, - "align-text": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/align-text/-/align-text-0.1.4.tgz", - "integrity": "sha1-DNkKVhCT810KmSVsIrcGlDP60Rc=", + "acorn-walk": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-7.1.1.tgz", + "integrity": "sha512-wdlPY2tm/9XBr7QkKlq0WQVgiuGTX6YWPyRyBviSoScBuLfTVQhvwg6wJ369GJ/1nPfTLMfnrFIfjqVg6d+jQQ==", + "dev": true + }, + "agent-base": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-4.2.1.tgz", + "integrity": "sha512-JVwXMr9nHYTUXsBFKUqhJwvlcYU/blreOEUkhNR2eXZIvwd+c+o5V4MgDPKWnMS/56awN3TRzIP+KoPn+roQtg==", "dev": true, "requires": { - "kind-of": "^3.0.2", - "longest": "^1.0.1", - "repeat-string": "^1.5.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } + "es6-promisify": "^5.0.0" } }, "ansi-regex": { @@ -127,21 +132,43 @@ "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=", "dev": true }, - "asn1": { - "version": "0.2.4", - "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz", - "integrity": "sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg==", + "asn1.js": { + "version": "4.10.1", + "resolved": "https://registry.npmjs.org/asn1.js/-/asn1.js-4.10.1.tgz", + "integrity": "sha512-p32cOF5q0Zqs9uBiONKYLm6BClCoBCM5O9JfeUSlnQLBTxYdTK+pW+nXflm8UkKd2UYlEbYz5qEi0JuZR9ckSw==", "dev": true, - "optional": true, "requires": { - "safer-buffer": "~2.1.0" + "bn.js": "^4.0.0", + "inherits": "^2.0.1", + "minimalistic-assert": "^1.0.0" } }, - "assert-plus": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", - "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=", - "dev": true + "assert": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/assert/-/assert-1.5.0.tgz", + "integrity": "sha512-EDsgawzwoun2CZkCgtxJbv392v4nbk9XDD06zI+kQYoBM/3RBWLlEyJARDOmhAAosBjWACEkKL6S+lIZtcAubA==", + "dev": true, + "requires": { + "object-assign": "^4.1.1", + "util": "0.10.3" + }, + "dependencies": { + "inherits": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz", + "integrity": "sha1-sX0I0ya0Qj5Wjv9xn5GwscvfafE=", + "dev": true + }, + "util": { + "version": "0.10.3", + "resolved": "http://registry.npmjs.org/util/-/util-0.10.3.tgz", + "integrity": "sha1-evsa/lCAUkZInj23/g7TeTNqwPk=", + "dev": true, + "requires": { + "inherits": "2.0.1" + } + } + } }, "assign-symbols": { "version": "1.0.0", @@ -161,38 +188,12 @@ "integrity": "sha512-jp/uFnooOiO+L211eZOoSyzpOITMXx1rBITauYykG3BRYPu8h0UcxsPNB04RR5vo4Tyz3+ay17tR6JVf9qzYWg==", "dev": true }, - "asynckit": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=", - "dev": true, - "optional": true - }, "atob": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz", "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==", "dev": true }, - "aws-sign2": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", - "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=", - "dev": true, - "optional": true - }, - "aws4": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.8.0.tgz", - "integrity": "sha512-ReZxvNHIOv88FlT7rxcXIIC0fPt4KZqZbOlivyWtXLt8ESx84zd3kMC6iK5jVeS2qt+g7ftS7ye4fi06X5rtRQ==", - "dev": true, - "optional": true - }, - "babylon": { - "version": "7.0.0-beta.19", - "resolved": "https://registry.npmjs.org/babylon/-/babylon-7.0.0-beta.19.tgz", - "integrity": "sha512-Vg0C9s/REX6/WIXN37UKpv5ZhRi6A4pjHlpkE34+8/a6c2W1Q692n3hmc+SZG5lKRnaExLUbxtJ1SVT+KaCQ/A==" - }, "balanced-match": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", @@ -254,20 +255,23 @@ } } }, - "bcrypt-pbkdf": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", - "integrity": "sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=", - "dev": true, - "optional": true, - "requires": { - "tweetnacl": "^0.14.3" - } + "base64-js": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.3.1.tgz", + "integrity": "sha512-mLQ4i2QO1ytvGWFWmcngKO//JXAQueZvwEKtjgQFM4jIK0kU+ytMfplL8j+n5mspOfjHwoAg+9yhb7BwAHm36g==", + "dev": true }, "bluebird": { - "version": "3.5.2", - "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.2.tgz", - "integrity": "sha512-dhHTWMI7kMx5whMQntl7Vr9C6BvV10lFXDAasnqnrMYhXVCzzk6IO9Fo2L75jXHT07WrOngL1WDXOp+yYS91Yg==" + "version": "3.7.2", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", + "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==", + "dev": true + }, + "bn.js": { + "version": "4.11.8", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.8.tgz", + "integrity": "sha512-ItfYfPLkWHUjckQCk8xC+LwxgK8NYcXywGigJgSwOP8Y2iyWT4f2vsZnoOXTTbo+o5yXmIUJ4gn5538SO5S3gA==", + "dev": true }, "brace-expansion": { "version": "1.1.11", @@ -308,13 +312,229 @@ } } }, + "brorand": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz", + "integrity": "sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8=", + "dev": true + }, + "browser-pack": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/browser-pack/-/browser-pack-6.1.0.tgz", + "integrity": "sha512-erYug8XoqzU3IfcU8fUgyHqyOXqIE4tUTTQ+7mqUjQlvnXkOO6OlT9c/ZoJVHYoAaqGxr09CN53G7XIsO4KtWA==", + "dev": true, + "requires": { + "JSONStream": "^1.0.3", + "combine-source-map": "~0.8.0", + "defined": "^1.0.0", + "safe-buffer": "^5.1.1", + "through2": "^2.0.0", + "umd": "^3.0.0" + } + }, + "browser-resolve": { + "version": "1.11.3", + "resolved": "https://registry.npmjs.org/browser-resolve/-/browser-resolve-1.11.3.tgz", + "integrity": "sha512-exDi1BYWB/6raKHmDTCicQfTkqwN5fioMFV4j8BsfMU4R2DK/QfZfK7kOVkmWCNANf0snkBzqGqAJBao9gZMdQ==", + "dev": true, + "requires": { + "resolve": "1.1.7" + } + }, + "browserify": { + "version": "16.5.0", + "resolved": "https://registry.npmjs.org/browserify/-/browserify-16.5.0.tgz", + "integrity": "sha512-6bfI3cl76YLAnCZ75AGu/XPOsqUhRyc0F/olGIJeCxtfxF2HvPKEcmjU9M8oAPxl4uBY1U7Nry33Q6koV3f2iw==", + "dev": true, + "requires": { + "JSONStream": "^1.0.3", + "assert": "^1.4.0", + "browser-pack": "^6.0.1", + "browser-resolve": "^1.11.0", + "browserify-zlib": "~0.2.0", + "buffer": "^5.0.2", + "cached-path-relative": "^1.0.0", + "concat-stream": "^1.6.0", + "console-browserify": "^1.1.0", + "constants-browserify": "~1.0.0", + "crypto-browserify": "^3.0.0", + "defined": "^1.0.0", + "deps-sort": "^2.0.0", + "domain-browser": "^1.2.0", + "duplexer2": "~0.1.2", + "events": "^2.0.0", + "glob": "^7.1.0", + "has": "^1.0.0", + "htmlescape": "^1.1.0", + "https-browserify": "^1.0.0", + "inherits": "~2.0.1", + "insert-module-globals": "^7.0.0", + "labeled-stream-splicer": "^2.0.0", + "mkdirp": "^0.5.0", + "module-deps": "^6.0.0", + "os-browserify": "~0.3.0", + "parents": "^1.0.1", + "path-browserify": "~0.0.0", + "process": "~0.11.0", + "punycode": "^1.3.2", + "querystring-es3": "~0.2.0", + "read-only-stream": "^2.0.0", + "readable-stream": "^2.0.2", + "resolve": "^1.1.4", + "shasum": "^1.0.0", + "shell-quote": "^1.6.1", + "stream-browserify": "^2.0.0", + "stream-http": "^3.0.0", + "string_decoder": "^1.1.1", + "subarg": "^1.0.0", + "syntax-error": "^1.1.1", + "through2": "^2.0.0", + "timers-browserify": "^1.0.1", + "tty-browserify": "0.0.1", + "url": "~0.11.0", + "util": "~0.10.1", + "vm-browserify": "^1.0.0", + "xtend": "^4.0.0" + }, + "dependencies": { + "glob": { + "version": "7.1.6", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", + "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "dev": true, + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + }, + "dependencies": { + "string_decoder": { + "version": "1.1.1", + "resolved": "http://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "requires": { + "safe-buffer": "~5.1.0" + } + } + } + }, + "string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "dev": true, + "requires": { + "safe-buffer": "~5.2.0" + }, + "dependencies": { + "safe-buffer": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.0.tgz", + "integrity": "sha512-fZEwUGbVl7kouZs1jCdMLdt95hdIv0ZeHg6L7qPeciMZhZ+/gdesW4wgTARkrFWEpspjEATAzUGPG8N2jJiwbg==", + "dev": true + } + } + } + } + }, + "browserify-aes": { + "version": "1.2.0", + "resolved": "http://registry.npmjs.org/browserify-aes/-/browserify-aes-1.2.0.tgz", + "integrity": "sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA==", + "dev": true, + "requires": { + "buffer-xor": "^1.0.3", + "cipher-base": "^1.0.0", + "create-hash": "^1.1.0", + "evp_bytestokey": "^1.0.3", + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "browserify-cipher": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/browserify-cipher/-/browserify-cipher-1.0.1.tgz", + "integrity": "sha512-sPhkz0ARKbf4rRQt2hTpAHqn47X3llLkUGn+xEJzLjwY8LRs2p0v7ljvI5EyoRO/mexrNunNECisZs+gw2zz1w==", + "dev": true, + "requires": { + "browserify-aes": "^1.0.4", + "browserify-des": "^1.0.0", + "evp_bytestokey": "^1.0.0" + } + }, + "browserify-des": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/browserify-des/-/browserify-des-1.0.2.tgz", + "integrity": "sha512-BioO1xf3hFwz4kc6iBhI3ieDFompMhrMlnDFC4/0/vd5MokpuAc3R+LYbwTA9A5Yc9pq9UYPqffKpW2ObuwX5A==", + "dev": true, + "requires": { + "cipher-base": "^1.0.1", + "des.js": "^1.0.0", + "inherits": "^2.0.1", + "safe-buffer": "^5.1.2" + } + }, + "browserify-rsa": { + "version": "4.0.1", + "resolved": "http://registry.npmjs.org/browserify-rsa/-/browserify-rsa-4.0.1.tgz", + "integrity": "sha1-IeCr+vbyApzy+vsTNWenAdQTVSQ=", + "dev": true, + "requires": { + "bn.js": "^4.1.0", + "randombytes": "^2.0.1" + } + }, + "browserify-sign": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/browserify-sign/-/browserify-sign-4.0.4.tgz", + "integrity": "sha1-qk62jl17ZYuqa/alfmMMvXqT0pg=", + "dev": true, + "requires": { + "bn.js": "^4.1.1", + "browserify-rsa": "^4.0.0", + "create-hash": "^1.1.0", + "create-hmac": "^1.1.2", + "elliptic": "^6.0.0", + "inherits": "^2.0.1", + "parse-asn1": "^5.0.0" + } + }, "browserify-zlib": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/browserify-zlib/-/browserify-zlib-0.1.4.tgz", - "integrity": "sha1-uzX4pRn2AOD6a4SFJByXnQFB+y0=", + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/browserify-zlib/-/browserify-zlib-0.2.0.tgz", + "integrity": "sha512-Z942RysHXmJrhqk88FmKBVq/v5tqmSkDz7p54G/MGyjMnCFFnC79XWNbg+Vta8W6Wb2qtSZTSxIGkJrRpCFEiA==", + "dev": true, + "requires": { + "pako": "~1.0.5" + } + }, + "buffer": { + "version": "5.4.3", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.4.3.tgz", + "integrity": "sha512-zvj65TkFeIt3i6aj5bIvJDzjjQQGs4o/sNoezg1F1kYap9Nu2jcUdpwzRSJTHMMzG0H7bZkn4rNQpImhuxWX2A==", "dev": true, "requires": { - "pako": "~0.2.0" + "base64-js": "^1.0.2", + "ieee754": "^1.1.4" } }, "buffer-from": { @@ -329,10 +549,16 @@ "integrity": "sha1-mXjOMXOIxkmth5MCjDR37wRKi1E=", "dev": true }, - "builtin-modules": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-1.1.1.tgz", - "integrity": "sha1-Jw8HbFpywC9bZaR9+Uxf46J4iS8=", + "buffer-xor": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/buffer-xor/-/buffer-xor-1.0.3.tgz", + "integrity": "sha1-JuYe0UIvtw3ULm42cp7VHYVf6Nk=", + "dev": true + }, + "builtin-status-codes": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/builtin-status-codes/-/builtin-status-codes-3.0.0.tgz", + "integrity": "sha1-hZgoeOIbmOHGZCXgPQF0eI9Wnug=", "dev": true }, "cache-base": { @@ -352,6 +578,12 @@ "unset-value": "^1.0.0" } }, + "cached-path-relative": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/cached-path-relative/-/cached-path-relative-1.0.2.tgz", + "integrity": "sha512-5r2GqsoEb4qMTTN9J+WzXfjov+hjxT+j3u5K+kIVNIwAd99DLCJE9pBIMP1qVeybV6JiijL385Oz0DcYxfbOIg==", + "dev": true + }, "camelcase": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-2.1.1.tgz", @@ -368,29 +600,21 @@ "map-obj": "^1.0.0" } }, - "caseless": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", - "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=", - "dev": true, - "optional": true - }, "catharsis": { - "version": "0.8.9", - "resolved": "https://registry.npmjs.org/catharsis/-/catharsis-0.8.9.tgz", - "integrity": "sha1-mMyJDKZS3S7w5ws3klMQ/56Q/Is=", - "requires": { - "underscore-contrib": "~0.3.0" - } - }, - "center-align": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/center-align/-/center-align-0.1.3.tgz", - "integrity": "sha1-qg0yYptu6XIgBBHL1EYckHvCt60=", + "version": "0.8.11", + "resolved": "https://registry.npmjs.org/catharsis/-/catharsis-0.8.11.tgz", + "integrity": "sha512-a+xUyMV7hD1BrDQA/3iPV7oc+6W26BgVJO05PGEoatMyIuPScQKsde6i3YorWX1qs+AZjnJ18NqdKoCtKiNh1g==", "dev": true, "requires": { - "align-text": "^0.1.3", - "lazy-cache": "^1.0.3" + "lodash": "^4.17.14" + }, + "dependencies": { + "lodash": { + "version": "4.17.15", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz", + "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==", + "dev": true + } } }, "chalk": { @@ -404,6 +628,16 @@ "supports-color": "^5.3.0" } }, + "cipher-base": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.4.tgz", + "integrity": "sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q==", + "dev": true, + "requires": { + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, "class-utils": { "version": "0.3.6", "resolved": "https://registry.npmjs.org/class-utils/-/class-utils-0.3.6.tgz", @@ -438,9 +672,9 @@ }, "dependencies": { "glob": { - "version": "7.1.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.3.tgz", - "integrity": "sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ==", + "version": "7.1.4", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.4.tgz", + "integrity": "sha512-hkLPepehmnKk41pUGm3sYxoFs/umurYfYJCerbXEyFIWcAzvpipAgVkBqqT9RBKMGjnq6kMuyYwha6csxbiM1A==", "dev": true, "requires": { "fs.realpath": "^1.0.0", @@ -453,24 +687,6 @@ } } }, - "cliui": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-2.1.0.tgz", - "integrity": "sha1-S0dXYP+AJkx2LDoXGQMukcf+oNE=", - "dev": true, - "requires": { - "center-align": "^0.1.1", - "right-align": "^0.1.1", - "wordwrap": "0.0.2" - } - }, - "co": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", - "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=", - "dev": true, - "optional": true - }, "coffeescript": { "version": "1.10.0", "resolved": "https://registry.npmjs.org/coffeescript/-/coffeescript-1.10.0.tgz", @@ -508,15 +724,24 @@ "integrity": "sha1-FopHAXVran9RoSzgyXv6KMCE7WM=", "dev": true }, - "combined-stream": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.7.tgz", - "integrity": "sha512-brWl9y6vOB1xYPZcpZde3N9zDByXTosAeMDo4p1wzo6UMOX4vumB+TP1RZ76sfE6Md68Q0NJSrE/gbezd4Ul+w==", + "combine-source-map": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/combine-source-map/-/combine-source-map-0.8.0.tgz", + "integrity": "sha1-pY0N8ELBhvz4IqjoAV9UUNLXmos=", "dev": true, "requires": { - "delayed-stream": "~1.0.0" + "convert-source-map": "~1.1.0", + "inline-source-map": "~0.6.0", + "lodash.memoize": "~3.0.3", + "source-map": "~0.5.3" } }, + "commander": { + "version": "2.20.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.0.tgz", + "integrity": "sha512-7j2y+40w61zy6YC2iRNpUe/NwhNyoXrYpHMrSunaMG64nRnaf96zO/KMQR4OyN/UnE5KLyEBnKHd4aG3rskjpQ==", + "dev": true + }, "component-emitter": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.2.1.tgz", @@ -576,6 +801,18 @@ "date-now": "^0.1.4" } }, + "constants-browserify": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/constants-browserify/-/constants-browserify-1.0.0.tgz", + "integrity": "sha1-wguW2MYXdIqvHBYCF2DNJ/y4y3U=", + "dev": true + }, + "convert-source-map": { + "version": "1.1.3", + "resolved": "http://registry.npmjs.org/convert-source-map/-/convert-source-map-1.1.3.tgz", + "integrity": "sha1-SCnId+n+SbMWHzvzZziI4gRpmGA=", + "dev": true + }, "copy-descriptor": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/copy-descriptor/-/copy-descriptor-0.1.1.tgz", @@ -588,17 +825,82 @@ "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=", "dev": true }, + "create-ecdh": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/create-ecdh/-/create-ecdh-4.0.3.tgz", + "integrity": "sha512-GbEHQPMOswGpKXM9kCWVrremUcBmjteUaQ01T9rkKCPDXfUHX0IoP9LpHYo2NPFampa4e+/pFDc3jQdxrxQLaw==", + "dev": true, + "requires": { + "bn.js": "^4.1.0", + "elliptic": "^6.0.0" + } + }, + "create-hash": { + "version": "1.2.0", + "resolved": "http://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz", + "integrity": "sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==", + "dev": true, + "requires": { + "cipher-base": "^1.0.1", + "inherits": "^2.0.1", + "md5.js": "^1.3.4", + "ripemd160": "^2.0.1", + "sha.js": "^2.4.0" + } + }, + "create-hmac": { + "version": "1.1.7", + "resolved": "http://registry.npmjs.org/create-hmac/-/create-hmac-1.1.7.tgz", + "integrity": "sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg==", + "dev": true, + "requires": { + "cipher-base": "^1.0.3", + "create-hash": "^1.1.0", + "inherits": "^2.0.1", + "ripemd160": "^2.0.0", + "safe-buffer": "^5.0.1", + "sha.js": "^2.4.8" + } + }, "cross-spawn": { - "version": "6.0.5", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", - "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.1.tgz", + "integrity": "sha512-u7v4o84SwFpD32Z8IIcPZ6z1/ie24O6RU3RbtL5Y316l3KuHVPx9ItBgWQ6VlfAFnRnTtMUrsQ9MUUTuEZjogg==", + "dev": true, + "requires": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "dependencies": { + "which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "requires": { + "isexe": "^2.0.0" + } + } + } + }, + "crypto-browserify": { + "version": "3.12.0", + "resolved": "https://registry.npmjs.org/crypto-browserify/-/crypto-browserify-3.12.0.tgz", + "integrity": "sha512-fz4spIh+znjO2VjL+IdhEpRJ3YN6sMzITSBijk6FK2UvTqruSQW+/cCZTSNsMiZNvUeq0CqurF+dAbyiGOY6Wg==", "dev": true, "requires": { - "nice-try": "^1.0.4", - "path-key": "^2.0.1", - "semver": "^5.5.0", - "shebang-command": "^1.2.0", - "which": "^1.2.9" + "browserify-cipher": "^1.0.0", + "browserify-sign": "^4.0.0", + "create-ecdh": "^4.0.0", + "create-hash": "^1.1.0", + "create-hmac": "^1.1.0", + "diffie-hellman": "^5.0.0", + "inherits": "^2.0.1", + "pbkdf2": "^3.0.3", + "public-encrypt": "^4.0.0", + "randombytes": "^2.0.0", + "randomfill": "^1.0.3" } }, "currently-unhandled": { @@ -610,22 +912,11 @@ "array-find-index": "^1.0.1" } }, - "cycle": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/cycle/-/cycle-1.0.3.tgz", - "integrity": "sha1-IegLK+hYD5i0aPN5QwZisEbDStI=", - "dev": true, - "optional": true - }, - "dashdash": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", - "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=", - "dev": true, - "optional": true, - "requires": { - "assert-plus": "^1.0.0" - } + "dash-ast": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/dash-ast/-/dash-ast-1.0.0.tgz", + "integrity": "sha512-Vy4dx7gquTeMcQR/hDkYLGUnwVil6vk4FOOct+djUnHOUWt+zJPJAaRIXaAFkPXtJjvlY7o3rfRu0/3hpnwoUA==", + "dev": true }, "date-now": { "version": "0.1.4", @@ -705,46 +996,98 @@ } } }, - "delayed-stream": { + "defined": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=", + "resolved": "https://registry.npmjs.org/defined/-/defined-1.0.0.tgz", + "integrity": "sha1-yY2bzvdWdBiOEQlpFRGZ45sfppM=", "dev": true }, + "deps-sort": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/deps-sort/-/deps-sort-2.0.1.tgz", + "integrity": "sha512-1orqXQr5po+3KI6kQb9A4jnXT1PBwggGl2d7Sq2xsnOeI9GPcE/tGcF9UiSZtZBM7MukY4cAh7MemS6tZYipfw==", + "dev": true, + "requires": { + "JSONStream": "^1.0.3", + "shasum-object": "^1.0.0", + "subarg": "^1.0.0", + "through2": "^2.0.0" + } + }, + "des.js": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/des.js/-/des.js-1.0.1.tgz", + "integrity": "sha512-Q0I4pfFrv2VPd34/vfLrFOoRmlYj3OV50i7fskps1jZWK1kApMWWT9G6RRUeYedLcBDIhnSDaUvJMb3AhUlaEA==", + "dev": true, + "requires": { + "inherits": "^2.0.1", + "minimalistic-assert": "^1.0.0" + } + }, "detect-file": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/detect-file/-/detect-file-1.0.0.tgz", "integrity": "sha1-8NZtA2cqglyxtzvbP+YjEMjlUrc=", "dev": true }, - "dom-serializer": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.1.0.tgz", - "integrity": "sha1-BzxpdUbOB4DOI75KKOKT5AvDDII=", + "detective": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/detective/-/detective-5.2.0.tgz", + "integrity": "sha512-6SsIx+nUUbuK0EthKjv0zrdnajCCXVYGmbYYiYjFVpzcjwEs/JMDZ8tPRG29J/HhN56t3GJp2cGSWDRjjot8Pg==", "dev": true, "requires": { - "domelementtype": "~1.1.1", - "entities": "~1.1.1" + "acorn-node": "^1.6.1", + "defined": "^1.0.0", + "minimist": "^1.1.1" }, "dependencies": { - "domelementtype": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.1.3.tgz", - "integrity": "sha1-vSh3PiZCiBrsUVRJJCmcXNgiGFs=", + "minimist": { + "version": "1.2.0", + "resolved": "http://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", "dev": true - }, + } + } + }, + "diffie-hellman": { + "version": "5.0.3", + "resolved": "http://registry.npmjs.org/diffie-hellman/-/diffie-hellman-5.0.3.tgz", + "integrity": "sha512-kqag/Nl+f3GwyK25fhUMYj81BUOrZ9IuJsjIcDE5icNM9FJHAVm3VcUDxdLPoQtTuUylWm6ZIknYJwwaPxsUzg==", + "dev": true, + "requires": { + "bn.js": "^4.1.0", + "miller-rabin": "^4.0.0", + "randombytes": "^2.0.0" + } + }, + "dom-serializer": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.1.1.tgz", + "integrity": "sha512-l0IU0pPzLWSHBcieZbpOKgkIn3ts3vAh7ZuFyXNwJxJXk/c4Gwj9xaTJwIDVQCXawWD0qb3IzMGH5rglQaO0XA==", + "dev": true, + "requires": { + "domelementtype": "^1.3.0", + "entities": "^1.1.1" + }, + "dependencies": { "entities": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/entities/-/entities-1.1.1.tgz", - "integrity": "sha1-blwtClYhtdra7O+AuQ7ftc13cvA=", + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/entities/-/entities-1.1.2.tgz", + "integrity": "sha512-f2LZMYl1Fzu7YSBKg+RoROelpOaNrcGmE9AZubeDfrCEia483oW4MI4VyFd5VNHIgQ/7qm1I0wUHK1eJnn2y2w==", "dev": true } } }, + "domain-browser": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/domain-browser/-/domain-browser-1.2.0.tgz", + "integrity": "sha512-jnjyiM6eRyZl2H+W8Q/zLMA481hzi0eszAaBUzIVnmYVDBbnLxVNnfu1HgEBvCbL+71FrxMl3E6lpKH7Ge3OXA==", + "dev": true + }, "domelementtype": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.3.0.tgz", - "integrity": "sha1-sXrtguirWeUt2cGbF1bg/BhyBMI=", + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.3.1.tgz", + "integrity": "sha512-BSKB+TSpMpFI/HOxCNr1O8aMOTZ8hT3pM3GQ0w/mWRmkhEDSFJkkyzz4XQsBV44BChwGkrDfMyjVD0eA2aFV3w==", "dev": true }, "domhandler": { @@ -766,15 +1109,60 @@ "domelementtype": "1" } }, - "ecc-jsbn": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", - "integrity": "sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=", + "duplexer": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.1.tgz", + "integrity": "sha1-rOb/gIwc5mtX0ev5eXessCM0z8E=", + "dev": true + }, + "duplexer2": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/duplexer2/-/duplexer2-0.1.4.tgz", + "integrity": "sha1-ixLauHjA1p4+eJEFFmKjL8a93ME=", "dev": true, - "optional": true, "requires": { - "jsbn": "~0.1.0", - "safer-buffer": "^2.1.0" + "readable-stream": "^2.0.2" + }, + "dependencies": { + "readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "dev": true, + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "http://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "requires": { + "safe-buffer": "~5.1.0" + } + } + } + }, + "elliptic": { + "version": "6.5.2", + "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.2.tgz", + "integrity": "sha512-f4x70okzZbIQl/NSRLkI/+tteV/9WqL98zx+SQ69KbXxmVrmjwsNUPn/gYJJ0sHvEak24cZgHIPegRePAtA/xw==", + "dev": true, + "requires": { + "bn.js": "^4.4.0", + "brorand": "^1.0.1", + "hash.js": "^1.0.0", + "hmac-drbg": "^1.0.0", + "inherits": "^2.0.1", + "minimalistic-assert": "^1.0.0", + "minimalistic-crypto-utils": "^1.0.0" } }, "entities": { @@ -800,7 +1188,7 @@ }, "es6-promisify": { "version": "5.0.0", - "resolved": "http://registry.npmjs.org/es6-promisify/-/es6-promisify-5.0.0.tgz", + "resolved": "https://registry.npmjs.org/es6-promisify/-/es6-promisify-5.0.0.tgz", "integrity": "sha1-UQnWLz5W6pZ8S2NQWu8IKRyKUgM=", "dev": true, "requires": { @@ -810,12 +1198,13 @@ "escape-string-regexp": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=" + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", + "dev": true }, "esprima": { - "version": "2.7.3", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-2.7.3.tgz", - "integrity": "sha1-luO3DVd59q1JzQMmc9HDEnZ7pYE=", + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", "dev": true }, "eventemitter2": { @@ -824,6 +1213,22 @@ "integrity": "sha1-j2G3XN4BKy6esoTUVFWDtWQ7Yas=", "dev": true }, + "events": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/events/-/events-2.1.0.tgz", + "integrity": "sha512-3Zmiobend8P9DjmKAty0Era4jV8oJ0yGYe2nJJAxgymF9+N8F2m0hhZiMoWtcfepExzNKZumFU3ksdQbInGWCg==", + "dev": true + }, + "evp_bytestokey": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz", + "integrity": "sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA==", + "dev": true, + "requires": { + "md5.js": "^1.3.4", + "safe-buffer": "^5.1.1" + } + }, "exit": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", @@ -978,33 +1383,12 @@ "yauzl": "2.4.1" } }, - "extsprintf": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", - "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=", + "fast-safe-stringify": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-2.0.7.tgz", + "integrity": "sha512-Utm6CdzT+6xsDk2m8S6uL8VHxNwI6Jub+e9NYTcAms28T84pTa25GJQV9j0CY0N1rM8hK4x6grpF2BQf+2qwVA==", "dev": true }, - "eyes": { - "version": "0.1.8", - "resolved": "https://registry.npmjs.org/eyes/-/eyes-0.1.8.tgz", - "integrity": "sha1-Ys8SAjTGg3hdkCNIqADvPgzCC8A=", - "dev": true, - "optional": true - }, - "fast-deep-equal": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-1.1.0.tgz", - "integrity": "sha1-wFNHeBfIa1HaqFPIHgWbcz0CNhQ=", - "dev": true, - "optional": true - }, - "fast-json-stable-stringify": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz", - "integrity": "sha1-1RQsDK7msRifh9OnYREGT4bIu/I=", - "dev": true, - "optional": true - }, "fd-slicer": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.0.1.tgz", @@ -1082,9 +1466,9 @@ } }, "fined": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/fined/-/fined-1.1.0.tgz", - "integrity": "sha1-s33IRLdqL15wgeiE98CuNE8VNHY=", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/fined/-/fined-1.1.1.tgz", + "integrity": "sha512-jQp949ZmEbiYHk3gkbdtpJ0G1+kgtLQBNdP5edFP7Fh+WAYceLQz6yO1SBj72Xkg8GVyTB3bBzAYrHJVh5Xd5g==", "dev": true, "requires": { "expand-tilde": "^2.0.2", @@ -1095,9 +1479,9 @@ } }, "flagged-respawn": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/flagged-respawn/-/flagged-respawn-1.0.0.tgz", - "integrity": "sha1-Tnmumy6zi/hrO7Vr8+ClaqX8q9c=", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/flagged-respawn/-/flagged-respawn-1.0.1.tgz", + "integrity": "sha512-lNaHNVymajmk0OJMBn8fVUAU1BtDeKIqKoVhk4xAALB57aALg6b4W0MfJ/cUE0g9YBXy5XhSlPIpYIJ7HaY/3Q==", "dev": true }, "for-in": { @@ -1115,25 +1499,6 @@ "for-in": "^1.0.1" } }, - "forever-agent": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", - "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=", - "dev": true, - "optional": true - }, - "form-data": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", - "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", - "dev": true, - "optional": true, - "requires": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.6", - "mime-types": "^2.1.12" - } - }, "fragment-cache": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/fragment-cache/-/fragment-cache-0.2.1.tgz", @@ -1143,36 +1508,24 @@ "map-cache": "^0.2.2" } }, - "fs-extra": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-1.0.0.tgz", - "integrity": "sha1-zTzl9+fLYUWIP8rjGR6Yd/hYeVA=", - "dev": true, - "optional": true, - "requires": { - "graceful-fs": "^4.1.2", - "jsonfile": "^2.1.0", - "klaw": "^1.0.0" - }, - "dependencies": { - "klaw": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/klaw/-/klaw-1.3.1.tgz", - "integrity": "sha1-QIhDO0azsbolnXh4XY6W9zugJDk=", - "dev": true, - "optional": true, - "requires": { - "graceful-fs": "^4.1.9" - } - } - } - }, "fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", "dev": true }, + "function-bind": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", + "dev": true + }, + "get-assigned-identifiers": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/get-assigned-identifiers/-/get-assigned-identifiers-1.2.0.tgz", + "integrity": "sha512-mBBwmeGTrxEMO4pMaaf/uUEFHnYtwr8FTe8Y/mer4rcV/bye0qGm6pw1bGZFGStxC5O76c5ZAVBGnqHmOaJpdQ==", + "dev": true + }, "get-stdin": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-4.0.1.tgz", @@ -1191,16 +1544,6 @@ "integrity": "sha1-BHpEl4n6Fg0Bj1SG7ZEyC27HiFw=", "dev": true }, - "getpass": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", - "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=", - "dev": true, - "optional": true, - "requires": { - "assert-plus": "^1.0.0" - } - }, "glob": { "version": "7.0.6", "resolved": "https://registry.npmjs.org/glob/-/glob-7.0.6.tgz", @@ -1240,14 +1583,15 @@ } }, "graceful-fs": { - "version": "4.1.11", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz", - "integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg=" + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.3.tgz", + "integrity": "sha512-a30VEBm4PEdx1dRB7MFK7BejejvCvBronbLjht+sHuGYj8PHs7M/5Z+rt5lw551vZ7yfTCj4Vuyy3mSJytDWRQ==", + "dev": true }, "grunt": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/grunt/-/grunt-1.0.3.tgz", - "integrity": "sha512-/JzmZNPfKorlCrrmxWqQO4JVodO+DVd5XX4DkocL/1WlLlKVLE9+SdEIempOAxDhWPysLle6afvn/hg7Ck2k9g==", + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/grunt/-/grunt-1.0.4.tgz", + "integrity": "sha512-PYsMOrOC+MsdGEkFVwMaMyc6Ob7pKmq+deg1Sjr+vvMWp35sztfwKE7qoN51V+UEtHsyNuMcGdgMLFkBHvMxHQ==", "dev": true, "requires": { "coffeescript": "~1.10.0", @@ -1261,7 +1605,7 @@ "grunt-legacy-log": "~2.0.0", "grunt-legacy-util": "~1.1.1", "iconv-lite": "~0.4.13", - "js-yaml": "~3.5.2", + "js-yaml": "~3.13.0", "minimatch": "~3.0.2", "mkdirp": "~0.5.1", "nopt": "~3.0.6", @@ -1293,16 +1637,16 @@ } }, "grunt-cli": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/grunt-cli/-/grunt-cli-1.3.1.tgz", - "integrity": "sha512-UwBRu/QpAjDc53DRLEkyilFdL0zenpxu+fddTIlsF/KJqdNcHaQmvyu1W3cDesZ9rqqZdKK5A8+QDIyLUEWoZQ==", + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/grunt-cli/-/grunt-cli-1.3.2.tgz", + "integrity": "sha512-8OHDiZZkcptxVXtMfDxJvmN7MVJNE8L/yIcPb4HB7TlyFD1kDvjHrb62uhySsU14wJx9ORMnTuhRMQ40lH/orQ==", "dev": true, "requires": { "grunt-known-options": "~1.1.0", "interpret": "~1.1.0", "liftoff": "~2.5.0", "nopt": "~4.0.1", - "v8flags": "~3.0.2" + "v8flags": "~3.1.1" } }, "grunt-contrib-concat": { @@ -1343,52 +1687,38 @@ } }, "grunt-contrib-jshint": { - "version": "1.1.0", - "resolved": "http://registry.npmjs.org/grunt-contrib-jshint/-/grunt-contrib-jshint-1.1.0.tgz", - "integrity": "sha1-Np2QmyWTxA6L55lAshNAhQx5Oaw=", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/grunt-contrib-jshint/-/grunt-contrib-jshint-2.1.0.tgz", + "integrity": "sha512-65S2/C/6RfjY/umTxfwXXn+wVvaYmykHkHSsW6Q6rhkbv3oudTEgqnFFZvWzWCoHUb+3GMZLbP3oSrNyvshmIQ==", "dev": true, "requires": { - "chalk": "^1.1.1", + "chalk": "^2.4.2", "hooker": "^0.2.3", - "jshint": "~2.9.4" + "jshint": "~2.10.2" }, "dependencies": { - "ansi-styles": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", - "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", - "dev": true - }, "chalk": { - "version": "1.1.3", - "resolved": "http://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", - "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", "dev": true, "requires": { - "ansi-styles": "^2.2.1", - "escape-string-regexp": "^1.0.2", - "has-ansi": "^2.0.0", - "strip-ansi": "^3.0.0", - "supports-color": "^2.0.0" + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" } - }, - "supports-color": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", - "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", - "dev": true } } }, "grunt-contrib-qunit": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/grunt-contrib-qunit/-/grunt-contrib-qunit-3.0.1.tgz", - "integrity": "sha512-s994+ipKwc+oUUIWaGIw1soyID4pExSGMd/cHQN5h0p8KbIjR1Le3ZC3giSDDKXtZFE0i+Obf0uIjNvjftX2Cw==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/grunt-contrib-qunit/-/grunt-contrib-qunit-3.1.0.tgz", + "integrity": "sha512-mdk8UltH6mxCD63E0hTXMAts42DOi4z4bBBrY7qnuHiShflMF7IueSMYe0zWaZ2dO8mgujh57Zfny2EbigJhRg==", "dev": true, "requires": { "eventemitter2": "^5.0.1", "p-each-series": "^1.0.0", - "puppeteer": "1.7.0" + "puppeteer": "^1.11.0" }, "dependencies": { "eventemitter2": { @@ -1400,62 +1730,25 @@ } }, "grunt-contrib-uglify": { - "version": "1.0.2", - "resolved": "http://registry.npmjs.org/grunt-contrib-uglify/-/grunt-contrib-uglify-1.0.2.tgz", - "integrity": "sha1-rmekb5FT7dTLEYE6Vetpxw19svs=", + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/grunt-contrib-uglify/-/grunt-contrib-uglify-4.0.1.tgz", + "integrity": "sha512-dwf8/+4uW1+7pH72WButOEnzErPGmtUvc8p08B0eQS/6ON0WdeQu0+WFeafaPTbbY1GqtS25lsHWaDeiTQNWPg==", "dev": true, "requires": { - "chalk": "^1.0.0", - "lodash": "^4.0.1", - "maxmin": "^1.1.0", - "uglify-js": "~2.6.2", + "chalk": "^2.4.1", + "maxmin": "^2.1.0", + "uglify-js": "^3.5.0", "uri-path": "^1.0.0" - }, - "dependencies": { - "ansi-styles": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", - "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", - "dev": true - }, - "chalk": { - "version": "1.1.3", - "resolved": "http://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", - "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", - "dev": true, - "requires": { - "ansi-styles": "^2.2.1", - "escape-string-regexp": "^1.0.2", - "has-ansi": "^2.0.0", - "strip-ansi": "^3.0.0", - "supports-color": "^2.0.0" - } - }, - "supports-color": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", - "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", - "dev": true - } } }, "grunt-jsdoc": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/grunt-jsdoc/-/grunt-jsdoc-2.3.0.tgz", - "integrity": "sha512-gC66TCRXeQMj3HIyqVSBJm8zdUz43e5vaG/PLO/627A1edbJnzxhJV7nF0KqLwMM0RDNu1istC6fvfnYqFKi3w==", + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/grunt-jsdoc/-/grunt-jsdoc-2.4.1.tgz", + "integrity": "sha512-S0zxU0wDewRu7z+vijEItOWe/UttxWVmvz0qz2ZVcAYR2GpXjsiski2CAVN0b18t2qeVLdmxZkJaEWCOsKzcAw==", "dev": true, "requires": { - "cross-spawn": "^6.0.5", - "jsdoc": "~3.5.5", - "marked": "^0.5.0" - }, - "dependencies": { - "marked": { - "version": "0.5.1", - "resolved": "https://registry.npmjs.org/marked/-/marked-0.5.1.tgz", - "integrity": "sha512-iUkBZegCZou4AdwbKTwSW/lNDcz5OuRSl3qdcl31Ia0B2QPG0Jn+tKblh/9/eP9/6+4h27vpoh8wel/vQOV0vw==", - "dev": true - } + "cross-spawn": "^7.0.1", + "jsdoc": "^3.6.3" } }, "grunt-known-options": { @@ -1502,41 +1795,30 @@ } }, "grunt-shell-spawn": { - "version": "0.3.10", - "resolved": "https://registry.npmjs.org/grunt-shell-spawn/-/grunt-shell-spawn-0.3.10.tgz", - "integrity": "sha1-gbuNRX7EfTGCqH1jCO+EXd+5SI8=", + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/grunt-shell-spawn/-/grunt-shell-spawn-0.4.0.tgz", + "integrity": "sha512-lfYvEQjbO1Wv+1Fk3d3XlcEpuQjyXiErZMkiz/i/tDQeMHHGF1LziqA4ZcietBAo/bM2RHdEEUJfnNWt1VRMwQ==", "dev": true, "requires": { - "grunt": ">=0.4.x", - "sync-exec": "~0.6.2" + "grunt": ">=0.4.x" } }, "gzip-size": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/gzip-size/-/gzip-size-1.0.0.tgz", - "integrity": "sha1-Zs+LEBBHInuVus5uodoMF37Vwi8=", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/gzip-size/-/gzip-size-3.0.0.tgz", + "integrity": "sha1-VGGI6b3DN/Zzdy+BZgRks4nc5SA=", "dev": true, "requires": { - "browserify-zlib": "^0.1.4", - "concat-stream": "^1.4.1" + "duplexer": "^0.1.1" } }, - "har-schema": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", - "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=", - "dev": true, - "optional": true - }, - "har-validator": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.0.tgz", - "integrity": "sha512-+qnmNjI4OfH2ipQ9VQOw23bBd/ibtfbVdK2fYbY4acTDqKTW/YDp9McimZdDbG8iV9fZizUqQMD5xvriB146TA==", + "has": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", "dev": true, - "optional": true, "requires": { - "ajv": "^5.3.0", - "har-schema": "^2.0.0" + "function-bind": "^1.1.1" } }, "has-ansi": { @@ -1586,15 +1868,35 @@ } } }, - "hasha": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/hasha/-/hasha-2.2.0.tgz", - "integrity": "sha1-eNfL/B5tZjA/55g3NlmEUXsvbuE=", + "hash-base": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-3.0.4.tgz", + "integrity": "sha1-X8hoaEfs1zSZQDMZprCj8/auSRg=", "dev": true, - "optional": true, "requires": { - "is-stream": "^1.0.1", - "pinkie-promise": "^2.0.0" + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "hash.js": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.7.tgz", + "integrity": "sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==", + "dev": true, + "requires": { + "inherits": "^2.0.3", + "minimalistic-assert": "^1.0.1" + } + }, + "hmac-drbg": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz", + "integrity": "sha1-0nRXAQJabHdabFRXk+1QL8DGSaE=", + "dev": true, + "requires": { + "hash.js": "^1.0.3", + "minimalistic-assert": "^1.0.0", + "minimalistic-crypto-utils": "^1.0.1" } }, "homedir-polyfill": { @@ -1613,9 +1915,15 @@ "dev": true }, "hosted-git-info": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.7.1.tgz", - "integrity": "sha512-7T/BxH19zbcCTa8XkMlbK5lTo1WtgkFi3GvdWEyNuc4Vex7/9Dqbnpsf4JMydcfj9HCg4zUWFTL3Za6lapg5/w==", + "version": "2.8.5", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.5.tgz", + "integrity": "sha512-kssjab8CvdXfcXMXVcvsXum4Hwdq9XGtRD3TteMEvEbq0LXyiNQr6AprqKqfeaDXze7SxWvRxdpwE6ku7ikLkg==", + "dev": true + }, + "htmlescape": { + "version": "1.1.1", + "resolved": "http://registry.npmjs.org/htmlescape/-/htmlescape-1.1.1.tgz", + "integrity": "sha1-OgPtwiFLyjtmQko+eVk0lQnLA1E=", "dev": true }, "htmlparser2": { @@ -1631,17 +1939,11 @@ "readable-stream": "1.1" } }, - "http-signature": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", - "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=", - "dev": true, - "optional": true, - "requires": { - "assert-plus": "^1.0.0", - "jsprim": "^1.2.2", - "sshpk": "^1.7.0" - } + "https-browserify": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/https-browserify/-/https-browserify-1.0.0.tgz", + "integrity": "sha1-7AbBDgo0wPL68Zn3/X/Hj//QPHM=", + "dev": true }, "https-proxy-agent": { "version": "2.2.1", @@ -1679,6 +1981,12 @@ "safer-buffer": ">= 2.1.2 < 3" } }, + "ieee754": { + "version": "1.1.13", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.13.tgz", + "integrity": "sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg==", + "dev": true + }, "indent-string": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-2.1.0.tgz", @@ -1710,6 +2018,33 @@ "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==", "dev": true }, + "inline-source-map": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/inline-source-map/-/inline-source-map-0.6.2.tgz", + "integrity": "sha1-+Tk0ccGKedFyT4Y/o4tYY3Ct4qU=", + "dev": true, + "requires": { + "source-map": "~0.5.3" + } + }, + "insert-module-globals": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/insert-module-globals/-/insert-module-globals-7.2.0.tgz", + "integrity": "sha512-VE6NlW+WGn2/AeOMd496AHFYmE7eLKkUY6Ty31k4og5vmA3Fjuwe9v6ifH6Xx/Hz27QvdoMoviw1/pqWRB09Sw==", + "dev": true, + "requires": { + "JSONStream": "^1.0.3", + "acorn-node": "^1.5.2", + "combine-source-map": "^0.8.0", + "concat-stream": "^1.6.1", + "is-buffer": "^1.1.0", + "path-is-absolute": "^1.0.1", + "process": "~0.11.0", + "through2": "^2.0.0", + "undeclared-identifiers": "^1.1.2", + "xtend": "^4.0.0" + } + }, "interpret": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/interpret/-/interpret-1.1.0.tgz", @@ -1758,15 +2093,6 @@ "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", "dev": true }, - "is-builtin-module": { - "version": "1.0.0", - "resolved": "http://registry.npmjs.org/is-builtin-module/-/is-builtin-module-1.0.0.tgz", - "integrity": "sha1-VAVy0096wxGfj3bDDLwbHgN6/74=", - "dev": true, - "requires": { - "builtin-modules": "^1.0.0" - } - }, "is-data-descriptor": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", @@ -1819,13 +2145,10 @@ "dev": true }, "is-finite": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-finite/-/is-finite-1.0.2.tgz", - "integrity": "sha1-zGZ3aVYCvlUO8R6LSqYwU0K20Ko=", - "dev": true, - "requires": { - "number-is-nan": "^1.0.0" - } + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-finite/-/is-finite-1.1.0.tgz", + "integrity": "sha512-cdyMtqX/BOqqNBBiKlIVkytNHm49MtMlYyn1zxzvJKWmFMlGzm+ry5BBfYyeY9YmNKbRSo/o7OX9w9ale0wg3w==", + "dev": true }, "is-glob": { "version": "3.1.0", @@ -1874,20 +2197,6 @@ "is-unc-path": "^1.0.0" } }, - "is-stream": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", - "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=", - "dev": true, - "optional": true - }, - "is-typedarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", - "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=", - "dev": true, - "optional": true - }, "is-unc-path": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-unc-path/-/is-unc-path-1.0.0.tgz", @@ -1927,72 +2236,69 @@ "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", "dev": true }, - "isstream": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", - "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=", - "dev": true - }, "js-yaml": { - "version": "3.5.5", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.5.5.tgz", - "integrity": "sha1-A3fDgBfKvHMisNH7zSWkkWQfL74=", + "version": "3.13.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.13.1.tgz", + "integrity": "sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw==", "dev": true, "requires": { - "argparse": "^1.0.2", - "esprima": "^2.6.0" + "argparse": "^1.0.7", + "esprima": "^4.0.0" } }, "js2xmlparser": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/js2xmlparser/-/js2xmlparser-3.0.0.tgz", - "integrity": "sha1-P7YOqgicVED5MZ9RdgzNB+JJlzM=", + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/js2xmlparser/-/js2xmlparser-4.0.1.tgz", + "integrity": "sha512-KrPTolcw6RocpYjdC7pL7v62e55q7qOMHvLX1UCLc5AAS8qeJ6nukarEJAF2KL2PZxlbGueEbINqZR2bDe/gUw==", + "dev": true, "requires": { - "xmlcreate": "^1.0.1" + "xmlcreate": "^2.0.3" } }, - "jsbn": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", - "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=", - "dev": true - }, "jsdoc": { - "version": "3.5.5", - "resolved": "https://registry.npmjs.org/jsdoc/-/jsdoc-3.5.5.tgz", - "integrity": "sha512-6PxB65TAU4WO0Wzyr/4/YhlGovXl0EVYfpKbpSroSj0qBxT4/xod/l40Opkm38dRHRdQgdeY836M0uVnJQG7kg==", - "requires": { - "babylon": "7.0.0-beta.19", - "bluebird": "~3.5.0", - "catharsis": "~0.8.9", - "escape-string-regexp": "~1.0.5", - "js2xmlparser": "~3.0.0", - "klaw": "~2.0.0", - "marked": "~0.3.6", - "mkdirp": "~0.5.1", - "requizzle": "~0.2.1", - "strip-json-comments": "~2.0.1", + "version": "3.6.3", + "resolved": "https://registry.npmjs.org/jsdoc/-/jsdoc-3.6.3.tgz", + "integrity": "sha512-Yf1ZKA3r9nvtMWHO1kEuMZTlHOF8uoQ0vyo5eH7SQy5YeIiHM+B0DgKnn+X6y6KDYZcF7G2SPkKF+JORCXWE/A==", + "dev": true, + "requires": { + "@babel/parser": "^7.4.4", + "bluebird": "^3.5.4", + "catharsis": "^0.8.11", + "escape-string-regexp": "^2.0.0", + "js2xmlparser": "^4.0.0", + "klaw": "^3.0.0", + "markdown-it": "^8.4.2", + "markdown-it-anchor": "^5.0.2", + "marked": "^0.7.0", + "mkdirp": "^0.5.1", + "requizzle": "^0.2.3", + "strip-json-comments": "^3.0.1", "taffydb": "2.6.2", - "underscore": "~1.8.3" + "underscore": "~1.9.1" + }, + "dependencies": { + "escape-string-regexp": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", + "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", + "dev": true + } } }, "jshint": { - "version": "2.9.6", - "resolved": "https://registry.npmjs.org/jshint/-/jshint-2.9.6.tgz", - "integrity": "sha512-KO9SIAKTlJQOM4lE64GQUtGBRpTOuvbrRrSZw3AhUxMNG266nX9hK2cKA4SBhXOj0irJGyNyGSLT62HGOVDEOA==", + "version": "2.10.2", + "resolved": "https://registry.npmjs.org/jshint/-/jshint-2.10.2.tgz", + "integrity": "sha512-e7KZgCSXMJxznE/4WULzybCMNXNAd/bf5TSrvVEq78Q/K8ZwFpmBqQeDtNiHc3l49nV4E/+YeHU/JZjSUIrLAA==", "dev": true, "requires": { "cli": "~1.0.0", "console-browserify": "1.1.x", "exit": "0.1.x", "htmlparser2": "3.8.x", - "lodash": "~4.17.10", + "lodash": "~4.17.11", "minimatch": "~3.0.2", - "phantom": "~4.0.1", - "phantomjs-prebuilt": "~2.1.7", "shelljs": "0.3.x", - "strip-json-comments": "1.0.x", - "unicode-5.2.0": "^0.7.5" + "strip-json-comments": "1.0.x" }, "dependencies": { "strip-json-comments": { @@ -2004,21 +2310,21 @@ } }, "jslint": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/jslint/-/jslint-0.12.0.tgz", - "integrity": "sha512-RoCsyICcKA+6TFsbys9DpKTfPVaC71Mm5QSjvrWA0lDVN+LIvx6apa42FFisMqmCTvJ8DxkcoQGJ0j7m3kTVow==", + "version": "0.12.1", + "resolved": "https://registry.npmjs.org/jslint/-/jslint-0.12.1.tgz", + "integrity": "sha512-q5iHswjOmJffbsGVq/1umGh4YBxb5pCArNHCZeHpkuVDDKM6IldqUn4hLehKSwQr7Bn07VXjD34Lx3nw+6j8eA==", "dev": true, "requires": { "exit": "~0.1.2", - "glob": "~7.1.2", - "nopt": "~3.0.1", + "glob": "~7.1.3", + "nopt": "~4.0.1", "readable-stream": "~2.1.5" }, "dependencies": { "glob": { - "version": "7.1.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.3.tgz", - "integrity": "sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ==", + "version": "7.1.6", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", + "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", "dev": true, "requires": { "fs.realpath": "^1.0.0", @@ -2029,15 +2335,6 @@ "path-is-absolute": "^1.0.0" } }, - "nopt": { - "version": "3.0.6", - "resolved": "https://registry.npmjs.org/nopt/-/nopt-3.0.6.tgz", - "integrity": "sha1-xkZdvwirzU2zWTF/eaxopkayj/k=", - "dev": true, - "requires": { - "abbrev": "1" - } - }, "process-nextick-args": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.7.tgz", @@ -2061,56 +2358,35 @@ } } }, - "json-schema": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz", - "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=", - "dev": true, - "optional": true - }, - "json-schema-traverse": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.3.1.tgz", - "integrity": "sha1-NJptRMU6Ud6JtAgFxdXlm0F9M0A=", - "dev": true, - "optional": true - }, - "json-stringify-safe": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", - "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=", - "dev": true, - "optional": true - }, - "jsonfile": { - "version": "2.4.0", - "resolved": "http://registry.npmjs.org/jsonfile/-/jsonfile-2.4.0.tgz", - "integrity": "sha1-NzaitCi4e72gzIO1P6PWM6NcKug=", + "json-int64": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json-int64/-/json-int64-1.0.2.tgz", + "integrity": "sha512-uGrIXtRehbksM17S2lJRLPljufK52KL2ewbJi0xgcRPONoRLXa4yAUIKAUxF69dbnqIoBu33fB28MAWSxupB8Q==", "dev": true, - "optional": true, "requires": { - "graceful-fs": "^4.1.6" + "node-int64": "0.4.0" } }, - "jsprim": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz", - "integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=", + "json-stable-stringify": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify/-/json-stable-stringify-0.0.1.tgz", + "integrity": "sha1-YRwj6BTbN1Un34URk9tZ3Sryf0U=", "dev": true, - "optional": true, "requires": { - "assert-plus": "1.0.0", - "extsprintf": "1.3.0", - "json-schema": "0.2.3", - "verror": "1.10.0" + "jsonify": "~0.0.0" } }, - "kew": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/kew/-/kew-0.7.0.tgz", - "integrity": "sha1-edk9LTM2PW/dKXCzNdkUGtWR15s=", - "dev": true, - "optional": true + "jsonify": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/jsonify/-/jsonify-0.0.0.tgz", + "integrity": "sha1-LHS27kHZPKUbe1qu6PUDYx0lKnM=", + "dev": true + }, + "jsonparse": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/jsonparse/-/jsonparse-1.3.1.tgz", + "integrity": "sha1-P02uSpH6wxX3EGL4UhzCOfE2YoA=", + "dev": true }, "kind-of": { "version": "6.0.2", @@ -2119,18 +2395,23 @@ "dev": true }, "klaw": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/klaw/-/klaw-2.0.0.tgz", - "integrity": "sha1-WcEo4Nxc5BAgEVEZTuucv4WGUPY=", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/klaw/-/klaw-3.0.0.tgz", + "integrity": "sha512-0Fo5oir+O9jnXu5EefYbVK+mHMBeEVEy2cmctR1O1NECcCkPRreJKrS6Qt/j3KC2C148Dfo9i3pCmCMsdqGr0g==", + "dev": true, "requires": { "graceful-fs": "^4.1.9" } }, - "lazy-cache": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/lazy-cache/-/lazy-cache-1.0.4.tgz", - "integrity": "sha1-odePw6UEdMuAhF07O24dpJpEbo4=", - "dev": true + "labeled-stream-splicer": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/labeled-stream-splicer/-/labeled-stream-splicer-2.0.2.tgz", + "integrity": "sha512-Ca4LSXFFZUjPScRaqOcFxneA0VpKZr4MMYCljyQr4LIewTLb3Y0IUTIsnBBsVubIeEfxeSZpSjSsRM8APEQaAw==", + "dev": true, + "requires": { + "inherits": "^2.0.1", + "stream-splicer": "^2.0.0" + } }, "liftoff": { "version": "2.5.0", @@ -2162,7 +2443,16 @@ } } }, - "load-json-file": { + "linkify-it": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-2.2.0.tgz", + "integrity": "sha512-GnAl/knGn+i1U/wjBz3akz2stz+HrHLsxMwHQGofCDfPvlf+gDKN58UtfmUquTY4/MXeE2x7k19KQmeoZi94Iw==", + "dev": true, + "requires": { + "uc.micro": "^1.0.1" + } + }, + "load-json-file": { "version": "1.1.0", "resolved": "http://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz", "integrity": "sha1-lWkFcI1YtLq0wiYbBPWfMcmTdMA=", @@ -2181,10 +2471,10 @@ "integrity": "sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg==", "dev": true }, - "longest": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/longest/-/longest-1.0.1.tgz", - "integrity": "sha1-MKCy2jj3N3DoKUoNIuZiXtd9AJc=", + "lodash.memoize": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-3.0.4.tgz", + "integrity": "sha1-LcvSwofLwKVcxCMovQxzYVDVPj8=", "dev": true }, "loud-rejection": { @@ -2227,21 +2517,49 @@ "object-visit": "^1.0.0" } }, + "markdown-it": { + "version": "8.4.2", + "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-8.4.2.tgz", + "integrity": "sha512-GcRz3AWTqSUphY3vsUqQSFMbgR38a4Lh3GWlHRh/7MRwz8mcu9n2IO7HOh+bXHrR9kOPDl5RNCaEsrneb+xhHQ==", + "dev": true, + "requires": { + "argparse": "^1.0.7", + "entities": "~1.1.1", + "linkify-it": "^2.0.0", + "mdurl": "^1.0.1", + "uc.micro": "^1.0.5" + }, + "dependencies": { + "entities": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/entities/-/entities-1.1.2.tgz", + "integrity": "sha512-f2LZMYl1Fzu7YSBKg+RoROelpOaNrcGmE9AZubeDfrCEia483oW4MI4VyFd5VNHIgQ/7qm1I0wUHK1eJnn2y2w==", + "dev": true + } + } + }, + "markdown-it-anchor": { + "version": "5.2.5", + "resolved": "https://registry.npmjs.org/markdown-it-anchor/-/markdown-it-anchor-5.2.5.tgz", + "integrity": "sha512-xLIjLQmtym3QpoY9llBgApknl7pxAcN3WDRc2d3rwpl+/YvDZHPmKscGs+L6E05xf2KrCXPBvosWt7MZukwSpQ==", + "dev": true + }, "marked": { - "version": "0.3.19", - "resolved": "http://registry.npmjs.org/marked/-/marked-0.3.19.tgz", - "integrity": "sha512-ea2eGWOqNxPcXv8dyERdSr/6FmzvWwzjMxpfGB/sbMccXoct+xY+YukPD+QTUZwyvK7BZwcr4m21WBOW41pAkg==" + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/marked/-/marked-0.7.0.tgz", + "integrity": "sha512-c+yYdCZJQrsRjTPhUx7VKkApw9bwDkNbHUKo1ovgcfDjb2kc8rLuRbIFyXL5WOEUwzSSKo3IXpph2K6DqB/KZg==", + "dev": true }, "maxmin": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/maxmin/-/maxmin-1.1.0.tgz", - "integrity": "sha1-cTZehKmd2Piz99X94vANHn9zvmE=", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/maxmin/-/maxmin-2.1.0.tgz", + "integrity": "sha1-TTsiCQPZXu5+t6x/qGTnLcCaMWY=", "dev": true, "requires": { "chalk": "^1.0.0", "figures": "^1.0.1", - "gzip-size": "^1.0.0", - "pretty-bytes": "^1.0.0" + "gzip-size": "^3.0.0", + "pretty-bytes": "^3.0.0" }, "dependencies": { "ansi-styles": { @@ -2252,7 +2570,7 @@ }, "chalk": { "version": "1.1.3", - "resolved": "http://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", "dev": true, "requires": { @@ -2271,6 +2589,23 @@ } } }, + "md5.js": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz", + "integrity": "sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg==", + "dev": true, + "requires": { + "hash-base": "^3.0.0", + "inherits": "^2.0.1", + "safe-buffer": "^5.1.2" + } + }, + "mdurl": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/mdurl/-/mdurl-1.0.1.tgz", + "integrity": "sha1-/oWy7HWlkDfyrf7BAP1sYBdhFS4=", + "dev": true + }, "meow": { "version": "3.7.0", "resolved": "http://registry.npmjs.org/meow/-/meow-3.7.0.tgz", @@ -2318,26 +2653,33 @@ "to-regex": "^3.0.2" } }, + "miller-rabin": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/miller-rabin/-/miller-rabin-4.0.1.tgz", + "integrity": "sha512-115fLhvZVqWwHPbClyntxEVfVDfl9DLLTuJvq3g2O/Oxi8AiNouAHvDSzHS0viUJc+V5vm3eq91Xwqn9dp4jRA==", + "dev": true, + "requires": { + "bn.js": "^4.0.0", + "brorand": "^1.0.1" + } + }, "mime": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/mime/-/mime-2.3.1.tgz", - "integrity": "sha512-OEUllcVoydBHGN1z84yfQDimn58pZNNNXgZlHXSboxMlFvgI6MXSWpWKpFRra7H1HxpVhHTkrghfRW49k6yjeg==", + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-2.4.0.tgz", + "integrity": "sha512-ikBcWwyqXQSHKtciCcctu9YfPbFYZ4+gbHEmE0Q8jzcTYQg5dHCr3g2wwAZjPoJfQVXZq6KXAjpXOTf5/cjT7w==", "dev": true }, - "mime-db": { - "version": "1.37.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.37.0.tgz", - "integrity": "sha512-R3C4db6bgQhlIhPU48fUtdVmKnflq+hRdad7IyKhtFj06VPNVdk2RhiYL3UjQIlso8L+YxAtFkobT0VK+S/ybg==", + "minimalistic-assert": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", + "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==", "dev": true }, - "mime-types": { - "version": "2.1.21", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.21.tgz", - "integrity": "sha512-3iL6DbwpyLzjR3xHSFNFeb9Nz/M8WDkX33t1GFQnFOllWk8pOrh/LSrB5OXlnlW5P9LH73X6loW/eogc+F5lJg==", - "dev": true, - "requires": { - "mime-db": "~1.37.0" - } + "minimalistic-crypto-utils": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz", + "integrity": "sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo=", + "dev": true }, "minimatch": { "version": "3.0.4", @@ -2351,7 +2693,8 @@ "minimist": { "version": "0.0.8", "resolved": "http://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", - "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=" + "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=", + "dev": true }, "mixin-deep": { "version": "1.3.1", @@ -2378,10 +2721,69 @@ "version": "0.5.1", "resolved": "http://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", + "dev": true, "requires": { "minimist": "0.0.8" } }, + "module-deps": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/module-deps/-/module-deps-6.2.2.tgz", + "integrity": "sha512-a9y6yDv5u5I4A+IPHTnqFxcaKr4p50/zxTjcQJaX2ws9tN/W6J6YXnEKhqRyPhl494dkcxx951onSKVezmI+3w==", + "dev": true, + "requires": { + "JSONStream": "^1.0.3", + "browser-resolve": "^1.7.0", + "cached-path-relative": "^1.0.2", + "concat-stream": "~1.6.0", + "defined": "^1.0.0", + "detective": "^5.2.0", + "duplexer2": "^0.1.2", + "inherits": "^2.0.1", + "parents": "^1.0.0", + "readable-stream": "^2.0.2", + "resolve": "^1.4.0", + "stream-combiner2": "^1.1.1", + "subarg": "^1.0.0", + "through2": "^2.0.0", + "xtend": "^4.0.0" + }, + "dependencies": { + "readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "dev": true, + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "resolve": { + "version": "1.15.1", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.15.1.tgz", + "integrity": "sha512-84oo6ZTtoTUpjgNEr5SJyzQhzL72gaRodsSfyxC/AXRvwu0Yse9H8eF9IpGo7b8YetZhlI6v7ZQ6bKBFV/6S7w==", + "dev": true, + "requires": { + "path-parse": "^1.0.6" + } + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "http://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "requires": { + "safe-buffer": "~5.1.0" + } + } + } + }, "ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", @@ -2407,31 +2809,43 @@ "to-regex": "^3.0.1" } }, - "nice-try": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", - "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==", + "node-int64": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", + "integrity": "sha1-h6kGXNs1XTGC2PlM4RGIuCXGijs=", "dev": true }, "nopt": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/nopt/-/nopt-4.0.1.tgz", "integrity": "sha1-0NRoWv1UFRk8jHUFYC0NF81kR00=", + "dev": true, "requires": { "abbrev": "1", "osenv": "^0.1.4" } }, "normalize-package-data": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.4.0.tgz", - "integrity": "sha512-9jjUFbTPfEy3R/ad/2oNbKtW9Hgovl5O1FvFWKkKblNXoN/Oou6+9+KKohPK13Yc3/TyunyWhJp6gvRNR/PPAw==", + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", + "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", "dev": true, "requires": { "hosted-git-info": "^2.1.4", - "is-builtin-module": "^1.0.0", + "resolve": "^1.10.0", "semver": "2 || 3 || 4 || 5", "validate-npm-package-license": "^3.0.1" + }, + "dependencies": { + "resolve": { + "version": "1.15.1", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.15.1.tgz", + "integrity": "sha512-84oo6ZTtoTUpjgNEr5SJyzQhzL72gaRodsSfyxC/AXRvwu0Yse9H8eF9IpGo7b8YetZhlI6v7ZQ6bKBFV/6S7w==", + "dev": true, + "requires": { + "path-parse": "^1.0.6" + } + } } }, "number-is-nan": { @@ -2440,13 +2854,6 @@ "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=", "dev": true }, - "oauth-sign": { - "version": "0.9.0", - "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", - "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==", - "dev": true, - "optional": true - }, "object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", @@ -2533,20 +2940,29 @@ "wrappy": "1" } }, + "os-browserify": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/os-browserify/-/os-browserify-0.3.0.tgz", + "integrity": "sha1-hUNzx/XCMVkU/Jv8a9gjj92h7Cc=", + "dev": true + }, "os-homedir": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz", - "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=" + "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=", + "dev": true }, "os-tmpdir": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", - "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=" + "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=", + "dev": true }, "osenv": { "version": "0.1.5", "resolved": "https://registry.npmjs.org/osenv/-/osenv-0.1.5.tgz", "integrity": "sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g==", + "dev": true, "requires": { "os-homedir": "^1.0.0", "os-tmpdir": "^1.0.0" @@ -2568,11 +2984,34 @@ "dev": true }, "pako": { - "version": "0.2.9", - "resolved": "https://registry.npmjs.org/pako/-/pako-0.2.9.tgz", - "integrity": "sha1-8/dSL073gjSNqBYbrZ7P1Rv4OnU=", + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", + "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==", "dev": true }, + "parents": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parents/-/parents-1.0.1.tgz", + "integrity": "sha1-/t1NK/GTp3dF/nHjcdc8MwfZx1E=", + "dev": true, + "requires": { + "path-platform": "~0.11.15" + } + }, + "parse-asn1": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/parse-asn1/-/parse-asn1-5.1.5.tgz", + "integrity": "sha512-jkMYn1dcJqF6d5CpU689bq7w/b5ALS9ROVSpQDPrZsqqesUJii9qutvoT5ltGedNXMO2e16YUWIghG9KxaViTQ==", + "dev": true, + "requires": { + "asn1.js": "^4.0.0", + "browserify-aes": "^1.0.0", + "create-hash": "^1.1.0", + "evp_bytestokey": "^1.0.0", + "pbkdf2": "^3.0.3", + "safe-buffer": "^5.1.1" + } + }, "parse-filepath": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/parse-filepath/-/parse-filepath-1.0.2.tgz", @@ -2605,6 +3044,12 @@ "integrity": "sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ=", "dev": true }, + "path-browserify": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-0.0.1.tgz", + "integrity": "sha512-BapA40NHICOS+USX9SN4tyhq+A2RrN/Ws5F0Z5aMHDp98Fl86lX8Oti8B7uN93L4Ifv4fHOEA+pQw87gmMO/lQ==", + "dev": true + }, "path-exists": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-2.1.0.tgz", @@ -2621,9 +3066,21 @@ "dev": true }, "path-key": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", - "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true + }, + "path-parse": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz", + "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==", + "dev": true + }, + "path-platform": { + "version": "0.11.15", + "resolved": "https://registry.npmjs.org/path-platform/-/path-platform-0.11.15.tgz", + "integrity": "sha1-6GQhf3TDaFDwhSt43Hv31KVyG/I=", "dev": true }, "path-root": { @@ -2652,49 +3109,25 @@ "pinkie-promise": "^2.0.0" } }, + "pbkdf2": { + "version": "3.0.17", + "resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.0.17.tgz", + "integrity": "sha512-U/il5MsrZp7mGg3mSQfn742na2T+1/vHDCG5/iTI3X9MKUuYUZVLQhyRsg06mCgDBTd57TxzgZt7P+fYfjRLtA==", + "dev": true, + "requires": { + "create-hash": "^1.1.2", + "create-hmac": "^1.1.4", + "ripemd160": "^2.0.1", + "safe-buffer": "^5.0.1", + "sha.js": "^2.4.8" + } + }, "pend": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", "integrity": "sha1-elfrVQpng/kRUzH89GY9XI4AelA=", "dev": true }, - "performance-now": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", - "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=", - "dev": true, - "optional": true - }, - "phantom": { - "version": "4.0.12", - "resolved": "https://registry.npmjs.org/phantom/-/phantom-4.0.12.tgz", - "integrity": "sha512-Tz82XhtPmwCk1FFPmecy7yRGZG2btpzY2KI9fcoPT7zT9det0CcMyfBFPp1S8DqzsnQnm8ZYEfdy528mwVtksA==", - "dev": true, - "optional": true, - "requires": { - "phantomjs-prebuilt": "^2.1.16", - "split": "^1.0.1", - "winston": "^2.4.0" - } - }, - "phantomjs-prebuilt": { - "version": "2.1.16", - "resolved": "https://registry.npmjs.org/phantomjs-prebuilt/-/phantomjs-prebuilt-2.1.16.tgz", - "integrity": "sha1-79ISpKOWbTZHaE6ouniFSb4q7+8=", - "dev": true, - "optional": true, - "requires": { - "es6-promise": "^4.0.3", - "extract-zip": "^1.6.5", - "fs-extra": "^1.0.0", - "hasha": "^2.2.0", - "kew": "^0.7.0", - "progress": "^1.1.8", - "request": "^2.81.0", - "request-progress": "^2.0.1", - "which": "^1.2.10" - } - }, "pify": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", @@ -2723,68 +3156,72 @@ "dev": true }, "pretty-bytes": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/pretty-bytes/-/pretty-bytes-1.0.4.tgz", - "integrity": "sha1-CiLoIQYJrTVUL4yNXSFZr/B1HIQ=", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/pretty-bytes/-/pretty-bytes-3.0.1.tgz", + "integrity": "sha1-J9AAjXeAY6C0gRuzXHnxvV1fvM8=", "dev": true, "requires": { - "get-stdin": "^4.0.1", - "meow": "^3.1.0" + "number-is-nan": "^1.0.0" } }, + "process": { + "version": "0.11.10", + "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", + "integrity": "sha1-czIwDoQBYb2j5podHZGn1LwW8YI=", + "dev": true + }, "process-nextick-args": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz", "integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw==", "dev": true }, - "progress": { - "version": "1.1.8", - "resolved": "http://registry.npmjs.org/progress/-/progress-1.1.8.tgz", - "integrity": "sha1-4mDHj2Fhzdmw5WzD4Khd4Xx6V74=", - "dev": true, - "optional": true - }, "proxy-from-env": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.0.0.tgz", "integrity": "sha1-M8UDmPcOp+uW0h97gXYwpVeRx+4=", "dev": true }, - "psl": { - "version": "1.1.29", - "resolved": "https://registry.npmjs.org/psl/-/psl-1.1.29.tgz", - "integrity": "sha512-AeUmQ0oLN02flVHXWh9sSJF7mcdFq0ppid/JkErufc3hGIV/AMa8Fo9VgDo/cT2jFdOWoFvHp90qqBH54W+gjQ==", + "public-encrypt": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/public-encrypt/-/public-encrypt-4.0.3.tgz", + "integrity": "sha512-zVpa8oKZSz5bTMTFClc1fQOnyyEzpl5ozpi1B5YcvBrdohMjH2rfsBtyXcuNuwjsDIXmBYlF2N5FlJYhR29t8Q==", "dev": true, - "optional": true + "requires": { + "bn.js": "^4.1.0", + "browserify-rsa": "^4.0.0", + "create-hash": "^1.1.0", + "parse-asn1": "^5.0.0", + "randombytes": "^2.0.1", + "safe-buffer": "^5.1.2" + } }, "punycode": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=", - "dev": true, - "optional": true + "dev": true }, "puppeteer": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/puppeteer/-/puppeteer-1.7.0.tgz", - "integrity": "sha512-f+1DxKHPqce6CXUBz2eVO2WcATeVeQSOPG9GYaGObEZDCiCEUwG+gogjMsrvn7he2wHTqNVb5p6RUrwmr8XFBA==", + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/puppeteer/-/puppeteer-1.11.0.tgz", + "integrity": "sha512-iG4iMOHixc2EpzqRV+pv7o3GgmU2dNYEMkvKwSaQO/vMZURakwSOn/EYJ6OIRFYOque1qorzIBvrytPIQB3YzQ==", "dev": true, "requires": { - "debug": "^3.1.0", + "debug": "^4.1.0", "extract-zip": "^1.6.6", "https-proxy-agent": "^2.2.1", "mime": "^2.0.3", - "progress": "^2.0.0", + "progress": "^2.0.1", "proxy-from-env": "^1.0.0", "rimraf": "^2.6.1", - "ws": "^5.1.1" + "ws": "^6.1.0" }, "dependencies": { "debug": { - "version": "3.2.6", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", - "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", + "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", "dev": true, "requires": { "ms": "^2.1.1" @@ -2797,19 +3234,78 @@ "dev": true }, "progress": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.1.tgz", - "integrity": "sha512-OE+a6vzqazc+K6LxJrX5UPyKFvGnL5CYmq2jFGNIBWHpc4QyE49/YOumcrpQFJpfejmvRtbJzgO1zPmMCqlbBg==", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", + "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", "dev": true } } }, - "qs": { - "version": "6.5.2", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", - "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==", + "querystring": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz", + "integrity": "sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA=", + "dev": true + }, + "querystring-es3": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/querystring-es3/-/querystring-es3-0.2.1.tgz", + "integrity": "sha1-nsYfeQSYdXB9aUFFlv2Qek1xHnM=", + "dev": true + }, + "randombytes": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", + "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", "dev": true, - "optional": true + "requires": { + "safe-buffer": "^5.1.0" + } + }, + "randomfill": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/randomfill/-/randomfill-1.0.4.tgz", + "integrity": "sha512-87lcbR8+MhcWcUiQ+9e+Rwx8MyR2P7qnt15ynUlbm3TU/fjbgz4GsvfSUDTemtCCtVCqb4ZcEFlyPNTh9bBTLw==", + "dev": true, + "requires": { + "randombytes": "^2.0.5", + "safe-buffer": "^5.1.0" + } + }, + "read-only-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/read-only-stream/-/read-only-stream-2.0.0.tgz", + "integrity": "sha1-JyT9aoET1zdkrCiNQ4YnDB2/F/A=", + "dev": true, + "requires": { + "readable-stream": "^2.0.2" + }, + "dependencies": { + "readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "dev": true, + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "http://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "requires": { + "safe-buffer": "~5.1.0" + } + } + } }, "read-pkg": { "version": "1.1.0", @@ -2834,7 +3330,7 @@ }, "readable-stream": { "version": "1.1.14", - "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz", "integrity": "sha1-fPTFTvZI44EwhMY23SB54WbAgdk=", "dev": true, "requires": { @@ -2902,57 +3398,20 @@ "is-finite": "^1.0.0" } }, - "request": { - "version": "2.88.0", - "resolved": "https://registry.npmjs.org/request/-/request-2.88.0.tgz", - "integrity": "sha512-NAqBSrijGLZdM0WZNsInLJpkJokL72XYjUpnB0iwsRgxh7dB6COrHnTBNwN0E+lHDAJzu7kLAkDeY08z2/A0hg==", - "dev": true, - "optional": true, - "requires": { - "aws-sign2": "~0.7.0", - "aws4": "^1.8.0", - "caseless": "~0.12.0", - "combined-stream": "~1.0.6", - "extend": "~3.0.2", - "forever-agent": "~0.6.1", - "form-data": "~2.3.2", - "har-validator": "~5.1.0", - "http-signature": "~1.2.0", - "is-typedarray": "~1.0.0", - "isstream": "~0.1.2", - "json-stringify-safe": "~5.0.1", - "mime-types": "~2.1.19", - "oauth-sign": "~0.9.0", - "performance-now": "^2.1.0", - "qs": "~6.5.2", - "safe-buffer": "^5.1.2", - "tough-cookie": "~2.4.3", - "tunnel-agent": "^0.6.0", - "uuid": "^3.3.2" - } - }, - "request-progress": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/request-progress/-/request-progress-2.0.1.tgz", - "integrity": "sha1-XTa7V5YcZzqlt4jbyBQf3yO0Tgg=", - "dev": true, - "optional": true, - "requires": { - "throttleit": "^1.0.0" - } - }, "requizzle": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/requizzle/-/requizzle-0.2.1.tgz", - "integrity": "sha1-aUPDUwxNmn5G8c3dUcFY/GcM294=", + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/requizzle/-/requizzle-0.2.3.tgz", + "integrity": "sha512-YanoyJjykPxGHii0fZP0uUPEXpvqfBDxWV7s6GKAiiOsiqhX6vHNyW3Qzdmqp/iq/ExbhaGbVrjB4ruEVSM4GQ==", + "dev": true, "requires": { - "underscore": "~1.6.0" + "lodash": "^4.17.14" }, "dependencies": { - "underscore": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.6.0.tgz", - "integrity": "sha1-izixDKze9jM3uLJOT/htRa6lKag=" + "lodash": { + "version": "4.17.15", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz", + "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==", + "dev": true } } }, @@ -2984,15 +3443,6 @@ "integrity": "sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==", "dev": true }, - "right-align": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/right-align/-/right-align-0.1.3.tgz", - "integrity": "sha1-YTObci/mo1FWiSENJOFMlhSGE+8=", - "dev": true, - "requires": { - "align-text": "^0.1.1" - } - }, "rimraf": { "version": "2.6.2", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.2.tgz", @@ -3002,6 +3452,16 @@ "glob": "^7.0.5" } }, + "ripemd160": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/ripemd160/-/ripemd160-2.0.2.tgz", + "integrity": "sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA==", + "dev": true, + "requires": { + "hash-base": "^3.0.0", + "inherits": "^2.0.1" + } + }, "safe-buffer": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", @@ -3024,9 +3484,9 @@ "dev": true }, "semver": { - "version": "5.6.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.6.0.tgz", - "integrity": "sha512-RS9R6R35NYgQn++fkDWaOmqGoj4Ek9gGs+DPxNUZKuwE183xjJroKvyo1IzVFeXvUrvmALy6FWD5xrdJT25gMg==", + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", "dev": true }, "set-value": { @@ -3052,19 +3512,54 @@ } } }, + "sha.js": { + "version": "2.4.11", + "resolved": "http://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz", + "integrity": "sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==", + "dev": true, + "requires": { + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "shasum": { + "version": "1.0.2", + "resolved": "http://registry.npmjs.org/shasum/-/shasum-1.0.2.tgz", + "integrity": "sha1-5wEjENj0F/TetXEhUOVni4euVl8=", + "dev": true, + "requires": { + "json-stable-stringify": "~0.0.0", + "sha.js": "~2.4.4" + } + }, + "shasum-object": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/shasum-object/-/shasum-object-1.0.0.tgz", + "integrity": "sha512-Iqo5rp/3xVi6M4YheapzZhhGPVs0yZwHj7wvwQ1B9z8H6zk+FEnI7y3Teq7qwnekfEhu8WmG2z0z4iWZaxLWVg==", + "dev": true, + "requires": { + "fast-safe-stringify": "^2.0.7" + } + }, "shebang-command": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", - "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", "dev": true, "requires": { - "shebang-regex": "^1.0.0" + "shebang-regex": "^3.0.0" } }, "shebang-regex": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", - "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true + }, + "shell-quote": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.7.2.tgz", + "integrity": "sha512-mRz/m/JVscCrkMyPqHc/bczi3OQHkLTqXHEFu0zDhK/qfv3UcOA4SVmRCLmos4bhjr9ekVQubj/R7waKapmiQg==", "dev": true }, "shelljs": { @@ -3079,6 +3574,12 @@ "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=", "dev": true }, + "simple-concat": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.0.tgz", + "integrity": "sha1-c0TLuLbib7J9ZrL8hvn21Zl1IcY=", + "dev": true + }, "snapdragon": { "version": "0.8.2", "resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz", @@ -3212,9 +3713,9 @@ "dev": true }, "spdx-correct": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.0.2.tgz", - "integrity": "sha512-q9hedtzyXHr5S0A1vEPoK/7l8NpfkFYTq6iCY+Pno2ZbdZR6WexZFtqeVGkGxW3TEJMN914Z55EnAGMmenlIQQ==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.0.tgz", + "integrity": "sha512-lr2EZCctC2BNR7j7WzJ2FpDznxky1sjfxvvYEyzxNyb6lZXHODmEoJeFu4JupYlkfha1KZpJyoqiJ7pgA1qq8Q==", "dev": true, "requires": { "spdx-expression-parse": "^3.0.0", @@ -3238,21 +3739,11 @@ } }, "spdx-license-ids": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.1.tgz", - "integrity": "sha512-TfOfPcYGBB5sDuPn3deByxPhmfegAhpDYKSOXZQN81Oyrrif8ZCodOLzK3AesELnCx03kikhyDwh0pfvvQvF8w==", + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.5.tgz", + "integrity": "sha512-J+FWzZoynJEXGphVIS+XEh3kFSjZX/1i9gFBaWQcB+/tmpe2qUsSBABpcxqxnAxFdiUFEgAX1bjYGQvIZmoz9Q==", "dev": true }, - "split": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/split/-/split-1.0.1.tgz", - "integrity": "sha512-mTyOoPbrivtXnwnIxZRFYRrPNtEFKlpB2fvjSnCQUiAA6qAZzqwna5envK4uk6OIeP17CsdF3rSBGYVBsU0Tkg==", - "dev": true, - "optional": true, - "requires": { - "through": "2" - } - }, "split-string": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/split-string/-/split-string-3.1.0.tgz", @@ -3263,36 +3754,11 @@ } }, "sprintf-js": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.1.tgz", - "integrity": "sha1-Nr54Mgr+WAH2zqPueLblqrlA6gw=", + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.2.tgz", + "integrity": "sha512-VE0SOVEHCk7Qc8ulkWw3ntAzXuqf7S2lvwQaDLRnUeIEaKNQJzV6BwmLKhOqT61aGhfUMrXeaBk+oDGCzvhcug==", "dev": true }, - "sshpk": { - "version": "1.15.1", - "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.15.1.tgz", - "integrity": "sha512-mSdgNUaidk+dRU5MhYtN9zebdzF2iG0cNPWy8HG+W8y+fT1JnSkh0fzzpjOa0L7P8i1Rscz38t0h4gPcKz43xA==", - "dev": true, - "optional": true, - "requires": { - "asn1": "~0.2.3", - "assert-plus": "^1.0.0", - "bcrypt-pbkdf": "^1.0.0", - "dashdash": "^1.12.0", - "ecc-jsbn": "~0.1.1", - "getpass": "^0.1.1", - "jsbn": "~0.1.0", - "safer-buffer": "^2.0.2", - "tweetnacl": "~0.14.0" - } - }, - "stack-trace": { - "version": "0.0.10", - "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.10.tgz", - "integrity": "sha1-VHxws0fo0ytOEI6hoqFZ5f3eGcA=", - "dev": true, - "optional": true - }, "static-extend": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/static-extend/-/static-extend-0.1.2.tgz", @@ -3314,6 +3780,154 @@ } } }, + "stream-browserify": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/stream-browserify/-/stream-browserify-2.0.2.tgz", + "integrity": "sha512-nX6hmklHs/gr2FuxYDltq8fJA1GDlxKQCz8O/IM4atRqBH8OORmBNgfvW5gG10GT/qQ9u0CzIvr2X5Pkt6ntqg==", + "dev": true, + "requires": { + "inherits": "~2.0.1", + "readable-stream": "^2.0.2" + }, + "dependencies": { + "readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "dev": true, + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "http://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "requires": { + "safe-buffer": "~5.1.0" + } + } + } + }, + "stream-combiner2": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/stream-combiner2/-/stream-combiner2-1.1.1.tgz", + "integrity": "sha1-+02KFCDqNidk4hrUeAOXvry0HL4=", + "dev": true, + "requires": { + "duplexer2": "~0.1.0", + "readable-stream": "^2.0.2" + }, + "dependencies": { + "readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "dev": true, + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "http://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "requires": { + "safe-buffer": "~5.1.0" + } + } + } + }, + "stream-http": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/stream-http/-/stream-http-3.1.0.tgz", + "integrity": "sha512-cuB6RgO7BqC4FBYzmnvhob5Do3wIdIsXAgGycHJnW+981gHqoYcYz9lqjJrk8WXRddbwPuqPYRl+bag6mYv4lw==", + "dev": true, + "requires": { + "builtin-status-codes": "^3.0.0", + "inherits": "^2.0.1", + "readable-stream": "^3.0.6", + "xtend": "^4.0.0" + }, + "dependencies": { + "readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "dev": true, + "requires": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + } + }, + "safe-buffer": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.0.tgz", + "integrity": "sha512-fZEwUGbVl7kouZs1jCdMLdt95hdIv0ZeHg6L7qPeciMZhZ+/gdesW4wgTARkrFWEpspjEATAzUGPG8N2jJiwbg==", + "dev": true + }, + "string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "dev": true, + "requires": { + "safe-buffer": "~5.2.0" + } + } + } + }, + "stream-splicer": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/stream-splicer/-/stream-splicer-2.0.1.tgz", + "integrity": "sha512-Xizh4/NPuYSyAXyT7g8IvdJ9HJpxIGL9PjyhtywCZvvP0OPIdqyrr4dMikeuvY8xahpdKEBlBTySe583totajg==", + "dev": true, + "requires": { + "inherits": "^2.0.1", + "readable-stream": "^2.0.2" + }, + "dependencies": { + "readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "dev": true, + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "http://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "requires": { + "safe-buffer": "~5.1.0" + } + } + } + }, "string_decoder": { "version": "0.10.31", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", @@ -3348,9 +3962,27 @@ } }, "strip-json-comments": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", - "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=" + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.0.1.tgz", + "integrity": "sha512-VTyMAUfdm047mwKl+u79WIdrZxtFtn+nBxHeb844XBQ9uMNTuTHdx2hc5RiAJYqwTj3wc/xe5HLSdJSkJ+WfZw==", + "dev": true + }, + "subarg": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/subarg/-/subarg-1.0.0.tgz", + "integrity": "sha1-9izxdYHplrSPyWVpn1TAauJouNI=", + "dev": true, + "requires": { + "minimist": "^1.1.0" + }, + "dependencies": { + "minimist": { + "version": "1.2.0", + "resolved": "http://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", + "dev": true + } + } }, "supports-color": { "version": "5.5.0", @@ -3361,30 +3993,71 @@ "has-flag": "^3.0.0" } }, - "sync-exec": { - "version": "0.6.2", - "resolved": "https://registry.npmjs.org/sync-exec/-/sync-exec-0.6.2.tgz", - "integrity": "sha1-cX0izFPwzh3vVZQ2LzqJouu5EQU=", - "dev": true + "syntax-error": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/syntax-error/-/syntax-error-1.4.0.tgz", + "integrity": "sha512-YPPlu67mdnHGTup2A8ff7BC2Pjq0e0Yp/IyTFN03zWO0RcK07uLcbi7C2KpGR2FvWbaB0+bfE27a+sBKebSo7w==", + "dev": true, + "requires": { + "acorn-node": "^1.2.0" + } }, "taffydb": { "version": "2.6.2", "resolved": "https://registry.npmjs.org/taffydb/-/taffydb-2.6.2.tgz", - "integrity": "sha1-fLy2S1oUG2ou/CxdLGe04VCyomg=" - }, - "throttleit": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/throttleit/-/throttleit-1.0.0.tgz", - "integrity": "sha1-nnhYNtr0Z0MUWlmEtiaNgoUorGw=", - "dev": true, - "optional": true + "integrity": "sha1-fLy2S1oUG2ou/CxdLGe04VCyomg=", + "dev": true }, "through": { "version": "2.3.8", "resolved": "http://registry.npmjs.org/through/-/through-2.3.8.tgz", "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=", + "dev": true + }, + "through2": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", + "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==", + "dev": true, + "requires": { + "readable-stream": "~2.3.6", + "xtend": "~4.0.1" + }, + "dependencies": { + "readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "dev": true, + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "http://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "requires": { + "safe-buffer": "~5.1.0" + } + } + } + }, + "timers-browserify": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/timers-browserify/-/timers-browserify-1.4.2.tgz", + "integrity": "sha1-ycWLV1voQHN1y14kYtrO50NZ9B0=", "dev": true, - "optional": true + "requires": { + "process": "~0.11.0" + } }, "to-object-path": { "version": "0.3.0", @@ -3428,37 +4101,16 @@ "repeat-string": "^1.6.1" } }, - "tough-cookie": { - "version": "2.4.3", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.4.3.tgz", - "integrity": "sha512-Q5srk/4vDM54WJsJio3XNn6K2sCG+CQ8G5Wz6bZhRZoAe/+TxjWB/GlFAnYEbkYVlON9FMk/fE3h2RLpPXo4lQ==", - "dev": true, - "optional": true, - "requires": { - "psl": "^1.1.24", - "punycode": "^1.4.1" - } - }, "trim-newlines": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-1.0.0.tgz", "integrity": "sha1-WIeWa7WCpFA6QetST301ARgVphM=", "dev": true }, - "tunnel-agent": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", - "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", - "dev": true, - "optional": true, - "requires": { - "safe-buffer": "^5.0.1" - } - }, - "tweetnacl": { - "version": "0.14.5", - "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", - "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=", + "tty-browserify": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/tty-browserify/-/tty-browserify-0.0.1.tgz", + "integrity": "sha512-C3TaO7K81YvjCgQH9Q1S3R3P3BtN3RIM8n+OvX4il1K1zgE8ZhI0op7kClgkxtutIE8hQrcrHBXvIheqKUUCxw==", "dev": true }, "typedarray": { @@ -3467,30 +4119,34 @@ "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=", "dev": true }, + "uc.micro": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-1.0.6.tgz", + "integrity": "sha512-8Y75pvTYkLJW2hWQHXxoqRgV7qb9B+9vFEtidML+7koHUFapnVJAZ6cKs+Qjz5Aw3aZWHMC6u0wJE3At+nSGwA==", + "dev": true + }, "uglify-js": { - "version": "2.6.4", - "resolved": "http://registry.npmjs.org/uglify-js/-/uglify-js-2.6.4.tgz", - "integrity": "sha1-ZeovswWck5RpLxX+2HwrNsFrmt8=", + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.6.0.tgz", + "integrity": "sha512-W+jrUHJr3DXKhrsS7NUVxn3zqMOFn0hL/Ei6v0anCIMoKC93TjcflTagwIHLW7SfMFfiQuktQyFVCFHGUE0+yg==", "dev": true, "requires": { - "async": "~0.2.6", - "source-map": "~0.5.1", - "uglify-to-browserify": "~1.0.0", - "yargs": "~3.10.0" + "commander": "~2.20.0", + "source-map": "~0.6.1" }, "dependencies": { - "async": { - "version": "0.2.10", - "resolved": "http://registry.npmjs.org/async/-/async-0.2.10.tgz", - "integrity": "sha1-trvgsGdLnXGXCMo43owjfLUmw9E=", + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", "dev": true } } }, - "uglify-to-browserify": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/uglify-to-browserify/-/uglify-to-browserify-1.0.2.tgz", - "integrity": "sha1-bgkk1r2mta/jSeOabWMoUKD4grc=", + "umd": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/umd/-/umd-3.0.3.tgz", + "integrity": "sha512-4IcGSufhFshvLNcMCV80UnQVlZ5pMOC8mvNPForqwA4+lzYQuetTESLDQkeLmihq8bRcnpbQa48Wb8Lh16/xow==", "dev": true }, "unc-path-regex": { @@ -3499,26 +4155,25 @@ "integrity": "sha1-5z3T17DXxe2G+6xrCufYxqadUPo=", "dev": true }, - "underscore": { - "version": "1.8.3", - "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.8.3.tgz", - "integrity": "sha1-Tz+1OxBuYJf8+ctBCfKl6b36UCI=" - }, - "underscore-contrib": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/underscore-contrib/-/underscore-contrib-0.3.0.tgz", - "integrity": "sha1-ZltmwkeD+PorGMn4y7Dix9SMJsc=", + "undeclared-identifiers": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/undeclared-identifiers/-/undeclared-identifiers-1.1.3.tgz", + "integrity": "sha512-pJOW4nxjlmfwKApE4zvxLScM/njmwj/DiUBv7EabwE4O8kRUy+HIwxQtZLBPll/jx1LJyBcqNfB3/cpv9EZwOw==", + "dev": true, "requires": { - "underscore": "1.6.0" - }, - "dependencies": { - "underscore": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.6.0.tgz", - "integrity": "sha1-izixDKze9jM3uLJOT/htRa6lKag=" - } + "acorn-node": "^1.3.0", + "dash-ast": "^1.0.0", + "get-assigned-identifiers": "^1.2.0", + "simple-concat": "^1.0.0", + "xtend": "^4.0.1" } }, + "underscore": { + "version": "1.9.2", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.9.2.tgz", + "integrity": "sha512-D39qtimx0c1fI3ya1Lnhk3E9nONswSKhnffBI0gME9C99fYOkNi04xs8K6pePLhvl1frbDemkaBQ5ikWllR2HQ==", + "dev": true + }, "underscore.string": { "version": "3.3.5", "resolved": "https://registry.npmjs.org/underscore.string/-/underscore.string-3.3.5.tgz", @@ -3529,12 +4184,6 @@ "util-deprecate": "^1.0.2" } }, - "unicode-5.2.0": { - "version": "0.7.5", - "resolved": "https://registry.npmjs.org/unicode-5.2.0/-/unicode-5.2.0-0.7.5.tgz", - "integrity": "sha512-KVGLW1Bri30x00yv4HNM8kBxoqFXr0Sbo55735nvrlsx4PYBZol3UtoWgO492fSwmsetzPEZzy73rbU8OGXJcA==", - "dev": true - }, "union-value": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.0.tgz", @@ -3622,29 +4271,49 @@ "integrity": "sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI=", "dev": true }, + "url": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/url/-/url-0.11.0.tgz", + "integrity": "sha1-ODjpfPxgUh63PFJajlW/3Z4uKPE=", + "dev": true, + "requires": { + "punycode": "1.3.2", + "querystring": "0.2.0" + }, + "dependencies": { + "punycode": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz", + "integrity": "sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0=", + "dev": true + } + } + }, "use": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/use/-/use-3.1.1.tgz", "integrity": "sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ==", "dev": true }, + "util": { + "version": "0.10.4", + "resolved": "https://registry.npmjs.org/util/-/util-0.10.4.tgz", + "integrity": "sha512-0Pm9hTQ3se5ll1XihRic3FDIku70C+iHUdT/W926rSgHV5QgXsYbKZN8MSC3tJtSkhuROzvsQjAaFENRXr+19A==", + "dev": true, + "requires": { + "inherits": "2.0.3" + } + }, "util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", "dev": true }, - "uuid": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.2.tgz", - "integrity": "sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA==", - "dev": true, - "optional": true - }, "v8flags": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/v8flags/-/v8flags-3.0.2.tgz", - "integrity": "sha512-6sgSKoFw1UpUPd3cFdF7QGnrH6tDeBgW1F3v9gy8gLY0mlbiBXq8soy8aQpY6xeeCjH5K+JvC62Acp7gtl7wWA==", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/v8flags/-/v8flags-3.1.2.tgz", + "integrity": "sha512-MtivA7GF24yMPte9Rp/BWGCYQNaUj86zeYxV/x2RRJMKagImbbv3u8iJC57lNhWLPcGLJmHcHmFWkNsplbbLWw==", "dev": true, "requires": { "homedir-polyfill": "^1.0.1" @@ -3660,17 +4329,11 @@ "spdx-expression-parse": "^3.0.0" } }, - "verror": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", - "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=", - "dev": true, - "optional": true, - "requires": { - "assert-plus": "^1.0.0", - "core-util-is": "1.0.2", - "extsprintf": "^1.2.0" - } + "vm-browserify": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vm-browserify/-/vm-browserify-1.1.2.tgz", + "integrity": "sha512-2ham8XPWTONajOR0ohOKOHXkm3+gaBmGut3SRuu75xLd/RRaY6vqgh8NBYYk7+RW3u5AtzPQZG8F10LHkl0lAQ==", + "dev": true }, "which": { "version": "1.3.1", @@ -3681,49 +4344,6 @@ "isexe": "^2.0.0" } }, - "window-size": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/window-size/-/window-size-0.1.0.tgz", - "integrity": "sha1-VDjNLqk7IC76Ohn+iIeu58lPnJ0=", - "dev": true - }, - "winston": { - "version": "2.4.4", - "resolved": "https://registry.npmjs.org/winston/-/winston-2.4.4.tgz", - "integrity": "sha512-NBo2Pepn4hK4V01UfcWcDlmiVTs7VTB1h7bgnB0rgP146bYhMxX0ypCz3lBOfNxCO4Zuek7yeT+y/zM1OfMw4Q==", - "dev": true, - "optional": true, - "requires": { - "async": "~1.0.0", - "colors": "1.0.x", - "cycle": "1.0.x", - "eyes": "0.1.x", - "isstream": "0.1.x", - "stack-trace": "0.0.x" - }, - "dependencies": { - "async": { - "version": "1.0.0", - "resolved": "http://registry.npmjs.org/async/-/async-1.0.0.tgz", - "integrity": "sha1-+PwEyjoTeErenhZBr5hXjPvWR6k=", - "dev": true, - "optional": true - }, - "colors": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/colors/-/colors-1.0.3.tgz", - "integrity": "sha1-BDP0TYCWgP3rYO0mDxsMJi6CpAs=", - "dev": true, - "optional": true - } - } - }, - "wordwrap": { - "version": "0.0.2", - "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.2.tgz", - "integrity": "sha1-t5Zpu0LstAn4PVg8rVLKF+qhZD8=", - "dev": true - }, "wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", @@ -3731,38 +4351,25 @@ "dev": true }, "ws": { - "version": "5.2.2", - "resolved": "https://registry.npmjs.org/ws/-/ws-5.2.2.tgz", - "integrity": "sha512-jaHFD6PFv6UgoIVda6qZllptQsMlDEJkTQcybzzXDYM1XO9Y8em691FGMPmM46WGyLU4z9KMgQN+qrux/nhlHA==", + "version": "6.1.3", + "resolved": "https://registry.npmjs.org/ws/-/ws-6.1.3.tgz", + "integrity": "sha512-tbSxiT+qJI223AP4iLfQbkbxkwdFcneYinM2+x46Gx2wgvbaOMO36czfdfVUBRTHvzAMRhDd98sA5d/BuWbQdg==", "dev": true, "requires": { "async-limiter": "~1.0.0" } }, "xmlcreate": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/xmlcreate/-/xmlcreate-1.0.2.tgz", - "integrity": "sha1-+mv3YqYKQT+z3Y9LA8WyaSONMI8=" + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/xmlcreate/-/xmlcreate-2.0.3.tgz", + "integrity": "sha512-HgS+X6zAztGa9zIK3Y3LXuJes33Lz9x+YyTxgrkIdabu2vqcGOWwdfCpf1hWLRrd553wd4QCDf6BBO6FfdsRiQ==", + "dev": true }, - "yargs": { - "version": "3.10.0", - "resolved": "http://registry.npmjs.org/yargs/-/yargs-3.10.0.tgz", - "integrity": "sha1-9+572FfdfB0tOMDnTvvWgdFDH9E=", - "dev": true, - "requires": { - "camelcase": "^1.0.2", - "cliui": "^2.1.0", - "decamelize": "^1.0.0", - "window-size": "0.1.0" - }, - "dependencies": { - "camelcase": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-1.2.1.tgz", - "integrity": "sha1-m7UwTS4LVmmLLHWLCKPqqdqlijk=", - "dev": true - } - } + "xtend": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", + "dev": true }, "yauzl": { "version": "2.4.1", diff --git a/lib/js/package.json b/lib/js/package.json index 2ddd23861f4..a46a7325594 100644 --- a/lib/js/package.json +++ b/lib/js/package.json @@ -1,19 +1,30 @@ { "name": "thrift", - "version": "0.12.0", - "devDependencies": { - "grunt": "^1.0.2", - "grunt-cli": "^1.2.0", - "grunt-contrib-concat": "^1.0.1", - "grunt-contrib-jshint": "^1.0.0", - "grunt-contrib-qunit": "^3.0.1", - "grunt-contrib-uglify": "^1.0.1", - "grunt-jsdoc": "^2.2.1", - "grunt-shell-spawn": "^0.3.10", - "jslint": "^0.12.0" + "version": "0.14.0", + "description": "Thrift is a software framework for scalable cross-language services development.", + "main": "./src/thrift", + "author": { + "name": "Apache Thrift Developers", + "email": "dev@thrift.apache.org" }, - "dependencies": { - "jsdoc": "^3.5.5", - "nopt": "^4.0.1" + "bugs": "https://issues.apache.org/jira/projects/THRIFT/summary", + "homepage": "http://thrift.apache.org", + "repository": "https://github.com/apache/thrift", + "license": "Apache-2.0", + "devDependencies": { + "browserify": "~16.5", + "grunt-cli": "~1.3", + "grunt-contrib-concat": "~1.0", + "grunt-contrib-jshint": "~2.1", + "grunt-contrib-qunit": "~3.1", + "grunt-contrib-uglify": "~4.0", + "grunt-jsdoc": "~2.4", + "grunt-shell-spawn": "~0.4", + "grunt": "~1.0.4", + "jsdoc": "~3.6", + "jslint": "~0.12.1", + "json-int64": "~1.0.2", + "node-int64": "~0.4.0", + "nopt": "~4.0" } } diff --git a/lib/js/src/thrift.js b/lib/js/src/thrift.js index 9418ca37023..123f526ac69 100644 --- a/lib/js/src/thrift.js +++ b/lib/js/src/thrift.js @@ -46,7 +46,7 @@ var Thrift = { * @const {string} Version * @memberof Thrift */ - Version: '0.12.0', + Version: '0.14.0', /** * Thrift IDL type string to Id mapping. @@ -284,7 +284,9 @@ Thrift.TProtocolExceptionType = { Thrift.TProtocolException = function TProtocolException(type, message) { Error.call(this); - Error.captureStackTrace(this, this.constructor); + if (Error.captureStackTrace !== undefined) { + Error.captureStackTrace(this, this.constructor); + } this.name = this.constructor.name; this.type = type; this.message = message; @@ -438,7 +440,12 @@ Thrift.TXHRTransport.prototype = { } }, context: client, - success: jQuery.makeArray(args).pop() + success: jQuery.makeArray(args).pop(), + beforeSend: function (xreq) { + Object.keys(thriftTransport.customHeaders).forEach(function (prop) { + xreq.setRequestHeader(prop, thriftTransport.customHeaders[prop]); + }); + } }); return jqXHR; @@ -1002,7 +1009,11 @@ Thrift.Protocol.prototype = { /** Serializes a number */ writeI64: function(i64) { - this.tstack.push(i64); + if (typeof i64 === 'number') { + this.tstack.push(i64); + } else { + this.tstack.push(Int64Util.toDecimalString(i64)); + } }, /** Serializes a number */ @@ -1073,12 +1084,16 @@ Thrift.Protocol.prototype = { this.rstack = []; this.rpos = []; - if (typeof JSON !== 'undefined' && typeof JSON.parse === 'function') { - this.robj = JSON.parse(this.transport.readAll()); + received = this.transport.readAll(); + + if (typeof JSONInt64 !== 'undefined' && typeof JSONInt64.parse === 'function') { + this.robj = JSONInt64.parse(received); + } else if (typeof JSON !== 'undefined' && typeof JSON.parse === 'function') { + this.robj = JSON.parse(received); } else if (typeof jQuery !== 'undefined') { - this.robj = jQuery.parseJSON(this.transport.readAll()); + this.robj = jQuery.parseJSON(received); } else { - this.robj = eval(this.transport.readAll()); + this.robj = eval(received); } var r = {}; @@ -1221,7 +1236,6 @@ Thrift.Protocol.prototype = { r.vtype = Thrift.Protocol.RType[map.shift()]; r.size = map.shift(); - this.rpos.push(this.rstack.length); this.rstack.push(map.shift()); @@ -1346,8 +1360,54 @@ Thrift.Protocol.prototype = { /** Returns the an object with a value property set to the next value found in the protocol buffer */ - readI64: function() { - return this.readI32(); + readI64: function(f) { + if (f === undefined) { + f = this.rstack[this.rstack.length - 1]; + } + + var r = {}; + + if (f instanceof Array) { + if (f.length === 0) { + r.value = undefined; + } else { + if (!f.isReversed) { + f.reverse(); + f.isReversed = true; + } + r.value = f.pop(); + } + } else if (f instanceof Object) { + var int64Object = true; + var objectKeys = Object.keys(f).sort(); + var int64Keys = ['buffer', 'offset']; + if (objectKeys.length !== int64Keys.length) { + int64Object = false; + } + for (var it=0; int64Object && it < objectKeys.length; ++it) { + if (objectKeys[it] !== int64Keys[it]) { + int64Object = false; + } + } + if (int64Object) { + r.value = f; + } else { + for (var i in f) { + if (i === null) { + continue; + } + this.rstack.push(f[i]); + delete f[i]; + + r.value = i; + break; + } + } + } else { + r.value = f; + this.rstack.pop(); + } + return r; }, /** Returns the an object with a value property set to the @@ -1376,9 +1436,6 @@ Thrift.Protocol.prototype = { skip: function(type) { var ret, i; switch (type) { - case Thrift.Type.STOP: - return null; - case Thrift.Type.BOOL: return this.readBool(); diff --git a/lib/js/test/build.properties b/lib/js/test/build.properties index 84636683c94..845048cad50 100644 --- a/lib/js/test/build.properties +++ b/lib/js/test/build.properties @@ -1,5 +1,5 @@ # Maven Ant tasks Jar details mvn.ant.task.version=2.1.3 -mvn.repo=http://repo1.maven.org/maven2 +mvn.repo=https://repo1.maven.org/maven2 mvn.ant.task.url=${mvn.repo}/org/apache/maven/maven-ant-tasks/${mvn.ant.task.version} mvn.ant.task.jar=maven-ant-tasks-${mvn.ant.task.version}.jar diff --git a/lib/js/test/build.xml b/lib/js/test/build.xml index 04c1360baa1..833e5bd71ba 100755 --- a/lib/js/test/build.xml +++ b/lib/js/test/build.xml @@ -28,13 +28,13 @@ - + - + @@ -73,7 +73,7 @@ You need libthrift*.jar and libthrift*test.jar located at ${thrift.java.dir}/build/libs - Did you compile Thrift Java library and its test suite by "ant compile-test"? + Did you compile Thrift Java library and its test suite by "make check"? @@ -187,7 +187,6 @@ --> - @@ -236,6 +235,7 @@ + diff --git a/lib/js/test/test-deep-constructor.html b/lib/js/test/test-deep-constructor.html index 4c5fb02e4ef..462481a2daf 100755 --- a/lib/js/test/test-deep-constructor.html +++ b/lib/js/test/test-deep-constructor.html @@ -22,6 +22,7 @@ Thrift Javascript Bindings: Unit Test + @@ -35,7 +36,7 @@ -

Thrift Javascript Bindings: Deep Constructor Test (JsDeepConstructorTest.thrift)

+

Thrift Javascript Bindings: Deep Constructor Test (JsDeepConstructorTest.thrift)

diff --git a/lib/js/test/test-double-rendering.html b/lib/js/test/test-double-rendering.html index 240cb3946c3..7a430a50796 100644 --- a/lib/js/test/test-double-rendering.html +++ b/lib/js/test/test-double-rendering.html @@ -29,11 +29,11 @@ - + - - + + diff --git a/lib/js/test/test-double-rendering.js b/lib/js/test/test-double-rendering.js index b4b79b8dfe5..1790c1b80d0 100644 --- a/lib/js/test/test-double-rendering.js +++ b/lib/js/test/test-double-rendering.js @@ -20,29 +20,11 @@ /* * JavaScript test suite for double constants inside - * DebugProtoTest.thrift. These tests will run against Normal (-gen js) + * DoubleConstantsTest.thrift. These tests will run against Normal (-gen js) * Apache Thrift interfaces. * - * Synchronous blocking calls should be identical in both - * Normal and jQuery interfaces. All synchronous tests belong - * here. - * - * Asynchronous success callbacks passed as the last parameter - * of an RPC call should be identical in both Normal and jQuery - * interfaces. Async success tests belong here. - * - * Asynchronous exception processing is different in Normal - * and jQuery interfaces. Such tests belong in the test-nojq.js - * or test-jq.js files respectively. jQuery specific XHR object - * tests also belong in test-jq.js. Do not create any jQuery - * dependencies in this file or in test-nojq.js - * * To compile client code for this test use: - * $ thrift -gen js ThriftTest.thrift - * $ thrift -gen js DebugProtoTest.thrift - * - * See also: - * ++ test-nojq.js for "-gen js" only tests + * $ thrift -gen js DoubleConstantsTest.thrift */ // double assertion threshold diff --git a/lib/js/test/test-es6.html b/lib/js/test/test-es6.html index 5f55da78ee7..f0b8dd9b5f1 100644 --- a/lib/js/test/test-es6.html +++ b/lib/js/test/test-es6.html @@ -22,6 +22,9 @@ Thrift Javascript Bindings: Unit Test + + + @@ -46,7 +49,7 @@ -

Thrift Javascript Bindings: Unit Test (ThriftTest.thrift)

+

Thrift Javascript Bindings: Unit Test (ThriftTest.thrift)

diff --git a/lib/js/test/test-int64.html b/lib/js/test/test-int64.html new file mode 100644 index 00000000000..bcb05fb371e --- /dev/null +++ b/lib/js/test/test-int64.html @@ -0,0 +1,56 @@ ++ + + + + + Int64 Constants in JS: Unit Test + + + + + + + + + + + + + + + + + + + +

Int64 Constants in JS: Unit Test

+

+
+

+
+ + + diff --git a/lib/js/test/test-int64.js b/lib/js/test/test-int64.js new file mode 100644 index 00000000000..cf7e8282b79 --- /dev/null +++ b/lib/js/test/test-int64.js @@ -0,0 +1,97 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + /* jshint -W100 */ + +// Work around for old API used by QUnitAdapter of jsTestDriver +if (typeof QUnit.log == 'function') { + // When using real QUnit (fron PhantomJS) log failures to console + QUnit.log(function(details) { + if (!details.result) { + console.log('======== FAIL ========'); + console.log('TestName: ' + details.name); + if (details.message) console.log(details.message); + console.log('Expected: ' + details.expected); + console.log('Actual : ' + details.actual); + console.log('======================'); + } + }); +} + +QUnit.module('Int64'); + + QUnit.test('Int64', function(assert) { + console.log('Int64 test -- starts'); + const EXPECTED_SMALL_INT64_AS_NUMBER = 42; + const EXPECTED_SMALL_INT64 = new Int64(42); + const EXPECTED_MAX_JS_SAFE_INT64 = new Int64(Number.MAX_SAFE_INTEGER); + const EXPECTED_MIN_JS_SAFE_INT64 = new Int64(Number.MIN_SAFE_INTEGER); + const EXPECTED_MAX_JS_SAFE_PLUS_ONE_INT64 = new Int64("0020000000000000"); // hex-encoded + const EXPECTED_MIN_JS_SAFE_MINUS_ONE_INT64 = new Int64("ffe0000000000000"); // hex-encoded 2's complement + const EXPECTED_MAX_SIGNED_INT64 = new Int64("7fffffffffffffff"); // hex-encoded + const EXPECTED_MIN_SIGNED_INT64 = new Int64("8000000000000000"); // hex-encoded 2's complement + const EXPECTED_INT64_LIST = [ + EXPECTED_SMALL_INT64, + EXPECTED_MAX_JS_SAFE_INT64, + EXPECTED_MIN_JS_SAFE_INT64, + EXPECTED_MAX_JS_SAFE_PLUS_ONE_INT64, + EXPECTED_MIN_JS_SAFE_MINUS_ONE_INT64, + EXPECTED_MAX_SIGNED_INT64, + EXPECTED_MIN_SIGNED_INT64 + ]; + + assert.ok(EXPECTED_SMALL_INT64.equals(Int64Test.SMALL_INT64)); + assert.ok(EXPECTED_MAX_JS_SAFE_INT64.equals(Int64Test.MAX_JS_SAFE_INT64)); + assert.ok(EXPECTED_MIN_JS_SAFE_INT64.equals(Int64Test.MIN_JS_SAFE_INT64)); + assert.ok( + EXPECTED_MAX_JS_SAFE_PLUS_ONE_INT64.equals( + Int64Test.MAX_JS_SAFE_PLUS_ONE_INT64 + ) + ); + assert.ok( + EXPECTED_MIN_JS_SAFE_MINUS_ONE_INT64.equals( + Int64Test.MIN_JS_SAFE_MINUS_ONE_INT64 + ) + ); + assert.ok(EXPECTED_MAX_SIGNED_INT64.equals(Int64Test.MAX_SIGNED_INT64)); + assert.ok(EXPECTED_MIN_SIGNED_INT64.equals(Int64Test.MIN_SIGNED_INT64)); + assert.equal( + EXPECTED_SMALL_INT64_AS_NUMBER, + Int64Test.SMALL_INT64.toNumber() + ); + assert.equal( + Number.MAX_SAFE_INTEGER, + Int64Test.MAX_JS_SAFE_INT64.toNumber() + ); + assert.equal( + Number.MIN_SAFE_INTEGER, + Int64Test.MIN_JS_SAFE_INT64.toNumber() + ); + + for (let i = 0; i < EXPECTED_INT64_LIST.length; ++i) { + assert.ok(EXPECTED_INT64_LIST[i].equals(Int64Test.INT64_LIST[i])); + } + + for (let i = 0; i < EXPECTED_INT64_LIST.length; ++i){ + let int64Object = EXPECTED_INT64_LIST[i]; + assert.ok(Int64Test.INT64_2_INT64_MAP[JSONInt64.toDecimalString(int64Object)].equals(int64Object)); + } + + console.log('Int64 test -- ends'); + }); + diff --git a/lib/js/test/test-nojq.html b/lib/js/test/test-nojq.html index 9eec7fcd3f1..37b3eb8d0e8 100644 --- a/lib/js/test/test-nojq.html +++ b/lib/js/test/test-nojq.html @@ -22,6 +22,9 @@ Thrift Javascript Bindings: Unit Test + + + @@ -35,7 +38,7 @@ -

Thrift Javascript Bindings: Unit Test (ThriftTest.thrift)

+

Thrift Javascript Bindings: Unit Test (ThriftTest.thrift)

diff --git a/lib/js/test/test.html b/lib/js/test/test.html index af035b6ecca..85c83956737 100755 --- a/lib/js/test/test.html +++ b/lib/js/test/test.html @@ -22,6 +22,9 @@ Thrift Javascript Bindings: Unit Test + + + @@ -38,7 +41,7 @@ -

Thrift Javascript Bindings: Unit Test (ThriftTest.thrift)

+

Thrift Javascript Bindings: Unit Test (ThriftTest.thrift)

diff --git a/lib/js/test/test.js b/lib/js/test/test.js index a86a50935c9..35ed6ffd284 100755 --- a/lib/js/test/test.js +++ b/lib/js/test/test.js @@ -51,6 +51,9 @@ const transport = new Thrift.Transport('/service'); const protocol = new Thrift.Protocol(transport); const client = new ThriftTest.ThriftTestClient(protocol); +const int64_2_pow_60 = new Int64('1000000000000000'); +const int64_minus_2_pow_60 = new Int64('f000000000000000'); + // Work around for old API used by QUnitAdapter of jsTestDriver if (typeof QUnit.log == 'function') { // When using real QUnit (fron PhantomJS) log failures to console @@ -132,9 +135,12 @@ QUnit.module('Base Types'); }); QUnit.test('I64', function(assert) { assert.equal(client.testI64(0), 0); - //This is usually 2^60 but JS cannot represent anything over 2^52 accurately - assert.equal(client.testI64(Math.pow(2, 52)), Math.pow(2, 52)); - assert.equal(client.testI64(-Math.pow(2, 52)), -Math.pow(2, 52)); + + let int64_2_pow_60_result = client.testI64(int64_2_pow_60); + assert.ok(int64_2_pow_60.equals(int64_2_pow_60_result)); + + let int64_minus_2_pow_60_result = client.testI64(int64_minus_2_pow_60); + assert.ok(int64_minus_2_pow_60.equals(int64_minus_2_pow_60_result)); }); @@ -145,15 +151,14 @@ QUnit.module('Structured Types'); structTestInput.string_thing = 'worked'; structTestInput.byte_thing = 0x01; structTestInput.i32_thing = Math.pow(2, 30); - //This is usually 2^60 but JS cannot represent anything over 2^52 accurately - structTestInput.i64_thing = Math.pow(2, 52); + structTestInput.i64_thing = int64_2_pow_60; const structTestOutput = client.testStruct(structTestInput); assert.equal(structTestOutput.string_thing, structTestInput.string_thing); assert.equal(structTestOutput.byte_thing, structTestInput.byte_thing); assert.equal(structTestOutput.i32_thing, structTestInput.i32_thing); - assert.equal(structTestOutput.i64_thing, structTestInput.i64_thing); + assert.ok(structTestOutput.i64_thing.equals(structTestInput.i64_thing)); assert.equal(JSON.stringify(structTestOutput), JSON.stringify(structTestInput)); }); @@ -163,8 +168,7 @@ QUnit.module('Structured Types'); xtrTestInput.string_thing = 'worked'; xtrTestInput.byte_thing = 0x01; xtrTestInput.i32_thing = Math.pow(2, 30); - //This is usually 2^60 but JS cannot represent anything over 2^52 accurately - xtrTestInput.i64_thing = Math.pow(2, 52); + xtrTestInput.i64_thing = int64_2_pow_60; const nestTestInput = new ThriftTest.Xtruct2(); nestTestInput.byte_thing = 0x02; @@ -177,7 +181,7 @@ QUnit.module('Structured Types'); assert.equal(nestTestOutput.struct_thing.string_thing, nestTestInput.struct_thing.string_thing); assert.equal(nestTestOutput.struct_thing.byte_thing, nestTestInput.struct_thing.byte_thing); assert.equal(nestTestOutput.struct_thing.i32_thing, nestTestInput.struct_thing.i32_thing); - assert.equal(nestTestOutput.struct_thing.i64_thing, nestTestInput.struct_thing.i64_thing); + assert.ok(nestTestOutput.struct_thing.i64_thing.equals(nestTestInput.struct_thing.i64_thing)); assert.equal(nestTestOutput.i32_thing, nestTestInput.i32_thing); assert.equal(JSON.stringify(nestTestOutput), JSON.stringify(nestTestInput)); @@ -252,7 +256,7 @@ QUnit.module('Structured Types'); structTestInput.string_thing = 'worked'; structTestInput.byte_thing = 0x01; structTestInput.i32_thing = Math.pow(2, 30); - structTestInput.i64_thing = Math.pow(2, 52); + structTestInput.i64_thing = int64_2_pow_60; const structTestOutput = modifiedClient.testStruct(structTestInput); @@ -260,7 +264,7 @@ QUnit.module('Structured Types'); assert.equal(structTestOutput.string_thing, structTestInput.string_thing); assert.equal(structTestOutput.changed, null); assert.equal(structTestOutput.i32_thing, structTestInput.i32_thing); - assert.equal(structTestOutput.i64_thing, structTestInput.i64_thing); + assert.ok(structTestOutput.i64_thing.equals(structTestInput.i64_thing)); }); @@ -332,13 +336,13 @@ QUnit.module('Insanity'); 'string_thing': 'Goodbye4', 'byte_thing': 4, 'i32_thing': 4, - 'i64_thing': 4 + 'i64_thing': new Int64(4) }, { 'string_thing': 'Hello2', 'byte_thing': 2, 'i32_thing': 2, - 'i64_thing': 2 + 'i64_thing': new Int64(2) }] }; QUnit.test('testInsanity', function(assert) { @@ -401,15 +405,13 @@ QUnit.module('Async'); assert.expect(2); const done = assert.async(2); - //This is usually 2^60 but JS cannot represent anything over 2^52 accurately - client.testI64(Math.pow(2, 52), function(result) { - assert.equal(result, Math.pow(2, 52)); + client.testI64(int64_2_pow_60, function(result) { + assert.ok(int64_2_pow_60.equals(result)); done(); }); - //This is usually 2^60 but JS cannot represent anything over 2^52 accurately - client.testI64(Math.pow(-2, 52), function(result) { - assert.equal(result, Math.pow(-2, 52)); + client.testI64(int64_minus_2_pow_60, function(result) { + assert.ok(int64_minus_2_pow_60.equals(result)); done(); }); }); diff --git a/lib/js/test/test_handler.js b/lib/js/test/test_handler.js index af5f7bd9c17..8ba296ba108 100644 --- a/lib/js/test/test_handler.js +++ b/lib/js/test/test_handler.js @@ -24,6 +24,7 @@ const es6Mode = process.argv.includes('--es6'); const genFolder = es6Mode ? 'gen-nodejs-es6' : 'gen-nodejs'; const ttypes = require(`./${genFolder}/ThriftTest_types`); const TException = require('../../nodejs/lib/thrift').TException; +const Int64 = require('node-int64'); exports.ThriftTestHandler = { testVoid: function(result) { @@ -122,13 +123,13 @@ exports.ThriftTestHandler = { hello.string_thing = 'Hello2'; hello.byte_thing = 2; hello.i32_thing = 2; - hello.i64_thing = 2; + hello.i64_thing = new Int64(2); const goodbye = new ttypes.Xtruct(); goodbye.string_thing = 'Goodbye4'; goodbye.byte_thing = 4; goodbye.i32_thing = 4; - goodbye.i64_thing = 4; + goodbye.i64_thing = new Int64(4); const crazy = new ttypes.Insanity(); crazy.userMap = []; diff --git a/lib/js/test/testws.html b/lib/js/test/testws.html index 1edf0e07a95..5ff9f22911e 100644 --- a/lib/js/test/testws.html +++ b/lib/js/test/testws.html @@ -22,6 +22,9 @@ Thrift Javascript Bindings: Unit Test + + + @@ -46,7 +49,7 @@ -

Thrift Javascript Bindings: Unit Test (ThriftTest.thrift)

+

Thrift Javascript Bindings: Unit Test (ThriftTest.thrift)

diff --git a/lib/json/Makefile.am b/lib/json/Makefile.am index 1051b9bacd6..6c8c0ce8116 100644 --- a/lib/json/Makefile.am +++ b/lib/json/Makefile.am @@ -17,13 +17,17 @@ # under the License. # -SUBDIRS = - if WITH_JAVA # Schema validation test depends on java -SUBDIRS += test +SUBDIRS = test endif +clean-local: + $(RM) -r test/build/ + +dist-hook: + $(RM) -r $(distdir)/test/build/ + EXTRA_DIST = \ schema.json \ test diff --git a/lib/json/test/build.properties b/lib/json/test/build.properties index 075f6400189..997aa9bfa44 100644 --- a/lib/json/test/build.properties +++ b/lib/json/test/build.properties @@ -5,6 +5,6 @@ mvn.ant.task.version=2.1.3 json-schema-validator.version=2.2.6 # Maven dependency download locations -mvn.repo=http://repo1.maven.org/maven2 +mvn.repo=https://repo1.maven.org/maven2 mvn.ant.task.url=${mvn.repo}/org/apache/maven/maven-ant-tasks/${mvn.ant.task.version} mvn.ant.task.jar=maven-ant-tasks-${mvn.ant.task.version}.jar diff --git a/lib/json/test/build.xml b/lib/json/test/build.xml index 956a2382b74..eb39c4f2840 100644 --- a/lib/json/test/build.xml +++ b/lib/json/test/build.xml @@ -115,6 +115,7 @@ + diff --git a/lib/lua/Makefile.am b/lib/lua/Makefile.am index 5b0f17a3573..3b272f56cbe 100644 --- a/lib/lua/Makefile.am +++ b/lib/lua/Makefile.am @@ -17,7 +17,7 @@ # under the License. # -AUTOMAKE_OPTIONS = subdir-objects +AUTOMAKE_OPTIONS = subdir-objects nostdinc SUBDIRS = . diff --git a/lib/lua/TCompactProtocol.lua b/lib/lua/TCompactProtocol.lua index 877595a5d61..8ec7b3a2c0c 100644 --- a/lib/lua/TCompactProtocol.lua +++ b/lib/lua/TCompactProtocol.lua @@ -118,8 +118,8 @@ function TCompactProtocol:writeMessageEnd() end function TCompactProtocol:writeStructBegin(name) - self.lastFieldIndex = self.lastFieldIndex + 1 self.lastField[self.lastFieldIndex] = self.lastFieldId + self.lastFieldIndex = self.lastFieldIndex + 1 self.lastFieldId = 0 end @@ -176,7 +176,6 @@ function TCompactProtocol:writeBool(bool) if bool then value = TCompactType.COMPACT_BOOLEAN_TRUE end - print(value,self.booleanFieldPending,self.booleanFieldId) if self.booleanFieldPending then self:writeFieldBeginInternal(self.booleanFieldName, TType.BOOL, self.booleanFieldId, value) self.booleanFieldPending = false @@ -293,17 +292,20 @@ function TCompactProtocol:readFieldBegin() if ttype == TType.STOP then return nil, ttype, 0 end - -- mask off the 4 MSB of the type header. it could contain a field id delta. - local modifier = libluabitwise.shiftr(libluabitwise.band(field_and_ttype, 0xf0), 4) + local modifier = libluabitwise.shiftr(field_and_ttype, 4) local id = 0 if modifier == 0 then id = self:readI16() else id = self.lastFieldId + modifier end - if ttype == TType.BOOL then - boolValue = libluabitwise.band(field_and_ttype, 0x0f) == TCompactType.COMPACT_BOOLEAN_TRUE - boolValueIsNotNull = true + local type = libluabitwise.band(field_and_ttype, 0x0f) + if type == TCompactType.COMPACT_BOOLEAN_TRUE then + self.boolValue = true + self.boolValueIsNotNull = true + elseif type == TCompactType.COMPACT_BOOLEAN_FALSE then + self.boolValue = false + self.boolValueIsNotNull = true end self.lastFieldId = id return nil, ttype, id @@ -314,10 +316,10 @@ end function TCompactProtocol:readMapBegin() local size = self:readVarint32() - if size < 0 then - return nil,nil,nil + local kvtype = 0 + if size > 0 then + kvtype = self:readSignByte() end - local kvtype = self:readSignByte() local ktype = self:getTType(libluabitwise.shiftr(kvtype, 4)) local vtype = self:getTType(kvtype) return ktype, vtype, size @@ -350,9 +352,9 @@ function TCompactProtocol:readSetEnd() end function TCompactProtocol:readBool() - if boolValueIsNotNull then - boolValueIsNotNull = true - return boolValue + if self.boolValueIsNotNull then + self.boolValueIsNotNull = false + return self.boolValue end local val = self:readSignByte() if val == TCompactType.COMPACT_BOOLEAN_TRUE then @@ -426,7 +428,7 @@ function TCompactProtocol:readVarint64() local data = result(0) local shiftl = 0 while true do - b = self:readByte() + b = self:readSignByte() endFlag, data = libluabpack.fromVarint64(b, shiftl, data) shiftl = shiftl + 7 if endFlag == 0 then diff --git a/lib/lua/THttpTransport.lua b/lib/lua/THttpTransport.lua index 060a8ab5e09..2951db79ff8 100644 --- a/lib/lua/THttpTransport.lua +++ b/lib/lua/THttpTransport.lua @@ -25,7 +25,7 @@ THttpTransport = TTransportBase:new{ wBuf = '', rBuf = '', CRLF = '\r\n', - VERSION = '0.12.0', + VERSION = version, isServer = true } diff --git a/lib/lua/TProtocol.lua b/lib/lua/TProtocol.lua index 616e167a958..1306fb3d8c3 100644 --- a/lib/lua/TProtocol.lua +++ b/lib/lua/TProtocol.lua @@ -107,9 +107,7 @@ function TProtocolBase:readDouble() end function TProtocolBase:readString() end function TProtocolBase:skip(ttype) - if type == TType.STOP then - return - elseif ttype == TType.BOOL then + if ttype == TType.BOOL then self:readBool() elseif ttype == TType.BYTE then self:readByte() @@ -153,6 +151,10 @@ function TProtocolBase:skip(ttype) self:skip(ettype) end self:readListEnd() + else + terror(TProtocolException:new{ + message = 'Invalid data' + }) end end diff --git a/lib/lua/TServer.lua b/lib/lua/TServer.lua index 4e37d587157..9afe19e5eef 100644 --- a/lib/lua/TServer.lua +++ b/lib/lua/TServer.lua @@ -85,9 +85,17 @@ function TServer:_preServe() end end +function TServer:setExceptionHandler(exceptionHandler) + self.exceptionHandler = exceptionHandler +end + function TServer:_handleException(err) if string.find(err, 'TTransportException') == nil then - print(err) + if self.exceptionHandler then + self.exceptionHandler(err) + else + print(err) + end end end diff --git a/lib/lua/Thrift.lua b/lib/lua/Thrift.lua index d49572935c9..4a290de5cb8 100644 --- a/lib/lua/Thrift.lua +++ b/lib/lua/Thrift.lua @@ -48,7 +48,7 @@ function ttable_size(t) return count end -version = '0.12.0' +version = '0.14.0' TType = { STOP = 0, diff --git a/lib/lua/src/luasocket.c b/lib/lua/src/luasocket.c index d48351077bc..6f63d3dbfa1 100644 --- a/lib/lua/src/luasocket.c +++ b/lib/lua/src/luasocket.c @@ -344,22 +344,18 @@ static int l_socket_create_and_connect(lua_State *L) { // Create and connect loop for timeout milliseconds end = __gettime() + timeout/1000; do { - // Create the socket - err = tcp_create(&sock); - if (!err) { - // Connect - err = tcp_connect(&sock, host, port, timeout); - if (err) { - tcp_destroy(&sock); - usleep(100000); // sleep for 100ms - } else { - p_tcp tcp = (p_tcp) lua_newuserdata(L, sizeof(t_tcp)); - settype(L, -2, SOCKET_CLIENT); - socket_setnonblocking(&sock); - tcp->sock = sock; - tcp->timeout = timeout; - return 1; // Return userdata - } + // Create and connect the socket + err = tcp_create_and_connect(&sock, host, port, timeout); + if (err) { + tcp_destroy(&sock); + usleep(100000); // sleep for 100ms + } else { + p_tcp tcp = (p_tcp) lua_newuserdata(L, sizeof(t_tcp)); + settype(L, -2, SOCKET_CLIENT); + socket_setnonblocking(&sock); + tcp->sock = sock; + tcp->timeout = timeout; + return 1; // Return userdata } } while (err && __gettime() < end); diff --git a/lib/lua/src/socket.h b/lib/lua/src/socket.h index afb827e4739..c09eabc1af3 100644 --- a/lib/lua/src/socket.h +++ b/lib/lua/src/socket.h @@ -75,4 +75,7 @@ const char * tcp_accept(p_socket sock, p_socket client, int timeout); const char * tcp_connect(p_socket sock, const char *host, unsigned short port, int timeout); +const char * tcp_create_and_connect(p_socket sock, const char *host, + unsigned short port, int timeout); + #endif diff --git a/lib/lua/src/usocket.c b/lib/lua/src/usocket.c index 1a1b549a0c6..21c0bac9e92 100644 --- a/lib/lua/src/usocket.c +++ b/lib/lua/src/usocket.c @@ -89,12 +89,6 @@ T_ERRCODE socket_wait(p_socket sock, int mode, int timeout) { return TIMEOUT; } - // Verify that we can actually read from the remote host - if (mode & WAIT_MODE_C && FD_ISSET(*sock, &rfds) && - recv(*sock, (char*) &rfds, 0, 0) != 0) { - return errno; - } - return SUCCESS; } @@ -289,6 +283,7 @@ T_ERRCODE socket_setblocking(p_socket sock) { return strerror(err) const char * tcp_create(p_socket sock) { + // TODO support IPv6 int err = socket_create(sock, AF_INET, SOCK_STREAM, 0); ERRORSTR_RETURN(err); } @@ -299,6 +294,7 @@ const char * tcp_destroy(p_socket sock) { } const char * tcp_bind(p_socket sock, const char *host, unsigned short port) { + // TODO support IPv6 int err; struct hostent *h; struct sockaddr_in local; @@ -333,6 +329,7 @@ const char * tcp_connect(p_socket sock, const char *host, unsigned short port, int timeout) { + // TODO support IPv6 int err; struct hostent *h; struct sockaddr_in remote; @@ -352,6 +349,66 @@ const char * tcp_connect(p_socket sock, ERRORSTR_RETURN(err); } +const char * tcp_create_and_connect(p_socket sock, + const char *host, + unsigned short port, + int timeout) { + int err; + struct sockaddr_in sa4; + struct sockaddr_in6 sa6; + + memset(&sa4, 0, sizeof(sa4)); + sa4.sin_family = AF_INET; + sa4.sin_port = htons(port); + memset(&sa6, 0, sizeof(sa6)); + sa6.sin6_family = AF_INET6; + sa6.sin6_port = htons(port); + + if (inet_pton(AF_INET, host, &sa4.sin_addr)) { + socket_create(sock, AF_INET, SOCK_STREAM, 0); + err = socket_connect(sock, (p_sa) &sa4, sizeof(sa4), timeout); + ERRORSTR_RETURN(err); + } else if (inet_pton(AF_INET6, host, &sa6.sin6_addr)) { + socket_create(sock, AF_INET6, SOCK_STREAM, 0); + err = socket_connect(sock, (p_sa) &sa6, sizeof(sa6), timeout); + ERRORSTR_RETURN(err); + } else { + struct addrinfo hints, *servinfo, *rp; + char portStr[6]; + int rv; + + memset(&hints, 0, sizeof(hints)); + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + + sprintf(portStr, "%u", port); + + if ((rv = getaddrinfo(host, portStr, &hints, &servinfo)) != 0) { + return gai_strerror(rv); + } + + err = TIMEOUT; + for (rp = servinfo; rp != NULL; rp = rp->ai_next) { + err = socket_create(sock, rp->ai_family, rp->ai_socktype, rp->ai_protocol); + if (err != SUCCESS) { + continue; + } + err = socket_connect(sock, (p_sa) rp->ai_addr, rp->ai_addrlen, timeout); + if (err == SUCCESS) { + break; + } + close(*sock); + } + freeaddrinfo(servinfo); + if (rp == NULL) { + *sock = -1; + return "Failed to connect"; + } else { + ERRORSTR_RETURN(err); + } + } +} + #define WRITE_STEP 8192 const char * tcp_send( p_socket sock, const char * data, size_t w_len, int timeout) { diff --git a/lib/netcore/README.md b/lib/netcore/README.md deleted file mode 100644 index 94b047f5cd9..00000000000 --- a/lib/netcore/README.md +++ /dev/null @@ -1,24 +0,0 @@ -# Apache Thrift netcore - -Thrift client library ported to Microsoft .Net Core - -# Content -- Tests/Thrift.PublicInterfaces.Compile.Tests - project for checking public interfaces during adding changes to Thrift library -- Thrift - Thrift library - -# Reused components -- .NET Standard 1.6 (SDK 2.0.0) - -# How to build on Windows -- Get Thrift IDL compiler executable, add to some folder and add path to this folder into PATH variable -- Open the Thrift.sln project with Visual Studio and build. -or -- Build with scripts - -# How to build on Unix -- Ensure you have .NET Core 2.0.0 SDK installed or use the Ubuntu Xenial docker image -- Follow common build practice for Thrift: bootstrap, configure, and make - -# Known issues -- In trace logging mode you can see some not important internal exceptions - diff --git a/lib/netcore/Tests/Thrift.IntegrationTests/.gitignore b/lib/netcore/Tests/Thrift.IntegrationTests/.gitignore deleted file mode 100644 index 7254c313a61..00000000000 --- a/lib/netcore/Tests/Thrift.IntegrationTests/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -# ignore for autogenerated files -/Apache diff --git a/lib/netcore/Tests/Thrift.IntegrationTests/Thrift.IntegrationTests.csproj b/lib/netcore/Tests/Thrift.IntegrationTests/Thrift.IntegrationTests.csproj deleted file mode 100644 index f25dac536fd..00000000000 --- a/lib/netcore/Tests/Thrift.IntegrationTests/Thrift.IntegrationTests.csproj +++ /dev/null @@ -1,29 +0,0 @@ - - - netcoreapp2.0 - Thrift.IntegrationTests - Thrift.IntegrationTests - Exe - false - false - false - false - false - false - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/lib/netcore/Tests/Thrift.PublicInterfaces.Compile.Tests/.gitignore b/lib/netcore/Tests/Thrift.PublicInterfaces.Compile.Tests/.gitignore deleted file mode 100644 index ae929a32e42..00000000000 --- a/lib/netcore/Tests/Thrift.PublicInterfaces.Compile.Tests/.gitignore +++ /dev/null @@ -1,4 +0,0 @@ -# ignore for autogenerated files -/ThriftTest -/Apache -/Facebook diff --git a/lib/netcore/Tests/Thrift.PublicInterfaces.Compile.Tests/Thrift.PublicInterfaces.Compile.Tests.csproj b/lib/netcore/Tests/Thrift.PublicInterfaces.Compile.Tests/Thrift.PublicInterfaces.Compile.Tests.csproj deleted file mode 100644 index c4a84a301f0..00000000000 --- a/lib/netcore/Tests/Thrift.PublicInterfaces.Compile.Tests/Thrift.PublicInterfaces.Compile.Tests.csproj +++ /dev/null @@ -1,36 +0,0 @@ - - - - netcoreapp2.0 - Thrift.PublicInterfaces.Compile.Tests - Thrift.PublicInterfaces.Compile.Tests - false - false - false - false - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/lib/netcore/Tests/Thrift.Tests/Collections/TCollectionsTests.cs b/lib/netcore/Tests/Thrift.Tests/Collections/TCollectionsTests.cs deleted file mode 100644 index 1be99b48fd6..00000000000 --- a/lib/netcore/Tests/Thrift.Tests/Collections/TCollectionsTests.cs +++ /dev/null @@ -1,83 +0,0 @@ -// Licensed to the Apache Software Foundation(ASF) under one -// or more contributor license agreements.See the NOTICE file -// distributed with this work for additional information -// regarding copyright ownership.The ASF licenses this file -// to you under the Apache License, Version 2.0 (the -// "License"); you may not use this file except in compliance -// with the License. You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, -// software distributed under the License is distributed on an -// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -// KIND, either express or implied. See the License for the -// specific language governing permissions and limitations -// under the License. - -using System; -using System.Collections.Generic; -using System.Text; -using Microsoft.VisualStudio.TestTools.UnitTesting; -using Thrift.Collections; - -namespace Thrift.Tests.Collections -{ - // ReSharper disable once InconsistentNaming - [TestClass] - public class TCollectionsTests - { - //TODO: Add tests for IEnumerable with objects and primitive values inside - - [TestMethod] - public void TCollection_Equals_Primitive_Test() - { - var collection1 = new List {1,2,3}; - var collection2 = new List {1,2,3}; - - var result = TCollections.Equals(collection1, collection2); - - Assert.IsTrue(result); - } - - [TestMethod] - public void TCollection_Equals_Primitive_Different_Test() - { - var collection1 = new List { 1, 2, 3 }; - var collection2 = new List { 1, 2 }; - - var result = TCollections.Equals(collection1, collection2); - - Assert.IsFalse(result); - } - - [TestMethod] - public void TCollection_Equals_Objects_Test() - { - var collection1 = new List { new ExampleClass { X = 1 }, new ExampleClass { X = 2 } }; - var collection2 = new List { new ExampleClass { X = 1 }, new ExampleClass { X = 2 } }; - - var result = TCollections.Equals(collection1, collection2); - - // references to different collections - Assert.IsFalse(result); - } - - [TestMethod] - public void TCollection_Equals_OneAndTheSameObject_Test() - { - var collection1 = new List { new ExampleClass { X = 1 }, new ExampleClass { X = 2 } }; - var collection2 = collection1; - - var result = TCollections.Equals(collection1, collection2); - - // references to one and the same collection - Assert.IsTrue(result); - } - - private class ExampleClass - { - public int X { get; set; } - } - } -} diff --git a/lib/netcore/Tests/Thrift.Tests/Thrift.Tests.csproj b/lib/netcore/Tests/Thrift.Tests/Thrift.Tests.csproj deleted file mode 100644 index e46f16522f1..00000000000 --- a/lib/netcore/Tests/Thrift.Tests/Thrift.Tests.csproj +++ /dev/null @@ -1,18 +0,0 @@ - - - netcoreapp2.0 - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/lib/netcore/Thrift.sln b/lib/netcore/Thrift.sln deleted file mode 100644 index fe30aa5c676..00000000000 --- a/lib/netcore/Thrift.sln +++ /dev/null @@ -1,85 +0,0 @@ -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 15 -VisualStudioVersion = 15.0.26730.12 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tests", "Tests", "{F51FC4DA-CAC0-48B1-A069-B1712BCAA5BE}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Thrift", "Thrift\Thrift.csproj", "{D85F572F-7D80-40A4-9A9B-2731ED187C24}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Thrift.IntegrationTests", "Tests\Thrift.IntegrationTests\Thrift.IntegrationTests.csproj", "{9F9A11BF-3C95-4E80-AFBF-768541996844}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Thrift.Tests", "Tests\Thrift.Tests\Thrift.Tests.csproj", "{75C2F9DC-3546-4D0A-A2DF-31C93516B6C1}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Thrift.PublicInterfaces.Compile.Tests", "Tests\Thrift.PublicInterfaces.Compile.Tests\Thrift.PublicInterfaces.Compile.Tests.csproj", "{A429F05B-F511-45EF-AE7B-04E1AE9C9367}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Debug|x64 = Debug|x64 - Debug|x86 = Debug|x86 - Release|Any CPU = Release|Any CPU - Release|x64 = Release|x64 - Release|x86 = Release|x86 - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {D85F572F-7D80-40A4-9A9B-2731ED187C24}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {D85F572F-7D80-40A4-9A9B-2731ED187C24}.Debug|Any CPU.Build.0 = Debug|Any CPU - {D85F572F-7D80-40A4-9A9B-2731ED187C24}.Debug|x64.ActiveCfg = Debug|Any CPU - {D85F572F-7D80-40A4-9A9B-2731ED187C24}.Debug|x64.Build.0 = Debug|Any CPU - {D85F572F-7D80-40A4-9A9B-2731ED187C24}.Debug|x86.ActiveCfg = Debug|Any CPU - {D85F572F-7D80-40A4-9A9B-2731ED187C24}.Debug|x86.Build.0 = Debug|Any CPU - {D85F572F-7D80-40A4-9A9B-2731ED187C24}.Release|Any CPU.ActiveCfg = Release|Any CPU - {D85F572F-7D80-40A4-9A9B-2731ED187C24}.Release|Any CPU.Build.0 = Release|Any CPU - {D85F572F-7D80-40A4-9A9B-2731ED187C24}.Release|x64.ActiveCfg = Release|Any CPU - {D85F572F-7D80-40A4-9A9B-2731ED187C24}.Release|x64.Build.0 = Release|Any CPU - {D85F572F-7D80-40A4-9A9B-2731ED187C24}.Release|x86.ActiveCfg = Release|Any CPU - {D85F572F-7D80-40A4-9A9B-2731ED187C24}.Release|x86.Build.0 = Release|Any CPU - {9F9A11BF-3C95-4E80-AFBF-768541996844}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {9F9A11BF-3C95-4E80-AFBF-768541996844}.Debug|Any CPU.Build.0 = Debug|Any CPU - {9F9A11BF-3C95-4E80-AFBF-768541996844}.Debug|x64.ActiveCfg = Debug|Any CPU - {9F9A11BF-3C95-4E80-AFBF-768541996844}.Debug|x64.Build.0 = Debug|Any CPU - {9F9A11BF-3C95-4E80-AFBF-768541996844}.Debug|x86.ActiveCfg = Debug|Any CPU - {9F9A11BF-3C95-4E80-AFBF-768541996844}.Debug|x86.Build.0 = Debug|Any CPU - {9F9A11BF-3C95-4E80-AFBF-768541996844}.Release|Any CPU.ActiveCfg = Release|Any CPU - {9F9A11BF-3C95-4E80-AFBF-768541996844}.Release|Any CPU.Build.0 = Release|Any CPU - {9F9A11BF-3C95-4E80-AFBF-768541996844}.Release|x64.ActiveCfg = Release|Any CPU - {9F9A11BF-3C95-4E80-AFBF-768541996844}.Release|x64.Build.0 = Release|Any CPU - {9F9A11BF-3C95-4E80-AFBF-768541996844}.Release|x86.ActiveCfg = Release|Any CPU - {9F9A11BF-3C95-4E80-AFBF-768541996844}.Release|x86.Build.0 = Release|Any CPU - {75C2F9DC-3546-4D0A-A2DF-31C93516B6C1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {75C2F9DC-3546-4D0A-A2DF-31C93516B6C1}.Debug|Any CPU.Build.0 = Debug|Any CPU - {75C2F9DC-3546-4D0A-A2DF-31C93516B6C1}.Debug|x64.ActiveCfg = Debug|Any CPU - {75C2F9DC-3546-4D0A-A2DF-31C93516B6C1}.Debug|x64.Build.0 = Debug|Any CPU - {75C2F9DC-3546-4D0A-A2DF-31C93516B6C1}.Debug|x86.ActiveCfg = Debug|Any CPU - {75C2F9DC-3546-4D0A-A2DF-31C93516B6C1}.Debug|x86.Build.0 = Debug|Any CPU - {75C2F9DC-3546-4D0A-A2DF-31C93516B6C1}.Release|Any CPU.ActiveCfg = Release|Any CPU - {75C2F9DC-3546-4D0A-A2DF-31C93516B6C1}.Release|Any CPU.Build.0 = Release|Any CPU - {75C2F9DC-3546-4D0A-A2DF-31C93516B6C1}.Release|x64.ActiveCfg = Release|Any CPU - {75C2F9DC-3546-4D0A-A2DF-31C93516B6C1}.Release|x64.Build.0 = Release|Any CPU - {75C2F9DC-3546-4D0A-A2DF-31C93516B6C1}.Release|x86.ActiveCfg = Release|Any CPU - {75C2F9DC-3546-4D0A-A2DF-31C93516B6C1}.Release|x86.Build.0 = Release|Any CPU - {A429F05B-F511-45EF-AE7B-04E1AE9C9367}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {A429F05B-F511-45EF-AE7B-04E1AE9C9367}.Debug|Any CPU.Build.0 = Debug|Any CPU - {A429F05B-F511-45EF-AE7B-04E1AE9C9367}.Debug|x64.ActiveCfg = Debug|Any CPU - {A429F05B-F511-45EF-AE7B-04E1AE9C9367}.Debug|x64.Build.0 = Debug|Any CPU - {A429F05B-F511-45EF-AE7B-04E1AE9C9367}.Debug|x86.ActiveCfg = Debug|Any CPU - {A429F05B-F511-45EF-AE7B-04E1AE9C9367}.Debug|x86.Build.0 = Debug|Any CPU - {A429F05B-F511-45EF-AE7B-04E1AE9C9367}.Release|Any CPU.ActiveCfg = Release|Any CPU - {A429F05B-F511-45EF-AE7B-04E1AE9C9367}.Release|Any CPU.Build.0 = Release|Any CPU - {A429F05B-F511-45EF-AE7B-04E1AE9C9367}.Release|x64.ActiveCfg = Release|Any CPU - {A429F05B-F511-45EF-AE7B-04E1AE9C9367}.Release|x64.Build.0 = Release|Any CPU - {A429F05B-F511-45EF-AE7B-04E1AE9C9367}.Release|x86.ActiveCfg = Release|Any CPU - {A429F05B-F511-45EF-AE7B-04E1AE9C9367}.Release|x86.Build.0 = Release|Any CPU - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection - GlobalSection(NestedProjects) = preSolution - {9F9A11BF-3C95-4E80-AFBF-768541996844} = {F51FC4DA-CAC0-48B1-A069-B1712BCAA5BE} - {75C2F9DC-3546-4D0A-A2DF-31C93516B6C1} = {F51FC4DA-CAC0-48B1-A069-B1712BCAA5BE} - {A429F05B-F511-45EF-AE7B-04E1AE9C9367} = {F51FC4DA-CAC0-48B1-A069-B1712BCAA5BE} - EndGlobalSection - GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {FD20BC4A-0109-41D8-8C0C-893E784D7EF9} - EndGlobalSection -EndGlobal diff --git a/lib/netcore/Thrift/Protocols/TAbstractBase.cs b/lib/netcore/Thrift/Protocols/TAbstractBase.cs deleted file mode 100644 index 4e18681bfb9..00000000000 --- a/lib/netcore/Thrift/Protocols/TAbstractBase.cs +++ /dev/null @@ -1,28 +0,0 @@ -// Licensed to the Apache Software Foundation(ASF) under one -// or more contributor license agreements.See the NOTICE file -// distributed with this work for additional information -// regarding copyright ownership.The ASF licenses this file -// to you under the Apache License, Version 2.0 (the -// "License"); you may not use this file except in compliance -// with the License. You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, -// software distributed under the License is distributed on an -// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -// KIND, either express or implied. See the License for the -// specific language governing permissions and limitations -// under the License. - -using System.Threading; -using System.Threading.Tasks; - -namespace Thrift.Protocols -{ - // ReSharper disable once InconsistentNaming - public interface TAbstractBase - { - Task WriteAsync(TProtocol tProtocol, CancellationToken cancellationToken); - } -} \ No newline at end of file diff --git a/lib/netcore/Thrift/Protocols/TBinaryProtocol.cs b/lib/netcore/Thrift/Protocols/TBinaryProtocol.cs deleted file mode 100644 index deec85c4245..00000000000 --- a/lib/netcore/Thrift/Protocols/TBinaryProtocol.cs +++ /dev/null @@ -1,613 +0,0 @@ -// Licensed to the Apache Software Foundation(ASF) under one -// or more contributor license agreements.See the NOTICE file -// distributed with this work for additional information -// regarding copyright ownership.The ASF licenses this file -// to you under the Apache License, Version 2.0 (the -// "License"); you may not use this file except in compliance -// with the License. You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, -// software distributed under the License is distributed on an -// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -// KIND, either express or implied. See the License for the -// specific language governing permissions and limitations -// under the License. - -using System; -using System.Text; -using System.Threading; -using System.Threading.Tasks; -using Thrift.Protocols.Entities; -using Thrift.Transports; - -namespace Thrift.Protocols -{ - // ReSharper disable once InconsistentNaming - public class TBinaryProtocol : TProtocol - { - //TODO: Unit tests - //TODO: Localization - //TODO: pragma - - protected const uint VersionMask = 0xffff0000; - protected const uint Version1 = 0x80010000; - - protected bool StrictRead; - protected bool StrictWrite; - - public TBinaryProtocol(TClientTransport trans) - : this(trans, false, true) - { - } - - public TBinaryProtocol(TClientTransport trans, bool strictRead, bool strictWrite) - : base(trans) - { - StrictRead = strictRead; - StrictWrite = strictWrite; - } - - public override async Task WriteMessageBeginAsync(TMessage message, CancellationToken cancellationToken) - { - if (cancellationToken.IsCancellationRequested) - { - return; - } - - if (StrictWrite) - { - var version = Version1 | (uint) message.Type; - await WriteI32Async((int) version, cancellationToken); - await WriteStringAsync(message.Name, cancellationToken); - await WriteI32Async(message.SeqID, cancellationToken); - } - else - { - await WriteStringAsync(message.Name, cancellationToken); - await WriteByteAsync((sbyte) message.Type, cancellationToken); - await WriteI32Async(message.SeqID, cancellationToken); - } - } - - public override async Task WriteMessageEndAsync(CancellationToken cancellationToken) - { - if (cancellationToken.IsCancellationRequested) - { - await Task.FromCanceled(cancellationToken); - } - } - - public override async Task WriteStructBeginAsync(TStruct @struct, CancellationToken cancellationToken) - { - if (cancellationToken.IsCancellationRequested) - { - await Task.FromCanceled(cancellationToken); - } - } - - public override async Task WriteStructEndAsync(CancellationToken cancellationToken) - { - if (cancellationToken.IsCancellationRequested) - { - await Task.FromCanceled(cancellationToken); - } - } - - public override async Task WriteFieldBeginAsync(TField field, CancellationToken cancellationToken) - { - if (cancellationToken.IsCancellationRequested) - { - return; - } - - await WriteByteAsync((sbyte) field.Type, cancellationToken); - await WriteI16Async(field.ID, cancellationToken); - } - - public override async Task WriteFieldEndAsync(CancellationToken cancellationToken) - { - if (cancellationToken.IsCancellationRequested) - { - await Task.FromCanceled(cancellationToken); - } - } - - public override async Task WriteFieldStopAsync(CancellationToken cancellationToken) - { - if (cancellationToken.IsCancellationRequested) - { - return; - } - - await WriteByteAsync((sbyte) TType.Stop, cancellationToken); - } - - public override async Task WriteMapBeginAsync(TMap map, CancellationToken cancellationToken) - { - if (cancellationToken.IsCancellationRequested) - { - return; - } - - await WriteByteAsync((sbyte) map.KeyType, cancellationToken); - await WriteByteAsync((sbyte) map.ValueType, cancellationToken); - await WriteI32Async(map.Count, cancellationToken); - } - - public override async Task WriteMapEndAsync(CancellationToken cancellationToken) - { - if (cancellationToken.IsCancellationRequested) - { - await Task.FromCanceled(cancellationToken); - } - } - - public override async Task WriteListBeginAsync(TList list, CancellationToken cancellationToken) - { - if (cancellationToken.IsCancellationRequested) - { - return; - } - - await WriteByteAsync((sbyte) list.ElementType, cancellationToken); - await WriteI32Async(list.Count, cancellationToken); - } - - public override async Task WriteListEndAsync(CancellationToken cancellationToken) - { - if (cancellationToken.IsCancellationRequested) - { - await Task.FromCanceled(cancellationToken); - } - } - - public override async Task WriteSetBeginAsync(TSet set, CancellationToken cancellationToken) - { - if (cancellationToken.IsCancellationRequested) - { - return; - } - - await WriteByteAsync((sbyte) set.ElementType, cancellationToken); - await WriteI32Async(set.Count, cancellationToken); - } - - public override async Task WriteSetEndAsync(CancellationToken cancellationToken) - { - if (cancellationToken.IsCancellationRequested) - { - await Task.FromCanceled(cancellationToken); - } - } - - public override async Task WriteBoolAsync(bool b, CancellationToken cancellationToken) - { - if (cancellationToken.IsCancellationRequested) - { - return; - } - - await WriteByteAsync(b ? (sbyte) 1 : (sbyte) 0, cancellationToken); - } - - protected internal static byte[] CreateWriteByte(sbyte b) - { - var bout = new byte[1]; - - bout[0] = (byte) b; - - return bout; - } - - public override async Task WriteByteAsync(sbyte b, CancellationToken cancellationToken) - { - if (cancellationToken.IsCancellationRequested) - { - return; - } - - var bout = CreateWriteByte(b); - await Trans.WriteAsync(bout, 0, 1, cancellationToken); - } - - protected internal static byte[] CreateWriteI16(short s) - { - var i16Out = new byte[2]; - - i16Out[0] = (byte) (0xff & (s >> 8)); - i16Out[1] = (byte) (0xff & s); - - return i16Out; - } - - public override async Task WriteI16Async(short i16, CancellationToken cancellationToken) - { - if (cancellationToken.IsCancellationRequested) - { - return; - } - - var i16Out = CreateWriteI16(i16); - await Trans.WriteAsync(i16Out, 0, 2, cancellationToken); - } - - protected internal static byte[] CreateWriteI32(int i32) - { - var i32Out = new byte[4]; - - i32Out[0] = (byte) (0xff & (i32 >> 24)); - i32Out[1] = (byte) (0xff & (i32 >> 16)); - i32Out[2] = (byte) (0xff & (i32 >> 8)); - i32Out[3] = (byte) (0xff & i32); - - return i32Out; - } - - public override async Task WriteI32Async(int i32, CancellationToken cancellationToken) - { - if (cancellationToken.IsCancellationRequested) - { - return; - } - - var i32Out = CreateWriteI32(i32); - await Trans.WriteAsync(i32Out, 0, 4, cancellationToken); - } - - protected internal static byte[] CreateWriteI64(long i64) - { - var i64Out = new byte[8]; - - i64Out[0] = (byte) (0xff & (i64 >> 56)); - i64Out[1] = (byte) (0xff & (i64 >> 48)); - i64Out[2] = (byte) (0xff & (i64 >> 40)); - i64Out[3] = (byte) (0xff & (i64 >> 32)); - i64Out[4] = (byte) (0xff & (i64 >> 24)); - i64Out[5] = (byte) (0xff & (i64 >> 16)); - i64Out[6] = (byte) (0xff & (i64 >> 8)); - i64Out[7] = (byte) (0xff & i64); - - return i64Out; - } - - public override async Task WriteI64Async(long i64, CancellationToken cancellationToken) - { - if (cancellationToken.IsCancellationRequested) - { - return; - } - - var i64Out = CreateWriteI64(i64); - await Trans.WriteAsync(i64Out, 0, 8, cancellationToken); - } - - public override async Task WriteDoubleAsync(double d, CancellationToken cancellationToken) - { - if (cancellationToken.IsCancellationRequested) - { - return; - } - - await WriteI64Async(BitConverter.DoubleToInt64Bits(d), cancellationToken); - } - - public override async Task WriteBinaryAsync(byte[] bytes, CancellationToken cancellationToken) - { - if (cancellationToken.IsCancellationRequested) - { - return; - } - - await WriteI32Async(bytes.Length, cancellationToken); - await Trans.WriteAsync(bytes, 0, bytes.Length, cancellationToken); - } - - public override async Task ReadMessageBeginAsync(CancellationToken cancellationToken) - { - if (cancellationToken.IsCancellationRequested) - { - return await Task.FromCanceled(cancellationToken); - } - - var message = new TMessage(); - var size = await ReadI32Async(cancellationToken); - if (size < 0) - { - var version = (uint) size & VersionMask; - if (version != Version1) - { - throw new TProtocolException(TProtocolException.BAD_VERSION, - $"Bad version in ReadMessageBegin: {version}"); - } - message.Type = (TMessageType) (size & 0x000000ff); - message.Name = await ReadStringAsync(cancellationToken); - message.SeqID = await ReadI32Async(cancellationToken); - } - else - { - if (StrictRead) - { - throw new TProtocolException(TProtocolException.BAD_VERSION, - "Missing version in ReadMessageBegin, old client?"); - } - message.Name = await ReadStringBodyAsync(size, cancellationToken); - message.Type = (TMessageType) await ReadByteAsync(cancellationToken); - message.SeqID = await ReadI32Async(cancellationToken); - } - return message; - } - - public override async Task ReadMessageEndAsync(CancellationToken cancellationToken) - { - if (cancellationToken.IsCancellationRequested) - { - await Task.FromCanceled(cancellationToken); - } - } - - public override async Task ReadStructBeginAsync(CancellationToken cancellationToken) - { - if (cancellationToken.IsCancellationRequested) - { - await Task.FromCanceled(cancellationToken); - } - - //TODO: no read from internal transport? - return new TStruct(); - } - - public override async Task ReadStructEndAsync(CancellationToken cancellationToken) - { - if (cancellationToken.IsCancellationRequested) - { - await Task.FromCanceled(cancellationToken); - } - } - - public override async Task ReadFieldBeginAsync(CancellationToken cancellationToken) - { - if (cancellationToken.IsCancellationRequested) - { - return await Task.FromCanceled(cancellationToken); - } - - var field = new TField - { - Type = (TType) await ReadByteAsync(cancellationToken) - }; - - if (field.Type != TType.Stop) - { - field.ID = await ReadI16Async(cancellationToken); - } - - return field; - } - - public override async Task ReadFieldEndAsync(CancellationToken cancellationToken) - { - if (cancellationToken.IsCancellationRequested) - { - await Task.FromCanceled(cancellationToken); - } - } - - public override async Task ReadMapBeginAsync(CancellationToken cancellationToken) - { - if (cancellationToken.IsCancellationRequested) - { - return await Task.FromCanceled(cancellationToken); - } - - var map = new TMap - { - KeyType = (TType) await ReadByteAsync(cancellationToken), - ValueType = (TType) await ReadByteAsync(cancellationToken), - Count = await ReadI32Async(cancellationToken) - }; - - return map; - } - - public override async Task ReadMapEndAsync(CancellationToken cancellationToken) - { - if (cancellationToken.IsCancellationRequested) - { - await Task.FromCanceled(cancellationToken); - } - } - - public override async Task ReadListBeginAsync(CancellationToken cancellationToken) - { - if (cancellationToken.IsCancellationRequested) - { - return await Task.FromCanceled(cancellationToken); - } - - var list = new TList - { - ElementType = (TType) await ReadByteAsync(cancellationToken), - Count = await ReadI32Async(cancellationToken) - }; - - return list; - } - - public override async Task ReadListEndAsync(CancellationToken cancellationToken) - { - if (cancellationToken.IsCancellationRequested) - { - await Task.FromCanceled(cancellationToken); - } - } - - public override async Task ReadSetBeginAsync(CancellationToken cancellationToken) - { - if (cancellationToken.IsCancellationRequested) - { - return await Task.FromCanceled(cancellationToken); - } - - var set = new TSet - { - ElementType = (TType) await ReadByteAsync(cancellationToken), - Count = await ReadI32Async(cancellationToken) - }; - - return set; - } - - public override async Task ReadSetEndAsync(CancellationToken cancellationToken) - { - if (cancellationToken.IsCancellationRequested) - { - await Task.FromCanceled(cancellationToken); - } - } - - public override async Task ReadBoolAsync(CancellationToken cancellationToken) - { - if (cancellationToken.IsCancellationRequested) - { - return await Task.FromCanceled(cancellationToken); - } - - return await ReadByteAsync(cancellationToken) == 1; - } - - public override async Task ReadByteAsync(CancellationToken cancellationToken) - { - if (cancellationToken.IsCancellationRequested) - { - return await Task.FromCanceled(cancellationToken); - } - - var bin = new byte[1]; - await Trans.ReadAllAsync(bin, 0, 1, cancellationToken); //TODO: why readall ? - return (sbyte) bin[0]; - } - - public override async Task ReadI16Async(CancellationToken cancellationToken) - { - if (cancellationToken.IsCancellationRequested) - { - return await Task.FromCanceled(cancellationToken); - } - - var i16In = new byte[2]; - await Trans.ReadAllAsync(i16In, 0, 2, cancellationToken); - var result = (short) (((i16In[0] & 0xff) << 8) | i16In[1] & 0xff); - return result; - } - - public override async Task ReadI32Async(CancellationToken cancellationToken) - { - if (cancellationToken.IsCancellationRequested) - { - return await Task.FromCanceled(cancellationToken); - } - - var i32In = new byte[4]; - await Trans.ReadAllAsync(i32In, 0, 4, cancellationToken); - - var result = - ((i32In[0] & 0xff) << 24) | - ((i32In[1] & 0xff) << 16) | - ((i32In[2] & 0xff) << 8) | - i32In[3] & 0xff; - - return result; - } - -#pragma warning disable 675 - - protected internal long CreateReadI64(byte[] buf) - { - var result = - ((long) (buf[0] & 0xff) << 56) | - ((long) (buf[1] & 0xff) << 48) | - ((long) (buf[2] & 0xff) << 40) | - ((long) (buf[3] & 0xff) << 32) | - ((long) (buf[4] & 0xff) << 24) | - ((long) (buf[5] & 0xff) << 16) | - ((long) (buf[6] & 0xff) << 8) | - buf[7] & 0xff; - - return result; - } - -#pragma warning restore 675 - - public override async Task ReadI64Async(CancellationToken cancellationToken) - { - if (cancellationToken.IsCancellationRequested) - { - return await Task.FromCanceled(cancellationToken); - } - - var i64In = new byte[8]; - await Trans.ReadAllAsync(i64In, 0, 8, cancellationToken); - return CreateReadI64(i64In); - } - - public override async Task ReadDoubleAsync(CancellationToken cancellationToken) - { - if (cancellationToken.IsCancellationRequested) - { - return await Task.FromCanceled(cancellationToken); - } - - var d = await ReadI64Async(cancellationToken); - return BitConverter.Int64BitsToDouble(d); - } - - public override async Task ReadBinaryAsync(CancellationToken cancellationToken) - { - if (cancellationToken.IsCancellationRequested) - { - return await Task.FromCanceled(cancellationToken); - } - - var size = await ReadI32Async(cancellationToken); - var buf = new byte[size]; - await Trans.ReadAllAsync(buf, 0, size, cancellationToken); - return buf; - } - - private async Task ReadStringBodyAsync(int size, CancellationToken cancellationToken) - { - if (cancellationToken.IsCancellationRequested) - { - await Task.FromCanceled(cancellationToken); - } - - var buf = new byte[size]; - await Trans.ReadAllAsync(buf, 0, size, cancellationToken); - return Encoding.UTF8.GetString(buf, 0, buf.Length); - } - - public class Factory : ITProtocolFactory - { - protected bool StrictRead; - protected bool StrictWrite; - - public Factory() - : this(false, true) - { - } - - public Factory(bool strictRead, bool strictWrite) - { - StrictRead = strictRead; - StrictWrite = strictWrite; - } - - public TProtocol GetProtocol(TClientTransport trans) - { - return new TBinaryProtocol(trans, StrictRead, StrictWrite); - } - } - } -} \ No newline at end of file diff --git a/lib/netcore/Thrift/Protocols/TCompactProtocol.cs b/lib/netcore/Thrift/Protocols/TCompactProtocol.cs deleted file mode 100644 index cecdf03ccb8..00000000000 --- a/lib/netcore/Thrift/Protocols/TCompactProtocol.cs +++ /dev/null @@ -1,922 +0,0 @@ -// Licensed to the Apache Software Foundation(ASF) under one -// or more contributor license agreements.See the NOTICE file -// distributed with this work for additional information -// regarding copyright ownership.The ASF licenses this file -// to you under the Apache License, Version 2.0 (the -// "License"); you may not use this file except in compliance -// with the License. You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, -// software distributed under the License is distributed on an -// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -// KIND, either express or implied. See the License for the -// specific language governing permissions and limitations -// under the License. - -using System; -using System.Collections.Generic; -using System.Text; -using System.Threading; -using System.Threading.Tasks; -using Thrift.Protocols.Entities; -using Thrift.Transports; - -namespace Thrift.Protocols -{ - //TODO: implementation of TProtocol - - // ReSharper disable once InconsistentNaming - public class TCompactProtocol : TProtocol - { - private const byte ProtocolId = 0x82; - private const byte Version = 1; - private const byte VersionMask = 0x1f; // 0001 1111 - private const byte TypeMask = 0xE0; // 1110 0000 - private const byte TypeBits = 0x07; // 0000 0111 - private const int TypeShiftAmount = 5; - private static readonly TStruct AnonymousStruct = new TStruct(string.Empty); - private static readonly TField Tstop = new TField(string.Empty, TType.Stop, 0); - - // ReSharper disable once InconsistentNaming - private static readonly byte[] TTypeToCompactType = new byte[16]; - - /// - /// Used to keep track of the last field for the current and previous structs, so we can do the delta stuff. - /// - private readonly Stack _lastField = new Stack(15); - - /// - /// If we encounter a boolean field begin, save the TField here so it can have the value incorporated. - /// - private TField? _booleanField; - - /// - /// If we Read a field header, and it's a boolean field, save the boolean value here so that ReadBool can use it. - /// - private bool? _boolValue; - - private short _lastFieldId; - - public TCompactProtocol(TClientTransport trans) - : base(trans) - { - TTypeToCompactType[(int) TType.Stop] = Types.Stop; - TTypeToCompactType[(int) TType.Bool] = Types.BooleanTrue; - TTypeToCompactType[(int) TType.Byte] = Types.Byte; - TTypeToCompactType[(int) TType.I16] = Types.I16; - TTypeToCompactType[(int) TType.I32] = Types.I32; - TTypeToCompactType[(int) TType.I64] = Types.I64; - TTypeToCompactType[(int) TType.Double] = Types.Double; - TTypeToCompactType[(int) TType.String] = Types.Binary; - TTypeToCompactType[(int) TType.List] = Types.List; - TTypeToCompactType[(int) TType.Set] = Types.Set; - TTypeToCompactType[(int) TType.Map] = Types.Map; - TTypeToCompactType[(int) TType.Struct] = Types.Struct; - } - - public void Reset() - { - _lastField.Clear(); - _lastFieldId = 0; - } - - public override async Task WriteMessageBeginAsync(TMessage message, CancellationToken cancellationToken) - { - if (cancellationToken.IsCancellationRequested) - { - return; - } - - await Trans.WriteAsync(new[] {ProtocolId}, cancellationToken); - await - Trans.WriteAsync( - new[] {(byte) ((Version & VersionMask) | (((uint) message.Type << TypeShiftAmount) & TypeMask))}, - cancellationToken); - - var bufferTuple = CreateWriteVarInt32((uint) message.SeqID); - await Trans.WriteAsync(bufferTuple.Item1, 0, bufferTuple.Item2, cancellationToken); - - await WriteStringAsync(message.Name, cancellationToken); - } - - public override async Task WriteMessageEndAsync(CancellationToken cancellationToken) - { - if (cancellationToken.IsCancellationRequested) - { - await Task.FromCanceled(cancellationToken); - } - } - - /// - /// Write a struct begin. This doesn't actually put anything on the wire. We - /// use it as an opportunity to put special placeholder markers on the field - /// stack so we can get the field id deltas correct. - /// - public override async Task WriteStructBeginAsync(TStruct @struct, CancellationToken cancellationToken) - { - if (cancellationToken.IsCancellationRequested) - { - await Task.FromCanceled(cancellationToken); - } - - _lastField.Push(_lastFieldId); - _lastFieldId = 0; - } - - public override async Task WriteStructEndAsync(CancellationToken cancellationToken) - { - if (cancellationToken.IsCancellationRequested) - { - await Task.FromCanceled(cancellationToken); - } - - _lastFieldId = _lastField.Pop(); - } - - private async Task WriteFieldBeginInternalAsync(TField field, byte typeOverride, - CancellationToken cancellationToken) - { - // if there's a exType override, use that. - var typeToWrite = typeOverride == 0xFF ? GetCompactType(field.Type) : typeOverride; - - // check if we can use delta encoding for the field id - if ((field.ID > _lastFieldId) && (field.ID - _lastFieldId <= 15)) - { - var b = (byte) (((field.ID - _lastFieldId) << 4) | typeToWrite); - // Write them together - await Trans.WriteAsync(new[] {b}, cancellationToken); - } - else - { - // Write them separate - await Trans.WriteAsync(new[] {typeToWrite}, cancellationToken); - await WriteI16Async(field.ID, cancellationToken); - } - - _lastFieldId = field.ID; - } - - public override async Task WriteFieldBeginAsync(TField field, CancellationToken cancellationToken) - { - if (field.Type == TType.Bool) - { - _booleanField = field; - } - else - { - await WriteFieldBeginInternalAsync(field, 0xFF, cancellationToken); - } - } - - public override async Task WriteFieldEndAsync(CancellationToken cancellationToken) - { - if (cancellationToken.IsCancellationRequested) - { - await Task.FromCanceled(cancellationToken); - } - } - - public override async Task WriteFieldStopAsync(CancellationToken cancellationToken) - { - if (cancellationToken.IsCancellationRequested) - { - return; - } - - await Trans.WriteAsync(new[] {Types.Stop}, cancellationToken); - } - - protected async Task WriteCollectionBeginAsync(TType elemType, int size, CancellationToken cancellationToken) - { - if (cancellationToken.IsCancellationRequested) - { - return; - } - - /* - Abstract method for writing the start of lists and sets. List and sets on - the wire differ only by the exType indicator. - */ - - if (size <= 14) - { - await Trans.WriteAsync(new[] {(byte) ((size << 4) | GetCompactType(elemType))}, cancellationToken); - } - else - { - await Trans.WriteAsync(new[] {(byte) (0xf0 | GetCompactType(elemType))}, cancellationToken); - - var bufferTuple = CreateWriteVarInt32((uint) size); - await Trans.WriteAsync(bufferTuple.Item1, 0, bufferTuple.Item2, cancellationToken); - } - } - - public override async Task WriteListBeginAsync(TList list, CancellationToken cancellationToken) - { - await WriteCollectionBeginAsync(list.ElementType, list.Count, cancellationToken); - } - - public override async Task WriteListEndAsync(CancellationToken cancellationToken) - { - if (cancellationToken.IsCancellationRequested) - { - await Task.FromCanceled(cancellationToken); - } - } - - public override async Task WriteSetBeginAsync(TSet set, CancellationToken cancellationToken) - { - if (cancellationToken.IsCancellationRequested) - { - return; - } - - await WriteCollectionBeginAsync(set.ElementType, set.Count, cancellationToken); - } - - public override async Task WriteSetEndAsync(CancellationToken cancellationToken) - { - if (cancellationToken.IsCancellationRequested) - { - await Task.FromCanceled(cancellationToken); - } - } - - public override async Task WriteBoolAsync(bool b, CancellationToken cancellationToken) - { - if (cancellationToken.IsCancellationRequested) - { - return; - } - - /* - Write a boolean value. Potentially, this could be a boolean field, in - which case the field header info isn't written yet. If so, decide what the - right exType header is for the value and then Write the field header. - Otherwise, Write a single byte. - */ - - if (_booleanField != null) - { - // we haven't written the field header yet - await - WriteFieldBeginInternalAsync(_booleanField.Value, b ? Types.BooleanTrue : Types.BooleanFalse, - cancellationToken); - _booleanField = null; - } - else - { - // we're not part of a field, so just Write the value. - await Trans.WriteAsync(new[] {b ? Types.BooleanTrue : Types.BooleanFalse}, cancellationToken); - } - } - - public override async Task WriteByteAsync(sbyte b, CancellationToken cancellationToken) - { - if (cancellationToken.IsCancellationRequested) - { - return; - } - - await Trans.WriteAsync(new[] {(byte) b}, cancellationToken); - } - - public override async Task WriteI16Async(short i16, CancellationToken cancellationToken) - { - if (cancellationToken.IsCancellationRequested) - { - return; - } - - var bufferTuple = CreateWriteVarInt32(IntToZigzag(i16)); - await Trans.WriteAsync(bufferTuple.Item1, 0, bufferTuple.Item2, cancellationToken); - } - - protected internal Tuple CreateWriteVarInt32(uint n) - { - // Write an i32 as a varint.Results in 1 - 5 bytes on the wire. - var i32Buf = new byte[5]; - var idx = 0; - - while (true) - { - if ((n & ~0x7F) == 0) - { - i32Buf[idx++] = (byte) n; - break; - } - - i32Buf[idx++] = (byte) ((n & 0x7F) | 0x80); - n >>= 7; - } - - return new Tuple(i32Buf, idx); - } - - public override async Task WriteI32Async(int i32, CancellationToken cancellationToken) - { - if (cancellationToken.IsCancellationRequested) - { - return; - } - - var bufferTuple = CreateWriteVarInt32(IntToZigzag(i32)); - await Trans.WriteAsync(bufferTuple.Item1, 0, bufferTuple.Item2, cancellationToken); - } - - protected internal Tuple CreateWriteVarInt64(ulong n) - { - // Write an i64 as a varint. Results in 1-10 bytes on the wire. - var buf = new byte[10]; - var idx = 0; - - while (true) - { - if ((n & ~(ulong) 0x7FL) == 0) - { - buf[idx++] = (byte) n; - break; - } - buf[idx++] = (byte) ((n & 0x7F) | 0x80); - n >>= 7; - } - - return new Tuple(buf, idx); - } - - public override async Task WriteI64Async(long i64, CancellationToken cancellationToken) - { - if (cancellationToken.IsCancellationRequested) - { - return; - } - - var bufferTuple = CreateWriteVarInt64(LongToZigzag(i64)); - await Trans.WriteAsync(bufferTuple.Item1, 0, bufferTuple.Item2, cancellationToken); - } - - public override async Task WriteDoubleAsync(double d, CancellationToken cancellationToken) - { - if (cancellationToken.IsCancellationRequested) - { - return; - } - - var data = new byte[8]; - FixedLongToBytes(BitConverter.DoubleToInt64Bits(d), data, 0); - await Trans.WriteAsync(data, cancellationToken); - } - - public override async Task WriteStringAsync(string str, CancellationToken cancellationToken) - { - if (cancellationToken.IsCancellationRequested) - { - return; - } - - var bytes = Encoding.UTF8.GetBytes(str); - - var bufferTuple = CreateWriteVarInt32((uint) bytes.Length); - await Trans.WriteAsync(bufferTuple.Item1, 0, bufferTuple.Item2, cancellationToken); - await Trans.WriteAsync(bytes, 0, bytes.Length, cancellationToken); - } - - public override async Task WriteBinaryAsync(byte[] bytes, CancellationToken cancellationToken) - { - if (cancellationToken.IsCancellationRequested) - { - return; - } - - var bufferTuple = CreateWriteVarInt32((uint) bytes.Length); - await Trans.WriteAsync(bufferTuple.Item1, 0, bufferTuple.Item2, cancellationToken); - await Trans.WriteAsync(bytes, 0, bytes.Length, cancellationToken); - } - - public override async Task WriteMapBeginAsync(TMap map, CancellationToken cancellationToken) - { - if (cancellationToken.IsCancellationRequested) - { - return; - } - - if (map.Count == 0) - { - await Trans.WriteAsync(new[] {(byte) 0}, cancellationToken); - } - else - { - var bufferTuple = CreateWriteVarInt32((uint) map.Count); - await Trans.WriteAsync(bufferTuple.Item1, 0, bufferTuple.Item2, cancellationToken); - await - Trans.WriteAsync( - new[] {(byte) ((GetCompactType(map.KeyType) << 4) | GetCompactType(map.ValueType))}, - cancellationToken); - } - } - - public override async Task WriteMapEndAsync(CancellationToken cancellationToken) - { - if (cancellationToken.IsCancellationRequested) - { - await Task.FromCanceled(cancellationToken); - } - } - - public override async Task ReadMessageBeginAsync(CancellationToken cancellationToken) - { - if (cancellationToken.IsCancellationRequested) - { - return await Task.FromCanceled(cancellationToken); - } - - var protocolId = (byte) await ReadByteAsync(cancellationToken); - if (protocolId != ProtocolId) - { - throw new TProtocolException($"Expected protocol id {ProtocolId:X} but got {protocolId:X}"); - } - - var versionAndType = (byte) await ReadByteAsync(cancellationToken); - var version = (byte) (versionAndType & VersionMask); - - if (version != Version) - { - throw new TProtocolException($"Expected version {Version} but got {version}"); - } - - var type = (byte) ((versionAndType >> TypeShiftAmount) & TypeBits); - var seqid = (int) await ReadVarInt32Async(cancellationToken); - var messageName = await ReadStringAsync(cancellationToken); - - return new TMessage(messageName, (TMessageType) type, seqid); - } - - public override async Task ReadMessageEndAsync(CancellationToken cancellationToken) - { - if (cancellationToken.IsCancellationRequested) - { - await Task.FromCanceled(cancellationToken); - } - } - - public override async Task ReadStructBeginAsync(CancellationToken cancellationToken) - { - if (cancellationToken.IsCancellationRequested) - { - return await Task.FromCanceled(cancellationToken); - } - - // some magic is here ) - - _lastField.Push(_lastFieldId); - _lastFieldId = 0; - - return AnonymousStruct; - } - - public override async Task ReadStructEndAsync(CancellationToken cancellationToken) - { - if (cancellationToken.IsCancellationRequested) - { - await Task.FromCanceled(cancellationToken); - } - - /* - Doesn't actually consume any wire data, just removes the last field for - this struct from the field stack. - */ - - // consume the last field we Read off the wire. - _lastFieldId = _lastField.Pop(); - } - - public override async Task ReadFieldBeginAsync(CancellationToken cancellationToken) - { - // Read a field header off the wire. - var type = (byte) await ReadByteAsync(cancellationToken); - // if it's a stop, then we can return immediately, as the struct is over. - if (type == Types.Stop) - { - return Tstop; - } - - short fieldId; - // mask off the 4 MSB of the exType header. it could contain a field id delta. - var modifier = (short) ((type & 0xf0) >> 4); - if (modifier == 0) - { - fieldId = await ReadI16Async(cancellationToken); - } - else - { - fieldId = (short) (_lastFieldId + modifier); - } - - var field = new TField(string.Empty, GetTType((byte) (type & 0x0f)), fieldId); - // if this happens to be a boolean field, the value is encoded in the exType - if (IsBoolType(type)) - { - _boolValue = (byte) (type & 0x0f) == Types.BooleanTrue; - } - - // push the new field onto the field stack so we can keep the deltas going. - _lastFieldId = field.ID; - return field; - } - - public override async Task ReadFieldEndAsync(CancellationToken cancellationToken) - { - if (cancellationToken.IsCancellationRequested) - { - await Task.FromCanceled(cancellationToken); - } - } - - public override async Task ReadMapBeginAsync(CancellationToken cancellationToken) - { - if (cancellationToken.IsCancellationRequested) - { - await Task.FromCanceled(cancellationToken); - } - - /* - Read a map header off the wire. If the size is zero, skip Reading the key - and value exType. This means that 0-length maps will yield TMaps without the - "correct" types. - */ - - var size = (int) await ReadVarInt32Async(cancellationToken); - var keyAndValueType = size == 0 ? (byte) 0 : (byte) await ReadByteAsync(cancellationToken); - return new TMap(GetTType((byte) (keyAndValueType >> 4)), GetTType((byte) (keyAndValueType & 0xf)), size); - } - - public override async Task ReadMapEndAsync(CancellationToken cancellationToken) - { - if (cancellationToken.IsCancellationRequested) - { - await Task.FromCanceled(cancellationToken); - } - } - - public override async Task ReadSetBeginAsync(CancellationToken cancellationToken) - { - /* - Read a set header off the wire. If the set size is 0-14, the size will - be packed into the element exType header. If it's a longer set, the 4 MSB - of the element exType header will be 0xF, and a varint will follow with the - true size. - */ - - return new TSet(await ReadListBeginAsync(cancellationToken)); - } - - public override async Task ReadBoolAsync(CancellationToken cancellationToken) - { - if (cancellationToken.IsCancellationRequested) - { - return await Task.FromCanceled(cancellationToken); - } - - /* - Read a boolean off the wire. If this is a boolean field, the value should - already have been Read during ReadFieldBegin, so we'll just consume the - pre-stored value. Otherwise, Read a byte. - */ - - if (_boolValue != null) - { - var result = _boolValue.Value; - _boolValue = null; - return result; - } - - return await ReadByteAsync(cancellationToken) == Types.BooleanTrue; - } - - public override async Task ReadByteAsync(CancellationToken cancellationToken) - { - if (cancellationToken.IsCancellationRequested) - { - return await Task.FromCanceled(cancellationToken); - } - - // Read a single byte off the wire. Nothing interesting here. - var buf = new byte[1]; - await Trans.ReadAllAsync(buf, 0, 1, cancellationToken); - return (sbyte) buf[0]; - } - - public override async Task ReadI16Async(CancellationToken cancellationToken) - { - if (cancellationToken.IsCancellationRequested) - { - return await Task.FromCanceled(cancellationToken); - } - - return (short) ZigzagToInt(await ReadVarInt32Async(cancellationToken)); - } - - public override async Task ReadI32Async(CancellationToken cancellationToken) - { - if (cancellationToken.IsCancellationRequested) - { - return await Task.FromCanceled(cancellationToken); - } - - return ZigzagToInt(await ReadVarInt32Async(cancellationToken)); - } - - public override async Task ReadI64Async(CancellationToken cancellationToken) - { - if (cancellationToken.IsCancellationRequested) - { - return await Task.FromCanceled(cancellationToken); - } - - return ZigzagToLong(await ReadVarInt64Async(cancellationToken)); - } - - public override async Task ReadDoubleAsync(CancellationToken cancellationToken) - { - if (cancellationToken.IsCancellationRequested) - { - return await Task.FromCanceled(cancellationToken); - } - - var longBits = new byte[8]; - await Trans.ReadAllAsync(longBits, 0, 8, cancellationToken); - - return BitConverter.Int64BitsToDouble(BytesToLong(longBits)); - } - - public override async Task ReadStringAsync(CancellationToken cancellationToken) - { - if (cancellationToken.IsCancellationRequested) - { - await Task.FromCanceled(cancellationToken); - } - - // Reads a byte[] (via ReadBinary), and then UTF-8 decodes it. - var length = (int) await ReadVarInt32Async(cancellationToken); - - if (length == 0) - { - return string.Empty; - } - - var buf = new byte[length]; - await Trans.ReadAllAsync(buf, 0, length, cancellationToken); - - return Encoding.UTF8.GetString(buf); - } - - public override async Task ReadBinaryAsync(CancellationToken cancellationToken) - { - if (cancellationToken.IsCancellationRequested) - { - return await Task.FromCanceled(cancellationToken); - } - - // Read a byte[] from the wire. - var length = (int) await ReadVarInt32Async(cancellationToken); - if (length == 0) - { - return new byte[0]; - } - - var buf = new byte[length]; - await Trans.ReadAllAsync(buf, 0, length, cancellationToken); - return buf; - } - - public override async Task ReadListBeginAsync(CancellationToken cancellationToken) - { - if (cancellationToken.IsCancellationRequested) - { - await Task.FromCanceled(cancellationToken); - } - - /* - Read a list header off the wire. If the list size is 0-14, the size will - be packed into the element exType header. If it's a longer list, the 4 MSB - of the element exType header will be 0xF, and a varint will follow with the - true size. - */ - - var sizeAndType = (byte) await ReadByteAsync(cancellationToken); - var size = (sizeAndType >> 4) & 0x0f; - if (size == 15) - { - size = (int) await ReadVarInt32Async(cancellationToken); - } - - var type = GetTType(sizeAndType); - return new TList(type, size); - } - - public override async Task ReadListEndAsync(CancellationToken cancellationToken) - { - if (cancellationToken.IsCancellationRequested) - { - await Task.FromCanceled(cancellationToken); - } - } - - public override async Task ReadSetEndAsync(CancellationToken cancellationToken) - { - if (cancellationToken.IsCancellationRequested) - { - await Task.FromCanceled(cancellationToken); - } - } - - private static byte GetCompactType(TType ttype) - { - // Given a TType value, find the appropriate TCompactProtocol.Types constant. - return TTypeToCompactType[(int) ttype]; - } - - - private async Task ReadVarInt32Async(CancellationToken cancellationToken) - { - if (cancellationToken.IsCancellationRequested) - { - return await Task.FromCanceled(cancellationToken); - } - - /* - Read an i32 from the wire as a varint. The MSB of each byte is set - if there is another byte to follow. This can Read up to 5 bytes. - */ - - uint result = 0; - var shift = 0; - - while (true) - { - var b = (byte) await ReadByteAsync(cancellationToken); - result |= (uint) (b & 0x7f) << shift; - if ((b & 0x80) != 0x80) - { - break; - } - shift += 7; - } - - return result; - } - - private async Task ReadVarInt64Async(CancellationToken cancellationToken) - { - if (cancellationToken.IsCancellationRequested) - { - return await Task.FromCanceled(cancellationToken); - } - - /* - Read an i64 from the wire as a proper varint. The MSB of each byte is set - if there is another byte to follow. This can Read up to 10 bytes. - */ - - var shift = 0; - ulong result = 0; - while (true) - { - var b = (byte) await ReadByteAsync(cancellationToken); - result |= (ulong) (b & 0x7f) << shift; - if ((b & 0x80) != 0x80) - { - break; - } - shift += 7; - } - - return result; - } - - private static int ZigzagToInt(uint n) - { - return (int) (n >> 1) ^ -(int) (n & 1); - } - - private static long ZigzagToLong(ulong n) - { - return (long) (n >> 1) ^ -(long) (n & 1); - } - - private static long BytesToLong(byte[] bytes) - { - /* - Note that it's important that the mask bytes are long literals, - otherwise they'll default to ints, and when you shift an int left 56 bits, - you just get a messed up int. - */ - - return - ((bytes[7] & 0xffL) << 56) | - ((bytes[6] & 0xffL) << 48) | - ((bytes[5] & 0xffL) << 40) | - ((bytes[4] & 0xffL) << 32) | - ((bytes[3] & 0xffL) << 24) | - ((bytes[2] & 0xffL) << 16) | - ((bytes[1] & 0xffL) << 8) | - (bytes[0] & 0xffL); - } - - private static bool IsBoolType(byte b) - { - var lowerNibble = b & 0x0f; - return (lowerNibble == Types.BooleanTrue) || (lowerNibble == Types.BooleanFalse); - } - - private static TType GetTType(byte type) - { - // Given a TCompactProtocol.Types constant, convert it to its corresponding TType value. - switch ((byte) (type & 0x0f)) - { - case Types.Stop: - return TType.Stop; - case Types.BooleanFalse: - case Types.BooleanTrue: - return TType.Bool; - case Types.Byte: - return TType.Byte; - case Types.I16: - return TType.I16; - case Types.I32: - return TType.I32; - case Types.I64: - return TType.I64; - case Types.Double: - return TType.Double; - case Types.Binary: - return TType.String; - case Types.List: - return TType.List; - case Types.Set: - return TType.Set; - case Types.Map: - return TType.Map; - case Types.Struct: - return TType.Struct; - default: - throw new TProtocolException($"Don't know what exType: {(byte) (type & 0x0f)}"); - } - } - - private static ulong LongToZigzag(long n) - { - // Convert l into a zigzag long. This allows negative numbers to be represented compactly as a varint - return (ulong) (n << 1) ^ (ulong) (n >> 63); - } - - private static uint IntToZigzag(int n) - { - // Convert n into a zigzag int. This allows negative numbers to be represented compactly as a varint - return (uint) (n << 1) ^ (uint) (n >> 31); - } - - private static void FixedLongToBytes(long n, byte[] buf, int off) - { - // Convert a long into little-endian bytes in buf starting at off and going until off+7. - buf[off + 0] = (byte) (n & 0xff); - buf[off + 1] = (byte) ((n >> 8) & 0xff); - buf[off + 2] = (byte) ((n >> 16) & 0xff); - buf[off + 3] = (byte) ((n >> 24) & 0xff); - buf[off + 4] = (byte) ((n >> 32) & 0xff); - buf[off + 5] = (byte) ((n >> 40) & 0xff); - buf[off + 6] = (byte) ((n >> 48) & 0xff); - buf[off + 7] = (byte) ((n >> 56) & 0xff); - } - - public class Factory : ITProtocolFactory - { - public TProtocol GetProtocol(TClientTransport trans) - { - return new TCompactProtocol(trans); - } - } - - /// - /// All of the on-wire exType codes. - /// - private static class Types - { - public const byte Stop = 0x00; - public const byte BooleanTrue = 0x01; - public const byte BooleanFalse = 0x02; - public const byte Byte = 0x03; - public const byte I16 = 0x04; - public const byte I32 = 0x05; - public const byte I64 = 0x06; - public const byte Double = 0x07; - public const byte Binary = 0x08; - public const byte List = 0x09; - public const byte Set = 0x0A; - public const byte Map = 0x0B; - public const byte Struct = 0x0C; - } - } -} \ No newline at end of file diff --git a/lib/netcore/Thrift/Protocols/TProtocol.cs b/lib/netcore/Thrift/Protocols/TProtocol.cs deleted file mode 100644 index 91e009d639b..00000000000 --- a/lib/netcore/Thrift/Protocols/TProtocol.cs +++ /dev/null @@ -1,376 +0,0 @@ -// Licensed to the Apache Software Foundation(ASF) under one -// or more contributor license agreements.See the NOTICE file -// distributed with this work for additional information -// regarding copyright ownership.The ASF licenses this file -// to you under the Apache License, Version 2.0 (the -// "License"); you may not use this file except in compliance -// with the License. You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, -// software distributed under the License is distributed on an -// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -// KIND, either express or implied. See the License for the -// specific language governing permissions and limitations -// under the License. - -using System; -using System.Text; -using System.Threading; -using System.Threading.Tasks; -using Thrift.Protocols.Entities; -using Thrift.Transports; - -namespace Thrift.Protocols -{ - // ReSharper disable once InconsistentNaming - public abstract class TProtocol : IDisposable - { - public const int DefaultRecursionDepth = 64; - private bool _isDisposed; - protected int RecursionDepth; - - protected TClientTransport Trans; - - protected TProtocol(TClientTransport trans) - { - Trans = trans; - RecursionLimit = DefaultRecursionDepth; - RecursionDepth = 0; - } - - public TClientTransport Transport => Trans; - - protected int RecursionLimit { get; set; } - - public void Dispose() - { - Dispose(true); - } - - public void IncrementRecursionDepth() - { - if (RecursionDepth < RecursionLimit) - { - ++RecursionDepth; - } - else - { - throw new TProtocolException(TProtocolException.DEPTH_LIMIT, "Depth limit exceeded"); - } - } - - public void DecrementRecursionDepth() - { - --RecursionDepth; - } - - protected virtual void Dispose(bool disposing) - { - if (!_isDisposed) - { - if (disposing) - { - (Trans as IDisposable)?.Dispose(); - } - } - _isDisposed = true; - } - - public virtual async Task WriteMessageBeginAsync(TMessage message) - { - await WriteMessageBeginAsync(message, CancellationToken.None); - } - - public abstract Task WriteMessageBeginAsync(TMessage message, CancellationToken cancellationToken); - - public virtual async Task WriteMessageEndAsync() - { - await WriteMessageEndAsync(CancellationToken.None); - } - - public abstract Task WriteMessageEndAsync(CancellationToken cancellationToken); - - public virtual async Task WriteStructBeginAsync(TStruct @struct) - { - await WriteStructBeginAsync(@struct, CancellationToken.None); - } - - public abstract Task WriteStructBeginAsync(TStruct @struct, CancellationToken cancellationToken); - - public virtual async Task WriteStructEndAsync() - { - await WriteStructEndAsync(CancellationToken.None); - } - - public abstract Task WriteStructEndAsync(CancellationToken cancellationToken); - - public virtual async Task WriteFieldBeginAsync(TField field) - { - await WriteFieldBeginAsync(field, CancellationToken.None); - } - - public abstract Task WriteFieldBeginAsync(TField field, CancellationToken cancellationToken); - - public virtual async Task WriteFieldEndAsync() - { - await WriteFieldEndAsync(CancellationToken.None); - } - - public abstract Task WriteFieldEndAsync(CancellationToken cancellationToken); - - public virtual async Task WriteFieldStopAsync() - { - await WriteFieldStopAsync(CancellationToken.None); - } - - public abstract Task WriteFieldStopAsync(CancellationToken cancellationToken); - - public virtual async Task WriteMapBeginAsync(TMap map) - { - await WriteMapBeginAsync(map, CancellationToken.None); - } - - public abstract Task WriteMapBeginAsync(TMap map, CancellationToken cancellationToken); - - public virtual async Task WriteMapEndAsync() - { - await WriteMapEndAsync(CancellationToken.None); - } - - public abstract Task WriteMapEndAsync(CancellationToken cancellationToken); - - public virtual async Task WriteListBeginAsync(TList list) - { - await WriteListBeginAsync(list, CancellationToken.None); - } - - public abstract Task WriteListBeginAsync(TList list, CancellationToken cancellationToken); - - public virtual async Task WriteListEndAsync() - { - await WriteListEndAsync(CancellationToken.None); - } - - public abstract Task WriteListEndAsync(CancellationToken cancellationToken); - - public virtual async Task WriteSetBeginAsync(TSet set) - { - await WriteSetBeginAsync(set, CancellationToken.None); - } - - public abstract Task WriteSetBeginAsync(TSet set, CancellationToken cancellationToken); - - public virtual async Task WriteSetEndAsync() - { - await WriteSetEndAsync(CancellationToken.None); - } - - public abstract Task WriteSetEndAsync(CancellationToken cancellationToken); - - public virtual async Task WriteBoolAsync(bool b) - { - await WriteBoolAsync(b, CancellationToken.None); - } - - public abstract Task WriteBoolAsync(bool b, CancellationToken cancellationToken); - - public virtual async Task WriteByteAsync(sbyte b) - { - await WriteByteAsync(b, CancellationToken.None); - } - - public abstract Task WriteByteAsync(sbyte b, CancellationToken cancellationToken); - - public virtual async Task WriteI16Async(short i16) - { - await WriteI16Async(i16, CancellationToken.None); - } - - public abstract Task WriteI16Async(short i16, CancellationToken cancellationToken); - - public virtual async Task WriteI32Async(int i32) - { - await WriteI32Async(i32, CancellationToken.None); - } - - public abstract Task WriteI32Async(int i32, CancellationToken cancellationToken); - - public virtual async Task WriteI64Async(long i64) - { - await WriteI64Async(i64, CancellationToken.None); - } - - public abstract Task WriteI64Async(long i64, CancellationToken cancellationToken); - - public virtual async Task WriteDoubleAsync(double d) - { - await WriteDoubleAsync(d, CancellationToken.None); - } - - public abstract Task WriteDoubleAsync(double d, CancellationToken cancellationToken); - - public virtual async Task WriteStringAsync(string s) - { - await WriteStringAsync(s, CancellationToken.None); - } - - public virtual async Task WriteStringAsync(string s, CancellationToken cancellationToken) - { - var bytes = Encoding.UTF8.GetBytes(s); - await WriteBinaryAsync(bytes, cancellationToken); - } - - public virtual async Task WriteBinaryAsync(byte[] bytes) - { - await WriteBinaryAsync(bytes, CancellationToken.None); - } - - public abstract Task WriteBinaryAsync(byte[] bytes, CancellationToken cancellationToken); - - public virtual async Task ReadMessageBeginAsync() - { - return await ReadMessageBeginAsync(CancellationToken.None); - } - - public abstract Task ReadMessageBeginAsync(CancellationToken cancellationToken); - - public virtual async Task ReadMessageEndAsync() - { - await ReadMessageEndAsync(CancellationToken.None); - } - - public abstract Task ReadMessageEndAsync(CancellationToken cancellationToken); - - public virtual async Task ReadStructBeginAsync() - { - return await ReadStructBeginAsync(CancellationToken.None); - } - - public abstract Task ReadStructBeginAsync(CancellationToken cancellationToken); - - public virtual async Task ReadStructEndAsync() - { - await ReadStructEndAsync(CancellationToken.None); - } - - public abstract Task ReadStructEndAsync(CancellationToken cancellationToken); - - public virtual async Task ReadFieldBeginAsync() - { - return await ReadFieldBeginAsync(CancellationToken.None); - } - - public abstract Task ReadFieldBeginAsync(CancellationToken cancellationToken); - - public virtual async Task ReadFieldEndAsync() - { - await ReadFieldEndAsync(CancellationToken.None); - } - - public abstract Task ReadFieldEndAsync(CancellationToken cancellationToken); - - public virtual async Task ReadMapBeginAsync() - { - return await ReadMapBeginAsync(CancellationToken.None); - } - - public abstract Task ReadMapBeginAsync(CancellationToken cancellationToken); - - public virtual async Task ReadMapEndAsync() - { - await ReadMapEndAsync(CancellationToken.None); - } - - public abstract Task ReadMapEndAsync(CancellationToken cancellationToken); - - public virtual async Task ReadListBeginAsync() - { - return await ReadListBeginAsync(CancellationToken.None); - } - - public abstract Task ReadListBeginAsync(CancellationToken cancellationToken); - - public virtual async Task ReadListEndAsync() - { - await ReadListEndAsync(CancellationToken.None); - } - - public abstract Task ReadListEndAsync(CancellationToken cancellationToken); - - public virtual async Task ReadSetBeginAsync() - { - return await ReadSetBeginAsync(CancellationToken.None); - } - - public abstract Task ReadSetBeginAsync(CancellationToken cancellationToken); - - public virtual async Task ReadSetEndAsync() - { - await ReadSetEndAsync(CancellationToken.None); - } - - public abstract Task ReadSetEndAsync(CancellationToken cancellationToken); - - public virtual async Task ReadBoolAsync() - { - return await ReadBoolAsync(CancellationToken.None); - } - - public abstract Task ReadBoolAsync(CancellationToken cancellationToken); - - public virtual async Task ReadByteAsync() - { - return await ReadByteAsync(CancellationToken.None); - } - - public abstract Task ReadByteAsync(CancellationToken cancellationToken); - - public virtual async Task ReadI16Async() - { - return await ReadI16Async(CancellationToken.None); - } - - public abstract Task ReadI16Async(CancellationToken cancellationToken); - - public virtual async Task ReadI32Async() - { - return await ReadI32Async(CancellationToken.None); - } - - public abstract Task ReadI32Async(CancellationToken cancellationToken); - - public virtual async Task ReadI64Async() - { - return await ReadI64Async(CancellationToken.None); - } - - public abstract Task ReadI64Async(CancellationToken cancellationToken); - - public virtual async Task ReadDoubleAsync() - { - return await ReadDoubleAsync(CancellationToken.None); - } - - public abstract Task ReadDoubleAsync(CancellationToken cancellationToken); - - public virtual async Task ReadStringAsync() - { - return await ReadStringAsync(CancellationToken.None); - } - - public virtual async Task ReadStringAsync(CancellationToken cancellationToken) - { - var buf = await ReadBinaryAsync(cancellationToken); - return Encoding.UTF8.GetString(buf, 0, buf.Length); - } - - public virtual async Task ReadBinaryAsync() - { - return await ReadBinaryAsync(CancellationToken.None); - } - - public abstract Task ReadBinaryAsync(CancellationToken cancellationToken); - } -} \ No newline at end of file diff --git a/lib/netcore/Thrift/Protocols/Utilities/TBase64Helper.cs b/lib/netcore/Thrift/Protocols/Utilities/TBase64Helper.cs deleted file mode 100644 index 7eff5e181d1..00000000000 --- a/lib/netcore/Thrift/Protocols/Utilities/TBase64Helper.cs +++ /dev/null @@ -1,101 +0,0 @@ -// Licensed to the Apache Software Foundation(ASF) under one -// or more contributor license agreements.See the NOTICE file -// distributed with this work for additional information -// regarding copyright ownership.The ASF licenses this file -// to you under the Apache License, Version 2.0 (the -// "License"); you may not use this file except in compliance -// with the License. You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, -// software distributed under the License is distributed on an -// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -// KIND, either express or implied. See the License for the -// specific language governing permissions and limitations -// under the License. - -using System; - -namespace Thrift.Protocols.Utilities -{ - // ReSharper disable once InconsistentNaming - internal static class TBase64Helper - { - //TODO: Constants - //TODO: Check for args - //TODO: Unitests - - internal const string EncodeTable = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; - - private static readonly int[] DecodeTable = - { - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63, - 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1, - -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, - 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1, - -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, - 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 - }; - - internal static void Encode(byte[] src, int srcOff, int len, byte[] dst, int dstOff) - { - if (src == null) - { - throw new ArgumentNullException(nameof(src)); - } - - dst[dstOff] = (byte) EncodeTable[(src[srcOff] >> 2) & 0x3F]; - - if (len == 3) - { - dst[dstOff + 1] = (byte) EncodeTable[((src[srcOff] << 4) & 0x30) | ((src[srcOff + 1] >> 4) & 0x0F)]; - dst[dstOff + 2] = (byte) EncodeTable[((src[srcOff + 1] << 2) & 0x3C) | ((src[srcOff + 2] >> 6) & 0x03)]; - dst[dstOff + 3] = (byte) EncodeTable[src[srcOff + 2] & 0x3F]; - } - else if (len == 2) - { - dst[dstOff + 1] = (byte) EncodeTable[((src[srcOff] << 4) & 0x30) | ((src[srcOff + 1] >> 4) & 0x0F)]; - dst[dstOff + 2] = (byte) EncodeTable[(src[srcOff + 1] << 2) & 0x3C]; - } - else - { - // len == 1 - dst[dstOff + 1] = (byte) EncodeTable[(src[srcOff] << 4) & 0x30]; - } - } - - internal static void Decode(byte[] src, int srcOff, int len, byte[] dst, int dstOff) - { - if (src == null) - { - throw new ArgumentNullException(nameof(src)); - } - - dst[dstOff] = (byte) ((DecodeTable[src[srcOff] & 0x0FF] << 2) | (DecodeTable[src[srcOff + 1] & 0x0FF] >> 4)); - - if (len > 2) - { - dst[dstOff + 1] = - (byte) - (((DecodeTable[src[srcOff + 1] & 0x0FF] << 4) & 0xF0) | (DecodeTable[src[srcOff + 2] & 0x0FF] >> 2)); - if (len > 3) - { - dst[dstOff + 2] = - (byte) - (((DecodeTable[src[srcOff + 2] & 0x0FF] << 6) & 0xC0) | DecodeTable[src[srcOff + 3] & 0x0FF]); - } - } - } - } -} \ No newline at end of file diff --git a/lib/netcore/Thrift/Server/AsyncBaseServer.cs b/lib/netcore/Thrift/Server/AsyncBaseServer.cs deleted file mode 100644 index 325c39c7164..00000000000 --- a/lib/netcore/Thrift/Server/AsyncBaseServer.cs +++ /dev/null @@ -1,183 +0,0 @@ -// Licensed to the Apache Software Foundation(ASF) under one -// or more contributor license agreements.See the NOTICE file -// distributed with this work for additional information -// regarding copyright ownership.The ASF licenses this file -// to you under the Apache License, Version 2.0 (the -// "License"); you may not use this file except in compliance -// with the License. You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, -// software distributed under the License is distributed on an -// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -// KIND, either express or implied. See the License for the -// specific language governing permissions and limitations -// under the License. - -using System; -using System.Threading; -using System.Threading.Tasks; -using Microsoft.Extensions.Logging; -using Thrift.Protocols; -using Thrift.Transports; - -namespace Thrift.Server -{ - //TODO: unhandled exceptions, etc. - - // ReSharper disable once InconsistentNaming - public class AsyncBaseServer : TBaseServer - { - private readonly int _clientWaitingDelay; - private volatile Task _serverTask; - - public AsyncBaseServer(ITAsyncProcessor processor, TServerTransport serverTransport, - ITProtocolFactory inputProtocolFactory, ITProtocolFactory outputProtocolFactory, - ILoggerFactory loggerFactory, int clientWaitingDelay = 10) - : this(new SingletonTProcessorFactory(processor), serverTransport, - new TTransportFactory(), new TTransportFactory(), - inputProtocolFactory, outputProtocolFactory, - loggerFactory.CreateLogger(nameof(AsyncBaseServer)), clientWaitingDelay) - { - } - - public AsyncBaseServer(ITProcessorFactory itProcessorFactory, TServerTransport serverTransport, - TTransportFactory inputTransportFactory, TTransportFactory outputTransportFactory, - ITProtocolFactory inputProtocolFactory, ITProtocolFactory outputProtocolFactory, - ILogger logger, int clientWaitingDelay = 10) - : base(itProcessorFactory, serverTransport, inputTransportFactory, outputTransportFactory, - inputProtocolFactory, outputProtocolFactory, logger) - { - _clientWaitingDelay = clientWaitingDelay; - } - - public override async Task ServeAsync(CancellationToken cancellationToken) - { - try - { - // cancelation token - _serverTask = Task.Factory.StartNew(() => StartListening(cancellationToken), TaskCreationOptions.LongRunning); - await _serverTask; - } - catch (Exception ex) - { - Logger.LogError(ex.ToString()); - } - } - - private async Task StartListening(CancellationToken cancellationToken) - { - ServerTransport.Listen(); - - Logger.LogTrace("Started listening at server"); - - if (ServerEventHandler != null) - { - await ServerEventHandler.PreServeAsync(cancellationToken); - } - - while (!cancellationToken.IsCancellationRequested) - { - if (ServerTransport.IsClientPending()) - { - Logger.LogTrace("Waiting for client connection"); - - try - { - var client = await ServerTransport.AcceptAsync(cancellationToken); - await Task.Factory.StartNew(() => Execute(client, cancellationToken), cancellationToken); - } - catch (TTransportException ttx) - { - Logger.LogTrace($"Transport exception: {ttx}"); - - if (ttx.Type != TTransportException.ExceptionType.Interrupted) - { - Logger.LogError(ttx.ToString()); - } - } - } - else - { - try - { - await Task.Delay(TimeSpan.FromMilliseconds(_clientWaitingDelay), cancellationToken); - } - catch(TaskCanceledException) { } - } - } - - ServerTransport.Close(); - - Logger.LogTrace("Completed listening at server"); - } - - public override void Stop() - { - } - - private async Task Execute(TClientTransport client, CancellationToken cancellationToken) - { - Logger.LogTrace("Started client request processing"); - - var processor = ItProcessorFactory.GetAsyncProcessor(client, this); - - TClientTransport inputTransport = null; - TClientTransport outputTransport = null; - TProtocol inputProtocol = null; - TProtocol outputProtocol = null; - object connectionContext = null; - - try - { - inputTransport = InputTransportFactory.GetTransport(client); - outputTransport = OutputTransportFactory.GetTransport(client); - - inputProtocol = InputProtocolFactory.GetProtocol(inputTransport); - outputProtocol = OutputProtocolFactory.GetProtocol(outputTransport); - - if (ServerEventHandler != null) - { - connectionContext = await ServerEventHandler.CreateContextAsync(inputProtocol, outputProtocol, cancellationToken); - } - - while (!cancellationToken.IsCancellationRequested) - { - if (!await inputTransport.PeekAsync(cancellationToken)) - { - break; - } - - if (ServerEventHandler != null) - { - await ServerEventHandler.ProcessContextAsync(connectionContext, inputTransport, cancellationToken); - } - - if (!await processor.ProcessAsync(inputProtocol, outputProtocol, cancellationToken)) - { - break; - } - } - } - catch (TTransportException ttx) - { - Logger.LogTrace($"Transport exception: {ttx}"); - } - catch (Exception x) - { - Logger.LogError($"Error: {x}"); - } - - if (ServerEventHandler != null) - { - await ServerEventHandler.DeleteContextAsync(connectionContext, inputProtocol, outputProtocol, cancellationToken); - } - - inputTransport?.Close(); - outputTransport?.Close(); - - Logger.LogTrace("Completed client request processing"); - } - } -} diff --git a/lib/netcore/Thrift/Thrift.csproj b/lib/netcore/Thrift/Thrift.csproj deleted file mode 100644 index e806fede750..00000000000 --- a/lib/netcore/Thrift/Thrift.csproj +++ /dev/null @@ -1,31 +0,0 @@ - - - - netstandard2.0 - Thrift - Thrift - true - true - false - false - false - false - false - false - false - false - - - - - - - - - - - - - - - diff --git a/lib/netcore/Thrift/Transports/Client/TBufferedClientTransport.cs b/lib/netcore/Thrift/Transports/Client/TBufferedClientTransport.cs deleted file mode 100644 index 761f1ac783c..00000000000 --- a/lib/netcore/Thrift/Transports/Client/TBufferedClientTransport.cs +++ /dev/null @@ -1,206 +0,0 @@ -// Licensed to the Apache Software Foundation(ASF) under one -// or more contributor license agreements.See the NOTICE file -// distributed with this work for additional information -// regarding copyright ownership.The ASF licenses this file -// to you under the Apache License, Version 2.0 (the -// "License"); you may not use this file except in compliance -// with the License. You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, -// software distributed under the License is distributed on an -// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -// KIND, either express or implied. See the License for the -// specific language governing permissions and limitations -// under the License. - -using System; -using System.IO; -using System.Threading; -using System.Threading.Tasks; - -namespace Thrift.Transports.Client -{ - // ReSharper disable once InconsistentNaming - public class TBufferedClientTransport : TClientTransport - { - private readonly int _bufSize; - private readonly MemoryStream _inputBuffer = new MemoryStream(0); - private readonly MemoryStream _outputBuffer = new MemoryStream(0); - private readonly TClientTransport _transport; - private bool _isDisposed; - - //TODO: should support only specified input transport? - public TBufferedClientTransport(TClientTransport transport, int bufSize = 1024) - { - if (bufSize <= 0) - { - throw new ArgumentOutOfRangeException(nameof(bufSize), "Buffer size must be a positive number."); - } - - _transport = transport ?? throw new ArgumentNullException(nameof(transport)); - _bufSize = bufSize; - } - - public TClientTransport UnderlyingTransport - { - get - { - CheckNotDisposed(); - - return _transport; - } - } - - public override bool IsOpen => !_isDisposed && _transport.IsOpen; - - public override async Task OpenAsync(CancellationToken cancellationToken) - { - CheckNotDisposed(); - - await _transport.OpenAsync(cancellationToken); - } - - public override void Close() - { - CheckNotDisposed(); - - _transport.Close(); - } - - public override async Task ReadAsync(byte[] buffer, int offset, int length, - CancellationToken cancellationToken) - { - //TODO: investigate how it should work correctly - CheckNotDisposed(); - - ValidateBufferArgs(buffer, offset, length); - - if (!IsOpen) - { - throw new TTransportException(TTransportException.ExceptionType.NotOpen); - } - - if (_inputBuffer.Capacity < _bufSize) - { - _inputBuffer.Capacity = _bufSize; - } - - var got = await _inputBuffer.ReadAsync(buffer, offset, length, cancellationToken); - if (got > 0) - { - return got; - } - - _inputBuffer.Seek(0, SeekOrigin.Begin); - _inputBuffer.SetLength(_inputBuffer.Capacity); - - ArraySegment bufSegment; - _inputBuffer.TryGetBuffer(out bufSegment); - - // investigate - var filled = await _transport.ReadAsync(bufSegment.Array, 0, (int) _inputBuffer.Length, cancellationToken); - _inputBuffer.SetLength(filled); - - if (filled == 0) - { - return 0; - } - - return await ReadAsync(buffer, offset, length, cancellationToken); - } - - public override async Task WriteAsync(byte[] buffer, int offset, int length, CancellationToken cancellationToken) - { - CheckNotDisposed(); - - ValidateBufferArgs(buffer, offset, length); - - if (!IsOpen) - { - throw new TTransportException(TTransportException.ExceptionType.NotOpen); - } - - // Relative offset from "off" argument - var writtenCount = 0; - if (_outputBuffer.Length > 0) - { - var capa = (int) (_outputBuffer.Capacity - _outputBuffer.Length); - var writeSize = capa <= length ? capa : length; - await _outputBuffer.WriteAsync(buffer, offset, writeSize, cancellationToken); - - writtenCount += writeSize; - if (writeSize == capa) - { - //ArraySegment bufSegment; - //_outputBuffer.TryGetBuffer(out bufSegment); - var data = _outputBuffer.ToArray(); - //await _transport.WriteAsync(bufSegment.Array, cancellationToken); - await _transport.WriteAsync(data, cancellationToken); - _outputBuffer.SetLength(0); - } - } - - while (length - writtenCount >= _bufSize) - { - await _transport.WriteAsync(buffer, offset + writtenCount, _bufSize, cancellationToken); - writtenCount += _bufSize; - } - - var remain = length - writtenCount; - if (remain > 0) - { - if (_outputBuffer.Capacity < _bufSize) - { - _outputBuffer.Capacity = _bufSize; - } - await _outputBuffer.WriteAsync(buffer, offset + writtenCount, remain, cancellationToken); - } - } - - public override async Task FlushAsync(CancellationToken cancellationToken) - { - CheckNotDisposed(); - - if (!IsOpen) - { - throw new TTransportException(TTransportException.ExceptionType.NotOpen); - } - - if (_outputBuffer.Length > 0) - { - //ArraySegment bufSegment; - var data = _outputBuffer.ToArray(); // TryGetBuffer(out bufSegment); - - await _transport.WriteAsync(data /*bufSegment.Array*/, cancellationToken); - _outputBuffer.SetLength(0); - } - - await _transport.FlushAsync(cancellationToken); - } - - private void CheckNotDisposed() - { - if (_isDisposed) - { - throw new ObjectDisposedException(nameof(_transport)); - } - } - - // IDisposable - protected override void Dispose(bool disposing) - { - if (!_isDisposed) - { - if (disposing) - { - _inputBuffer?.Dispose(); - _outputBuffer?.Dispose(); - _transport?.Dispose(); - } - } - _isDisposed = true; - } - } -} \ No newline at end of file diff --git a/lib/netcore/Thrift/Transports/Client/TFramedClientTransport.cs b/lib/netcore/Thrift/Transports/Client/TFramedClientTransport.cs deleted file mode 100644 index d11bb959a2b..00000000000 --- a/lib/netcore/Thrift/Transports/Client/TFramedClientTransport.cs +++ /dev/null @@ -1,201 +0,0 @@ -// Licensed to the Apache Software Foundation(ASF) under one -// or more contributor license agreements.See the NOTICE file -// distributed with this work for additional information -// regarding copyright ownership.The ASF licenses this file -// to you under the Apache License, Version 2.0 (the -// "License"); you may not use this file except in compliance -// with the License. You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, -// software distributed under the License is distributed on an -// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -// KIND, either express or implied. See the License for the -// specific language governing permissions and limitations -// under the License. - -using System; -using System.IO; -using System.Threading; -using System.Threading.Tasks; - -namespace Thrift.Transports.Client -{ - //TODO: check for correct implementation - - // ReSharper disable once InconsistentNaming - public class TFramedClientTransport : TClientTransport - { - private const int HeaderSize = 4; - private readonly byte[] _headerBuf = new byte[HeaderSize]; - private readonly MemoryStream _readBuffer = new MemoryStream(1024); - private readonly TClientTransport _transport; - private readonly MemoryStream _writeBuffer = new MemoryStream(1024); - - private bool _isDisposed; - - public TFramedClientTransport(TClientTransport transport) - { - _transport = transport ?? throw new ArgumentNullException(nameof(transport)); - - InitWriteBuffer(); - } - - public override bool IsOpen => !_isDisposed && _transport.IsOpen; - - public override async Task OpenAsync(CancellationToken cancellationToken) - { - CheckNotDisposed(); - - await _transport.OpenAsync(cancellationToken); - } - - public override void Close() - { - CheckNotDisposed(); - - _transport.Close(); - } - - public override async Task ReadAsync(byte[] buffer, int offset, int length, - CancellationToken cancellationToken) - { - CheckNotDisposed(); - - ValidateBufferArgs(buffer, offset, length); - - if (!IsOpen) - { - throw new TTransportException(TTransportException.ExceptionType.NotOpen); - } - - var got = await _readBuffer.ReadAsync(buffer, offset, length, cancellationToken); - if (got > 0) - { - return got; - } - - // Read another frame of data - await ReadFrameAsync(cancellationToken); - - return await _readBuffer.ReadAsync(buffer, offset, length, cancellationToken); - } - - private async Task ReadFrameAsync(CancellationToken cancellationToken) - { - await _transport.ReadAllAsync(_headerBuf, 0, HeaderSize, cancellationToken); - - var size = DecodeFrameSize(_headerBuf); - - _readBuffer.SetLength(size); - _readBuffer.Seek(0, SeekOrigin.Begin); - - ArraySegment bufSegment; - _readBuffer.TryGetBuffer(out bufSegment); - - var buff = bufSegment.Array; - - await _transport.ReadAllAsync(buff, 0, size, cancellationToken); - } - - public override async Task WriteAsync(byte[] buffer, int offset, int length, CancellationToken cancellationToken) - { - CheckNotDisposed(); - - ValidateBufferArgs(buffer, offset, length); - - if (!IsOpen) - { - throw new TTransportException(TTransportException.ExceptionType.NotOpen); - } - - if (_writeBuffer.Length + length > int.MaxValue) - { - await FlushAsync(cancellationToken); - } - - await _writeBuffer.WriteAsync(buffer, offset, length, cancellationToken); - } - - public override async Task FlushAsync(CancellationToken cancellationToken) - { - CheckNotDisposed(); - - if (!IsOpen) - { - throw new TTransportException(TTransportException.ExceptionType.NotOpen); - } - - //ArraySegment bufSegment; - //_writeBuffer.TryGetBuffer(out bufSegment); - //var buf = bufSegment.Array; - var buf = _writeBuffer.ToArray(); - - //var len = (int)_writeBuffer.Length; - var dataLen = (int) _writeBuffer.Length - HeaderSize; - if (dataLen < 0) - { - throw new InvalidOperationException(); // logic error actually - } - - // Inject message header into the reserved buffer space - EncodeFrameSize(dataLen, buf); - - // Send the entire message at once - await _transport.WriteAsync(buf, cancellationToken); - - InitWriteBuffer(); - - await _transport.FlushAsync(cancellationToken); - } - - private void InitWriteBuffer() - { - // Reserve space for message header to be put right before sending it out - _writeBuffer.SetLength(HeaderSize); - _writeBuffer.Seek(0, SeekOrigin.End); - } - - private static void EncodeFrameSize(int frameSize, byte[] buf) - { - buf[0] = (byte) (0xff & (frameSize >> 24)); - buf[1] = (byte) (0xff & (frameSize >> 16)); - buf[2] = (byte) (0xff & (frameSize >> 8)); - buf[3] = (byte) (0xff & (frameSize)); - } - - private static int DecodeFrameSize(byte[] buf) - { - return - ((buf[0] & 0xff) << 24) | - ((buf[1] & 0xff) << 16) | - ((buf[2] & 0xff) << 8) | - (buf[3] & 0xff); - } - - - private void CheckNotDisposed() - { - if (_isDisposed) - { - throw new ObjectDisposedException("TFramedClientTransport"); - } - } - - // IDisposable - protected override void Dispose(bool disposing) - { - if (!_isDisposed) - { - if (disposing) - { - _readBuffer?.Dispose(); - _writeBuffer?.Dispose(); - _transport?.Dispose(); - } - } - _isDisposed = true; - } - } -} \ No newline at end of file diff --git a/lib/netcore/Thrift/Transports/Client/THttpClientTransport.cs b/lib/netcore/Thrift/Transports/Client/THttpClientTransport.cs deleted file mode 100644 index 60671414e9a..00000000000 --- a/lib/netcore/Thrift/Transports/Client/THttpClientTransport.cs +++ /dev/null @@ -1,226 +0,0 @@ -// Licensed to the Apache Software Foundation(ASF) under one -// or more contributor license agreements.See the NOTICE file -// distributed with this work for additional information -// regarding copyright ownership.The ASF licenses this file -// to you under the Apache License, Version 2.0 (the -// "License"); you may not use this file except in compliance -// with the License. You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, -// software distributed under the License is distributed on an -// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -// KIND, either express or implied. See the License for the -// specific language governing permissions and limitations -// under the License. - -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Net.Http; -using System.Net.Http.Headers; -using System.Security.Cryptography.X509Certificates; -using System.Threading; -using System.Threading.Tasks; - -namespace Thrift.Transports.Client -{ - // ReSharper disable once InconsistentNaming - public class THttpClientTransport : TClientTransport - { - private readonly X509Certificate[] _certificates; - private readonly Uri _uri; - - // Timeouts in milliseconds - private int _connectTimeout = 30000; - private HttpClient _httpClient; - private Stream _inputStream; - - private bool _isDisposed; - private MemoryStream _outputStream = new MemoryStream(); - - public THttpClientTransport(Uri u, IDictionary customHeaders) - : this(u, Enumerable.Empty(), customHeaders) - { - } - - public THttpClientTransport(Uri u, IEnumerable certificates, - IDictionary customHeaders) - { - _uri = u; - _certificates = (certificates ?? Enumerable.Empty()).ToArray(); - CustomHeaders = customHeaders; - - // due to current bug with performance of Dispose in netcore https://github.com/dotnet/corefx/issues/8809 - // this can be switched to default way (create client->use->dispose per flush) later - _httpClient = CreateClient(); - } - - public IDictionary CustomHeaders { get; } - - public int ConnectTimeout - { - set { _connectTimeout = value; } - } - - public override bool IsOpen => true; - - public override async Task OpenAsync(CancellationToken cancellationToken) - { - if (cancellationToken.IsCancellationRequested) - { - await Task.FromCanceled(cancellationToken); - } - } - - public override void Close() - { - if (_inputStream != null) - { - _inputStream.Dispose(); - _inputStream = null; - } - - if (_outputStream != null) - { - _outputStream.Dispose(); - _outputStream = null; - } - - if (_httpClient != null) - { - _httpClient.Dispose(); - _httpClient = null; - } - } - - public override async Task ReadAsync(byte[] buffer, int offset, int length, - CancellationToken cancellationToken) - { - if (cancellationToken.IsCancellationRequested) - { - return await Task.FromCanceled(cancellationToken); - } - - if (_inputStream == null) - { - throw new TTransportException(TTransportException.ExceptionType.NotOpen, "No request has been sent"); - } - - try - { - var ret = await _inputStream.ReadAsync(buffer, offset, length, cancellationToken); - - if (ret == -1) - { - throw new TTransportException(TTransportException.ExceptionType.EndOfFile, "No more data available"); - } - - return ret; - } - catch (IOException iox) - { - throw new TTransportException(TTransportException.ExceptionType.Unknown, iox.ToString()); - } - } - - public override async Task WriteAsync(byte[] buffer, int offset, int length, CancellationToken cancellationToken) - { - if (cancellationToken.IsCancellationRequested) - { - await Task.FromCanceled(cancellationToken); - } - - await _outputStream.WriteAsync(buffer, offset, length, cancellationToken); - } - - private HttpClient CreateClient() - { - var handler = new HttpClientHandler(); - handler.ClientCertificates.AddRange(_certificates); - - var httpClient = new HttpClient(handler); - - if (_connectTimeout > 0) - { - httpClient.Timeout = TimeSpan.FromSeconds(_connectTimeout); - } - - httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/x-thrift")); - httpClient.DefaultRequestHeaders.UserAgent.Add(new ProductInfoHeaderValue("THttpClientTransport", "0.12.0")); - - if (CustomHeaders != null) - { - foreach (var item in CustomHeaders) - { - httpClient.DefaultRequestHeaders.Add(item.Key, item.Value); - } - } - - return httpClient; - } - - public override async Task FlushAsync(CancellationToken cancellationToken) - { - try - { - try - { - if (_outputStream.CanSeek) - { - _outputStream.Seek(0, SeekOrigin.Begin); - } - - using (var outStream = new StreamContent(_outputStream)) - { - var msg = await _httpClient.PostAsync(_uri, outStream, cancellationToken); - - msg.EnsureSuccessStatusCode(); - - if (_inputStream != null) - { - _inputStream.Dispose(); - _inputStream = null; - } - - _inputStream = await msg.Content.ReadAsStreamAsync(); - if (_inputStream.CanSeek) - { - _inputStream.Seek(0, SeekOrigin.Begin); - } - } - } - catch (IOException iox) - { - throw new TTransportException(TTransportException.ExceptionType.Unknown, iox.ToString()); - } - catch (HttpRequestException wx) - { - throw new TTransportException(TTransportException.ExceptionType.Unknown, - "Couldn't connect to server: " + wx); - } - } - finally - { - _outputStream = new MemoryStream(); - } - } - - // IDisposable - protected override void Dispose(bool disposing) - { - if (!_isDisposed) - { - if (disposing) - { - _inputStream?.Dispose(); - _outputStream?.Dispose(); - _httpClient?.Dispose(); - } - } - _isDisposed = true; - } - } -} diff --git a/lib/netcore/Thrift/Transports/Client/TMemoryBufferClientTransport.cs b/lib/netcore/Thrift/Transports/Client/TMemoryBufferClientTransport.cs deleted file mode 100644 index 46a55a64a1e..00000000000 --- a/lib/netcore/Thrift/Transports/Client/TMemoryBufferClientTransport.cs +++ /dev/null @@ -1,97 +0,0 @@ -// Licensed to the Apache Software Foundation(ASF) under one -// or more contributor license agreements.See the NOTICE file -// distributed with this work for additional information -// regarding copyright ownership.The ASF licenses this file -// to you under the Apache License, Version 2.0 (the -// "License"); you may not use this file except in compliance -// with the License. You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, -// software distributed under the License is distributed on an -// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -// KIND, either express or implied. See the License for the -// specific language governing permissions and limitations -// under the License. - -using System.IO; -using System.Threading; -using System.Threading.Tasks; - -namespace Thrift.Transports.Client -{ - // ReSharper disable once InconsistentNaming - public class TMemoryBufferClientTransport : TClientTransport - { - private readonly MemoryStream _byteStream; - private bool _isDisposed; - - public TMemoryBufferClientTransport() - { - _byteStream = new MemoryStream(); - } - - public TMemoryBufferClientTransport(byte[] buf) - { - _byteStream = new MemoryStream(buf); - } - - public override bool IsOpen => true; - - public override async Task OpenAsync(CancellationToken cancellationToken) - { - if (cancellationToken.IsCancellationRequested) - { - await Task.FromCanceled(cancellationToken); - } - } - - public override void Close() - { - /** do nothing **/ - } - - public override async Task ReadAsync(byte[] buffer, int offset, int length, - CancellationToken cancellationToken) - { - return await _byteStream.ReadAsync(buffer, offset, length, cancellationToken); - } - - public override async Task WriteAsync(byte[] buffer, CancellationToken cancellationToken) - { - await _byteStream.WriteAsync(buffer, 0, buffer.Length, cancellationToken); - } - - public override async Task WriteAsync(byte[] buffer, int offset, int length, CancellationToken cancellationToken) - { - await _byteStream.WriteAsync(buffer, offset, length, cancellationToken); - } - - public override async Task FlushAsync(CancellationToken cancellationToken) - { - if (cancellationToken.IsCancellationRequested) - { - await Task.FromCanceled(cancellationToken); - } - } - - public byte[] GetBuffer() - { - return _byteStream.ToArray(); - } - - // IDisposable - protected override void Dispose(bool disposing) - { - if (!_isDisposed) - { - if (disposing) - { - _byteStream?.Dispose(); - } - } - _isDisposed = true; - } - } -} \ No newline at end of file diff --git a/lib/netcore/Thrift/Transports/Client/TNamedPipeClientTransport.cs b/lib/netcore/Thrift/Transports/Client/TNamedPipeClientTransport.cs deleted file mode 100644 index f5e4baf4a12..00000000000 --- a/lib/netcore/Thrift/Transports/Client/TNamedPipeClientTransport.cs +++ /dev/null @@ -1,95 +0,0 @@ -// Licensed to the Apache Software Foundation(ASF) under one -// or more contributor license agreements.See the NOTICE file -// distributed with this work for additional information -// regarding copyright ownership.The ASF licenses this file -// to you under the Apache License, Version 2.0 (the -// "License"); you may not use this file except in compliance -// with the License. You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, -// software distributed under the License is distributed on an -// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -// KIND, either express or implied. See the License for the -// specific language governing permissions and limitations -// under the License. - -using System.IO.Pipes; -using System.Threading; -using System.Threading.Tasks; - -namespace Thrift.Transports.Client -{ - // ReSharper disable once InconsistentNaming - public class TNamedPipeClientTransport : TClientTransport - { - private NamedPipeClientStream _client; - - public TNamedPipeClientTransport(string pipe) : this(".", pipe) - { - } - - public TNamedPipeClientTransport(string server, string pipe) - { - var serverName = string.IsNullOrWhiteSpace(server) ? server : "."; - - _client = new NamedPipeClientStream(serverName, pipe, PipeDirection.InOut, PipeOptions.None); - } - - public override bool IsOpen => _client != null && _client.IsConnected; - - public override async Task OpenAsync(CancellationToken cancellationToken) - { - if (IsOpen) - { - throw new TTransportException(TTransportException.ExceptionType.AlreadyOpen); - } - - await _client.ConnectAsync(cancellationToken); - } - - public override void Close() - { - if (_client != null) - { - _client.Dispose(); - _client = null; - } - } - - public override async Task ReadAsync(byte[] buffer, int offset, int length, - CancellationToken cancellationToken) - { - if (_client == null) - { - throw new TTransportException(TTransportException.ExceptionType.NotOpen); - } - - return await _client.ReadAsync(buffer, offset, length, cancellationToken); - } - - public override async Task WriteAsync(byte[] buffer, int offset, int length, CancellationToken cancellationToken) - { - if (_client == null) - { - throw new TTransportException(TTransportException.ExceptionType.NotOpen); - } - - await _client.WriteAsync(buffer, offset, length, cancellationToken); - } - - public override async Task FlushAsync(CancellationToken cancellationToken) - { - if (cancellationToken.IsCancellationRequested) - { - await Task.FromCanceled(cancellationToken); - } - } - - protected override void Dispose(bool disposing) - { - _client.Dispose(); - } - } -} \ No newline at end of file diff --git a/lib/netcore/Thrift/Transports/Server/THttpServerTransport.cs b/lib/netcore/Thrift/Transports/Server/THttpServerTransport.cs deleted file mode 100644 index 032063a3778..00000000000 --- a/lib/netcore/Thrift/Transports/Server/THttpServerTransport.cs +++ /dev/null @@ -1,98 +0,0 @@ -// Licensed to the Apache Software Foundation(ASF) under one -// or more contributor license agreements.See the NOTICE file -// distributed with this work for additional information -// regarding copyright ownership.The ASF licenses this file -// to you under the Apache License, Version 2.0 (the -// "License"); you may not use this file except in compliance -// with the License. You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, -// software distributed under the License is distributed on an -// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -// KIND, either express or implied. See the License for the -// specific language governing permissions and limitations -// under the License. - -using System; -using System.Text; -using System.Threading; -using System.Threading.Tasks; -using Microsoft.AspNetCore.Http; -using Microsoft.Extensions.Logging; -using Thrift.Protocols; -using Thrift.Transports.Client; - -namespace Thrift.Transports.Server -{ - // ReSharper disable once InconsistentNaming - public class THttpServerTransport - { - protected const string ContentType = "application/x-thrift"; - private readonly ILogger _logger; - private readonly RequestDelegate _next; - protected Encoding Encoding = Encoding.UTF8; - - protected ITProtocolFactory InputProtocolFactory; - protected ITProtocolFactory OutputProtocolFactory; - - protected ITAsyncProcessor Processor; - - public THttpServerTransport(ITAsyncProcessor processor, RequestDelegate next, ILoggerFactory loggerFactory) - : this(processor, new TBinaryProtocol.Factory(), next, loggerFactory) - { - } - - public THttpServerTransport(ITAsyncProcessor processor, ITProtocolFactory protocolFactory, RequestDelegate next, - ILoggerFactory loggerFactory) - : this(processor, protocolFactory, protocolFactory, next, loggerFactory) - { - } - - public THttpServerTransport(ITAsyncProcessor processor, ITProtocolFactory inputProtocolFactory, - ITProtocolFactory outputProtocolFactory, RequestDelegate next, ILoggerFactory loggerFactory) - { - if (loggerFactory == null) - { - throw new ArgumentNullException(nameof(loggerFactory)); - } - - Processor = processor ?? throw new ArgumentNullException(nameof(processor)); - InputProtocolFactory = inputProtocolFactory ?? throw new ArgumentNullException(nameof(inputProtocolFactory)); - OutputProtocolFactory = outputProtocolFactory ?? throw new ArgumentNullException(nameof(outputProtocolFactory)); - - _next = next; - _logger = loggerFactory.CreateLogger(); - } - - public async Task Invoke(HttpContext context) - { - context.Response.ContentType = ContentType; - await ProcessRequestAsync(context, context.RequestAborted); //TODO: check for correct logic - } - - public async Task ProcessRequestAsync(HttpContext context, CancellationToken cancellationToken) - { - var transport = new TStreamClientTransport(context.Request.Body, context.Response.Body); - - try - { - var input = InputProtocolFactory.GetProtocol(transport); - var output = OutputProtocolFactory.GetProtocol(transport); - - while (await Processor.ProcessAsync(input, output, cancellationToken)) - { - } - } - catch (TTransportException) - { - // Client died, just move on - } - finally - { - transport.Close(); - } - } - } -} \ No newline at end of file diff --git a/lib/netcore/Thrift/Transports/Server/TNamedPipeServerTransport.cs b/lib/netcore/Thrift/Transports/Server/TNamedPipeServerTransport.cs deleted file mode 100644 index 186786ed256..00000000000 --- a/lib/netcore/Thrift/Transports/Server/TNamedPipeServerTransport.cs +++ /dev/null @@ -1,191 +0,0 @@ -// Licensed to the Apache Software Foundation(ASF) under one -// or more contributor license agreements.See the NOTICE file -// distributed with this work for additional information -// regarding copyright ownership.The ASF licenses this file -// to you under the Apache License, Version 2.0 (the -// "License"); you may not use this file except in compliance -// with the License. You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, -// software distributed under the License is distributed on an -// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -// KIND, either express or implied. See the License for the -// specific language governing permissions and limitations -// under the License. - -using System; -using System.IO.Pipes; -using System.Threading; -using System.Threading.Tasks; - -namespace Thrift.Transports.Server -{ - // ReSharper disable once InconsistentNaming - public class TNamedPipeServerTransport : TServerTransport - { - /// - /// This is the address of the Pipe on the localhost. - /// - private readonly string _pipeAddress; - - private bool _asyncMode = true; - private volatile bool _isPending = true; - - private NamedPipeServerStream _stream = null; - - public TNamedPipeServerTransport(string pipeAddress) - { - _pipeAddress = pipeAddress; - } - - public override void Listen() - { - // nothing to do here - } - - public override void Close() - { - if (_stream != null) - { - try - { - //TODO: check for disconection - _stream.Disconnect(); - _stream.Dispose(); - } - finally - { - _stream = null; - _isPending = false; - } - } - } - - public override bool IsClientPending() - { - return _isPending; - } - - private void EnsurePipeInstance() - { - if (_stream == null) - { - var direction = PipeDirection.InOut; - var maxconn = 254; - var mode = PipeTransmissionMode.Byte; - var options = _asyncMode ? PipeOptions.Asynchronous : PipeOptions.None; - var inbuf = 4096; - var outbuf = 4096; - // TODO: security - - try - { - _stream = new NamedPipeServerStream(_pipeAddress, direction, maxconn, mode, options, inbuf, outbuf); - } - catch (NotImplementedException) // Mono still does not support async, fallback to sync - { - if (_asyncMode) - { - options &= (~PipeOptions.Asynchronous); - _stream = new NamedPipeServerStream(_pipeAddress, direction, maxconn, mode, options, inbuf, - outbuf); - _asyncMode = false; - } - else - { - throw; - } - } - } - } - - protected override async Task AcceptImplementationAsync(CancellationToken cancellationToken) - { - try - { - EnsurePipeInstance(); - - await _stream.WaitForConnectionAsync(cancellationToken); - - var trans = new ServerTransport(_stream); - _stream = null; // pass ownership to ServerTransport - - //_isPending = false; - - return trans; - } - catch (TTransportException) - { - Close(); - throw; - } - catch (Exception e) - { - Close(); - throw new TTransportException(TTransportException.ExceptionType.NotOpen, e.Message); - } - } - - private class ServerTransport : TClientTransport - { - private readonly NamedPipeServerStream _stream; - - public ServerTransport(NamedPipeServerStream stream) - { - _stream = stream; - } - - public override bool IsOpen => _stream != null && _stream.IsConnected; - - public override async Task OpenAsync(CancellationToken cancellationToken) - { - if (cancellationToken.IsCancellationRequested) - { - await Task.FromCanceled(cancellationToken); - } - } - - public override void Close() - { - _stream?.Dispose(); - } - - public override async Task ReadAsync(byte[] buffer, int offset, int length, - CancellationToken cancellationToken) - { - if (_stream == null) - { - throw new TTransportException(TTransportException.ExceptionType.NotOpen); - } - - return await _stream.ReadAsync(buffer, offset, length, cancellationToken); - } - - public override async Task WriteAsync(byte[] buffer, int offset, int length, - CancellationToken cancellationToken) - { - if (_stream == null) - { - throw new TTransportException(TTransportException.ExceptionType.NotOpen); - } - - await _stream.WriteAsync(buffer, offset, length, cancellationToken); - } - - public override async Task FlushAsync(CancellationToken cancellationToken) - { - if (cancellationToken.IsCancellationRequested) - { - await Task.FromCanceled(cancellationToken); - } - } - - protected override void Dispose(bool disposing) - { - _stream?.Dispose(); - } - } - } -} \ No newline at end of file diff --git a/lib/netcore/Thrift/Transports/Server/TServerSocketTransport.cs b/lib/netcore/Thrift/Transports/Server/TServerSocketTransport.cs deleted file mode 100644 index 3a9d8a17d0a..00000000000 --- a/lib/netcore/Thrift/Transports/Server/TServerSocketTransport.cs +++ /dev/null @@ -1,174 +0,0 @@ -// Licensed to the Apache Software Foundation(ASF) under one -// or more contributor license agreements.See the NOTICE file -// distributed with this work for additional information -// regarding copyright ownership.The ASF licenses this file -// to you under the Apache License, Version 2.0 (the -// "License"); you may not use this file except in compliance -// with the License. You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, -// software distributed under the License is distributed on an -// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -// KIND, either express or implied. See the License for the -// specific language governing permissions and limitations -// under the License. - -using System; -using System.Net; -using System.Net.Sockets; -using System.Threading; -using System.Threading.Tasks; -using Thrift.Transports.Client; - -namespace Thrift.Transports.Server -{ - // ReSharper disable once InconsistentNaming - public class TServerSocketTransport : TServerTransport - { - private readonly int _clientTimeout; - private readonly int _port; - private readonly bool _useBufferedSockets; - private readonly bool _useFramedTransport; - private TcpListener _server; - - public TServerSocketTransport(TcpListener listener) - : this(listener, 0) - { - } - - public TServerSocketTransport(TcpListener listener, int clientTimeout) - { - _server = listener; - _clientTimeout = clientTimeout; - } - - public TServerSocketTransport(int port) - : this(port, 0) - { - } - - public TServerSocketTransport(int port, int clientTimeout) - : this(port, clientTimeout, false) - { - } - - public TServerSocketTransport(int port, int clientTimeout, bool useBufferedSockets): - this(port, clientTimeout, useBufferedSockets, false) - { - } - - public TServerSocketTransport(int port, int clientTimeout, bool useBufferedSockets, bool useFramedTransport) - { - _port = port; - _clientTimeout = clientTimeout; - _useBufferedSockets = useBufferedSockets; - _useFramedTransport = useFramedTransport; - try - { - // Make server socket - _server = new TcpListener(IPAddress.Any, _port); - _server.Server.NoDelay = true; - } - catch (Exception) - { - _server = null; - throw new TTransportException("Could not create ServerSocket on port " + port + "."); - } - } - - public override void Listen() - { - // Make sure not to block on accept - if (_server != null) - { - try - { - _server.Start(); - } - catch (SocketException sx) - { - throw new TTransportException("Could not accept on listening socket: " + sx.Message); - } - } - } - - public override bool IsClientPending() - { - return _server.Pending(); - } - - protected override async Task AcceptImplementationAsync(CancellationToken cancellationToken) - { - if (cancellationToken.IsCancellationRequested) - { - return await Task.FromCanceled(cancellationToken); - } - - if (_server == null) - { - throw new TTransportException(TTransportException.ExceptionType.NotOpen, "No underlying server socket."); - } - - try - { - TClientTransport tSocketTransport = null; - var tcpClient = await _server.AcceptTcpClientAsync(); - - try - { - tSocketTransport = new TSocketClientTransport(tcpClient) - { - Timeout = _clientTimeout - }; - - if (_useBufferedSockets) - { - tSocketTransport = new TBufferedClientTransport(tSocketTransport); - } - - if (_useFramedTransport) - { - tSocketTransport = new TFramedClientTransport(tSocketTransport); - } - - return tSocketTransport; - } - catch (Exception) - { - if (tSocketTransport != null) - { - tSocketTransport.Dispose(); - } - else // Otherwise, clean it up ourselves. - { - ((IDisposable) tcpClient).Dispose(); - } - - throw; - } - } - catch (Exception ex) - { - throw new TTransportException(ex.ToString()); - } - } - - public override void Close() - { - if (_server != null) - { - try - { - _server.Stop(); - } - catch (Exception ex) - { - throw new TTransportException("WARNING: Could not close server socket: " + ex); - } - _server = null; - } - } - } -} \ No newline at end of file diff --git a/lib/netstd/Benchmarks/Thrift.Benchmarks/CompactProtocolBenchmarks.cs b/lib/netstd/Benchmarks/Thrift.Benchmarks/CompactProtocolBenchmarks.cs new file mode 100644 index 00000000000..16dcc766ef8 --- /dev/null +++ b/lib/netstd/Benchmarks/Thrift.Benchmarks/CompactProtocolBenchmarks.cs @@ -0,0 +1,75 @@ +// Licensed to the Apache Software Foundation(ASF) under one +// or more contributor license agreements.See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership.The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +using System.IO; +using System.Threading.Tasks; + +using BenchmarkDotNet.Attributes; + +using Thrift.Protocol; +using Thrift.Transport.Client; + +namespace Thrift.Benchmarks +{ + [MemoryDiagnoser] + public class CompactProtocolBenchmarks + { + private MemoryStream _Stream; + private TProtocol _Protocol; + + [Params(10000)] + public int NumberOfOperationsPerIteration { get; set; } + + [GlobalSetup] + public void GlobalSetup() + { + _Stream = new MemoryStream(); + var transport = new TStreamTransport(_Stream, _Stream, null); + _Protocol = new TCompactProtocol(transport); + } + + [GlobalCleanup] + public void GlobalCleanup() + { + _Protocol.Dispose(); + } + + [Benchmark] + public async Task WriteString() + { + for (int i = 0; i < NumberOfOperationsPerIteration; i++) + { + await _Protocol.WriteStringAsync("Thrift String Benchmark"); + + _Stream.Seek(0, SeekOrigin.Begin); + } + } + + [Benchmark] + public async Task ReadString() + { + await _Protocol.WriteStringAsync("Thrift String Benchmark"); + + for (int i = 0; i < NumberOfOperationsPerIteration; i++) + { + _Stream.Seek(0, SeekOrigin.Begin); + + await _Protocol.ReadStringAsync(); + } + } + } +} diff --git a/lib/netstd/Benchmarks/Thrift.Benchmarks/Program.cs b/lib/netstd/Benchmarks/Thrift.Benchmarks/Program.cs new file mode 100644 index 00000000000..923d73ef5fb --- /dev/null +++ b/lib/netstd/Benchmarks/Thrift.Benchmarks/Program.cs @@ -0,0 +1,29 @@ +// Licensed to the Apache Software Foundation(ASF) under one +// or more contributor license agreements.See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership.The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +using BenchmarkDotNet.Running; + +namespace Thrift.Benchmarks +{ + internal static class Program + { + public static void Main(string[] args) + { + BenchmarkSwitcher.FromAssembly(typeof(Program).Assembly).Run(args); + } + } +} diff --git a/lib/netstd/Benchmarks/Thrift.Benchmarks/Thrift.Benchmarks.csproj b/lib/netstd/Benchmarks/Thrift.Benchmarks/Thrift.Benchmarks.csproj new file mode 100644 index 00000000000..35138d8631f --- /dev/null +++ b/lib/netstd/Benchmarks/Thrift.Benchmarks/Thrift.Benchmarks.csproj @@ -0,0 +1,35 @@ + + + + + Exe + netcoreapp3.1;net48 + false + + + + + + + + + + + diff --git a/lib/netstd/Directory.Build.props b/lib/netstd/Directory.Build.props new file mode 100644 index 00000000000..3bd9541a4da --- /dev/null +++ b/lib/netstd/Directory.Build.props @@ -0,0 +1,7 @@ + + + + + + + diff --git a/lib/netstd/Makefile.am b/lib/netstd/Makefile.am new file mode 100644 index 00000000000..f1a87a24260 --- /dev/null +++ b/lib/netstd/Makefile.am @@ -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. +# + +SUBDIRS = . + +all-local: + $(DOTNETCORE) build -c Release + +check-local: + $(DOTNETCORE) test Tests/Thrift.PublicInterfaces.Compile.Tests/Thrift.PublicInterfaces.Compile.Tests.csproj + $(DOTNETCORE) test Tests/Thrift.Tests/Thrift.Tests.csproj + $(DOTNETCORE) test Tests/Thrift.IntegrationTests/Thrift.IntegrationTests.csproj + +clean-local: + $(RM) -r Thrift/bin + $(RM) -r Thrift/obj + $(RM) -r Benchmarks/Thrift.Benchmarks/bin + $(RM) -r Benchmarks/Thrift.Benchmarks/obj + $(RM) -r Tests/Thrift.Tests/bin + $(RM) -r Tests/Thrift.Tests/obj + $(RM) -r Tests/Thrift.IntegrationTests/bin + $(RM) -r Tests/Thrift.IntegrationTests/obj + $(RM) -r Tests/Thrift.PublicInterfaces.Compile.Tests/bin + $(RM) -r Tests/Thrift.PublicInterfaces.Compile.Tests/obj + +EXTRA_DIST = \ + README.md \ + Directory.Build.props \ + Benchmarks/Thrift.Benchmarks \ + Tests/Thrift.IntegrationTests/Protocols \ + Tests/Thrift.IntegrationTests/Thrift.IntegrationTests.csproj \ + Tests/Thrift.PublicInterfaces.Compile.Tests \ + Tests/Thrift.PublicInterfaces.Compile.Tests/CassandraTest.thrift \ + Tests/Thrift.PublicInterfaces.Compile.Tests/optional_required_default.thrift \ + Tests/Thrift.PublicInterfaces.Compile.Tests/Properties/AssemblyInfo.cs \ + Tests/Thrift.PublicInterfaces.Compile.Tests/Thrift.PublicInterfaces.Compile.Tests.csproj \ + Tests/Thrift.Tests/Collections \ + Tests/Thrift.Tests/DataModel \ + Tests/Thrift.Tests/Protocols \ + Tests/Thrift.Tests/Thrift.Tests.csproj \ + Thrift/Collections \ + Thrift/Processor \ + Thrift/Properties \ + Thrift/Protocol \ + Thrift/Server \ + Thrift/TApplicationException.cs \ + Thrift/TBaseClient.cs \ + Thrift/TConfiguration.cs \ + Thrift/TException.cs \ + Thrift/Thrift.csproj \ + Thrift/Transport \ + Thrift/*.snk \ + Thrift.sln \ + build.cmd \ + build.sh \ + runtests.cmd \ + runtests.sh + diff --git a/lib/netstd/README.md b/lib/netstd/README.md new file mode 100644 index 00000000000..d554e38543b --- /dev/null +++ b/lib/netstd/README.md @@ -0,0 +1,54 @@ +# Apache Thrift netstd + +Thrift client library for Microsoft .NET Standard + +# Build the library + +## How to build on Windows +- Get Thrift IDL compiler executable, add to some folder and add path to this folder into PATH variable +- Open the Thrift.sln project with Visual Studio and build. +or +- Build with scripts + +## How to build on Unix/Linux +- Ensure you have .NET Core SDK 3.1 (LTS) installed, or use the [Ubuntu docker image](../../build/docker/README.md) +- Follow common automake build practice: `./ bootstrap && ./ configure && make` + +## Known issues +- In trace logging mode you can see some not important internal exceptions + +# Migration to netstd + +## ... from netcore + +If you are migrating your code from netcore library, you will have to: + +- Switch to `thrift -gen netstd` +- the following compiler flags are no longer needed or supported: `hashcode` is now standard, while `nullable` is no longer supported. +- the `Thrift.Transport` and `Thrift.Protocol` namespaces now use the singular form +- add `using Thrift.Processor;` in the server code where appropriate +- rename all `T*ClientTransport` to `T*Transport` +- rename all `TBaseServer` occurrences in your code to `TServer` +- the `SingletonTProcessorFactory` is now called `TSingletonProcessorFactory` +- and the `AsyncBaseServer` is now the `TSimpleAsyncServer` + +You may wonder why we changed so many names. The naming scheme has been revised for two reasons: First, we want to get back the established, well-known naming consistency across the Thrift libraries which the netcore library did not fully respect. Second, by achieving that first objective, we get the additional benefit of making migration at least a bit easier for C# projects. + +## ... from csharp + +Because of the different environment requirements, migration from C# takes slightly more efforts. While the code changes related to Thrift itself are moderate, you may need to upgrade certain dependencies, components or even modules to more recent versions. + +1. Client and server applications must use at least framework 4.6.1, any version below will not work. +1. Switch to `thrift -gen netstd`. The following compiler flags are no longer needed or supported: `hashcode` and `async` are now standard, while `nullable` is no longer supported. +1. [Familiarize yourself with the `async/await` model](https://msdn.microsoft.com/en-us/magazine/jj991977.aspx), if you have not already done so. As netstd does not support `ISync` anymore, async is mandatory. The synchronous model is simply no longer available (that's also the reason why we don't need the `async` flag anymore). +1. Consider proper use of `cancellationToken` parameters. They are optional but may be quite helpful. +1. As you probably already guessed, there are a few names that have been changed: +- add `using Thrift.Processor;` in the server code where appropriate +- the `TServerSocket` is now called `TServerSocketTransport` +- change `IProtocolFactory` into `ITProtocolFactory` +- if you are looking for `TSimpleServer`, try `TSimpleAsyncServer` instead +- similarly, the `TThreadPoolServer` is now a `TThreadPoolAsyncServer` +- the server's `Serve()` method does now `ServeAsync()` +- In case you are using Thrift server event handlers: the `SetEventHandler` method now starts with an uppercase letter +- and you will also have to revise the method names of all `TServerEventHandler` descendants you have in your code + diff --git a/lib/netcore/Tests/Thrift.IntegrationTests/Protocols/ProtocolsOperationsTests.cs b/lib/netstd/Tests/Thrift.IntegrationTests/Protocols/ProtocolsOperationsTests.cs similarity index 87% rename from lib/netcore/Tests/Thrift.IntegrationTests/Protocols/ProtocolsOperationsTests.cs rename to lib/netstd/Tests/Thrift.IntegrationTests/Protocols/ProtocolsOperationsTests.cs index bc4afa156c7..62ab31780b2 100644 --- a/lib/netcore/Tests/Thrift.IntegrationTests/Protocols/ProtocolsOperationsTests.cs +++ b/lib/netstd/Tests/Thrift.IntegrationTests/Protocols/ProtocolsOperationsTests.cs @@ -21,9 +21,9 @@ using System.Threading.Tasks; using KellermanSoftware.CompareNetObjects; using Microsoft.VisualStudio.TestTools.UnitTesting; -using Thrift.Protocols; -using Thrift.Protocols.Entities; -using Thrift.Transports.Client; +using Thrift.Protocol; +using Thrift.Protocol.Entities; +using Thrift.Transport.Client; namespace Thrift.IntegrationTests.Protocols { @@ -31,6 +31,7 @@ namespace Thrift.IntegrationTests.Protocols public class ProtocolsOperationsTests { private readonly CompareLogic _compareLogic = new CompareLogic(); + private static readonly TConfiguration Configuration = null; // or new TConfiguration() if needed [DataTestMethod] [DataRow(typeof(TBinaryProtocol), TMessageType.Call)] @@ -56,13 +57,13 @@ public async Task WriteReadMessage_Test(Type protocolType, TMessageType messageT { var protocol = tuple.Item2; - await protocol.WriteMessageBeginAsync(expected); - await protocol.WriteMessageEndAsync(); + await protocol.WriteMessageBeginAsync(expected, default); + await protocol.WriteMessageEndAsync(default); stream.Seek(0, SeekOrigin.Begin); - var actualMessage = await protocol.ReadMessageBeginAsync(); - await protocol.ReadMessageEndAsync(); + var actualMessage = await protocol.ReadMessageBeginAsync(default); + await protocol.ReadMessageEndAsync(default); var result = _compareLogic.Compare(expected, actualMessage); Assert.IsTrue(result.AreEqual, result.DifferencesString); @@ -90,13 +91,13 @@ public async Task WriteReadStruct_Test(Type protocolType) { var protocol = tuple.Item2; - await protocol.WriteStructBeginAsync(expected); - await protocol.WriteStructEndAsync(); + await protocol.WriteStructBeginAsync(expected, default); + await protocol.WriteStructEndAsync(default); stream?.Seek(0, SeekOrigin.Begin); - var actual = await protocol.ReadStructBeginAsync(); - await protocol.ReadStructEndAsync(); + var actual = await protocol.ReadStructBeginAsync(default); + await protocol.ReadStructEndAsync(default); var result = _compareLogic.Compare(expected, actual); Assert.IsTrue(result.AreEqual, result.DifferencesString); @@ -125,13 +126,13 @@ public async Task WriteReadField_Test(Type protocolType) { var protocol = tuple.Item2; - await protocol.WriteFieldBeginAsync(expected); - await protocol.WriteFieldEndAsync(); + await protocol.WriteFieldBeginAsync(expected, default); + await protocol.WriteFieldEndAsync(default); stream?.Seek(0, SeekOrigin.Begin); - var actual = await protocol.ReadFieldBeginAsync(); - await protocol.ReadFieldEndAsync(); + var actual = await protocol.ReadFieldBeginAsync(default); + await protocol.ReadFieldEndAsync(default); var result = _compareLogic.Compare(expected, actual); Assert.IsTrue(result.AreEqual, result.DifferencesString); @@ -158,13 +159,13 @@ public async Task WriteReadMap_Test(Type protocolType) { var protocol = tuple.Item2; - await protocol.WriteMapBeginAsync(expected); - await protocol.WriteMapEndAsync(); + await protocol.WriteMapBeginAsync(expected, default); + await protocol.WriteMapEndAsync(default); stream?.Seek(0, SeekOrigin.Begin); - var actual = await protocol.ReadMapBeginAsync(); - await protocol.ReadMapEndAsync(); + var actual = await protocol.ReadMapBeginAsync(default); + await protocol.ReadMapEndAsync(default); var result = _compareLogic.Compare(expected, actual); Assert.IsTrue(result.AreEqual, result.DifferencesString); @@ -192,13 +193,13 @@ public async Task WriteReadList_Test(Type protocolType) { var protocol = tuple.Item2; - await protocol.WriteListBeginAsync(expected); - await protocol.WriteListEndAsync(); + await protocol.WriteListBeginAsync(expected, default); + await protocol.WriteListEndAsync(default); stream?.Seek(0, SeekOrigin.Begin); - var actual = await protocol.ReadListBeginAsync(); - await protocol.ReadListEndAsync(); + var actual = await protocol.ReadListBeginAsync(default); + await protocol.ReadListEndAsync(default); var result = _compareLogic.Compare(expected, actual); Assert.IsTrue(result.AreEqual, result.DifferencesString); @@ -225,13 +226,13 @@ public async Task WriteReadSet_Test(Type protocolType) { var protocol = tuple.Item2; - await protocol.WriteSetBeginAsync(expected); - await protocol.WriteSetEndAsync(); + await protocol.WriteSetBeginAsync(expected, default); + await protocol.WriteSetEndAsync(default); stream?.Seek(0, SeekOrigin.Begin); - var actual = await protocol.ReadSetBeginAsync(); - await protocol.ReadSetEndAsync(); + var actual = await protocol.ReadSetBeginAsync(default); + await protocol.ReadSetEndAsync(default); var result = _compareLogic.Compare(expected, actual); Assert.IsTrue(result.AreEqual, result.DifferencesString); @@ -258,11 +259,11 @@ public async Task WriteReadBool_Test(Type protocolType) { var protocol = tuple.Item2; - await protocol.WriteBoolAsync(expected); + await protocol.WriteBoolAsync(expected, default); stream?.Seek(0, SeekOrigin.Begin); - var actual = await protocol.ReadBoolAsync(); + var actual = await protocol.ReadBoolAsync(default); var result = _compareLogic.Compare(expected, actual); Assert.IsTrue(result.AreEqual, result.DifferencesString); @@ -289,11 +290,11 @@ public async Task WriteReadByte_Test(Type protocolType) { var protocol = tuple.Item2; - await protocol.WriteByteAsync(expected); + await protocol.WriteByteAsync(expected, default); stream?.Seek(0, SeekOrigin.Begin); - var actual = await protocol.ReadByteAsync(); + var actual = await protocol.ReadByteAsync(default); var result = _compareLogic.Compare(expected, actual); Assert.IsTrue(result.AreEqual, result.DifferencesString); @@ -320,11 +321,11 @@ public async Task WriteReadI16_Test(Type protocolType) { var protocol = tuple.Item2; - await protocol.WriteI16Async(expected); + await protocol.WriteI16Async(expected, default); stream?.Seek(0, SeekOrigin.Begin); - var actual = await protocol.ReadI16Async(); + var actual = await protocol.ReadI16Async(default); var result = _compareLogic.Compare(expected, actual); Assert.IsTrue(result.AreEqual, result.DifferencesString); @@ -351,11 +352,11 @@ public async Task WriteReadI32_Test(Type protocolType) { var protocol = tuple.Item2; - await protocol.WriteI32Async(expected); + await protocol.WriteI32Async(expected, default); stream?.Seek(0, SeekOrigin.Begin); - var actual = await protocol.ReadI32Async(); + var actual = await protocol.ReadI32Async(default); var result = _compareLogic.Compare(expected, actual); Assert.IsTrue(result.AreEqual, result.DifferencesString); @@ -382,11 +383,11 @@ public async Task WriteReadI64_Test(Type protocolType) { var protocol = tuple.Item2; - await protocol.WriteI64Async(expected); + await protocol.WriteI64Async(expected, default); stream?.Seek(0, SeekOrigin.Begin); - var actual = await protocol.ReadI64Async(); + var actual = await protocol.ReadI64Async(default); var result = _compareLogic.Compare(expected, actual); Assert.IsTrue(result.AreEqual, result.DifferencesString); @@ -413,11 +414,11 @@ public async Task WriteReadDouble_Test(Type protocolType) { var protocol = tuple.Item2; - await protocol.WriteDoubleAsync(expected); + await protocol.WriteDoubleAsync(expected, default); stream?.Seek(0, SeekOrigin.Begin); - var actual = await protocol.ReadDoubleAsync(); + var actual = await protocol.ReadDoubleAsync(default); var result = _compareLogic.Compare(expected, actual); Assert.IsTrue(result.AreEqual, result.DifferencesString); @@ -444,11 +445,11 @@ public async Task WriteReadString_Test(Type protocolType) { var protocol = tuple.Item2; - await protocol.WriteStringAsync(expected); + await protocol.WriteStringAsync(expected, default); stream?.Seek(0, SeekOrigin.Begin); - var actual = await protocol.ReadStringAsync(); + var actual = await protocol.ReadStringAsync(default); var result = _compareLogic.Compare(expected, actual); Assert.IsTrue(result.AreEqual, result.DifferencesString); @@ -475,11 +476,11 @@ public async Task WriteReadBinary_Test(Type protocolType) { var protocol = tuple.Item2; - await protocol.WriteBinaryAsync(expected); + await protocol.WriteBinaryAsync(expected, default); stream?.Seek(0, SeekOrigin.Begin); - var actual = await protocol.ReadBinaryAsync(); + var actual = await protocol.ReadBinaryAsync(default); var result = _compareLogic.Compare(expected, actual); Assert.IsTrue(result.AreEqual, result.DifferencesString); @@ -494,7 +495,7 @@ public async Task WriteReadBinary_Test(Type protocolType) private static Tuple GetProtocolInstance(Type protocolType) { var memoryStream = new MemoryStream(); - var streamClientTransport = new TStreamClientTransport(memoryStream, memoryStream); + var streamClientTransport = new TStreamTransport(memoryStream, memoryStream,Configuration); var protocol = (TProtocol) Activator.CreateInstance(protocolType, streamClientTransport); return new Tuple(memoryStream, protocol); } diff --git a/lib/netstd/Tests/Thrift.IntegrationTests/Thrift.IntegrationTests.csproj b/lib/netstd/Tests/Thrift.IntegrationTests/Thrift.IntegrationTests.csproj new file mode 100644 index 00000000000..7c5639bc7f7 --- /dev/null +++ b/lib/netstd/Tests/Thrift.IntegrationTests/Thrift.IntegrationTests.csproj @@ -0,0 +1,48 @@ + + + + + netcoreapp3.1 + Thrift.IntegrationTests + Thrift.IntegrationTests + Exe + false + false + false + false + false + false + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/lib/netcore/Tests/Thrift.PublicInterfaces.Compile.Tests/CassandraTest.thrift b/lib/netstd/Tests/Thrift.PublicInterfaces.Compile.Tests/CassandraTest.thrift similarity index 99% rename from lib/netcore/Tests/Thrift.PublicInterfaces.Compile.Tests/CassandraTest.thrift rename to lib/netstd/Tests/Thrift.PublicInterfaces.Compile.Tests/CassandraTest.thrift index 4b92720c2d4..26cb380c5a4 100644 --- a/lib/netcore/Tests/Thrift.PublicInterfaces.Compile.Tests/CassandraTest.thrift +++ b/lib/netstd/Tests/Thrift.PublicInterfaces.Compile.Tests/CassandraTest.thrift @@ -23,7 +23,7 @@ # Interface definition for Cassandra Service # -namespace netcore Apache.Cassandra.Test +namespace netstd Apache.Cassandra.Test # Thrift.rb has a bug where top-level modules that include modules # with the same name are not properly referenced, so we can't do diff --git a/lib/netstd/Tests/Thrift.PublicInterfaces.Compile.Tests/Impl/Thrift5253/MyService.cs b/lib/netstd/Tests/Thrift.PublicInterfaces.Compile.Tests/Impl/Thrift5253/MyService.cs new file mode 100644 index 00000000000..342dd4ad020 --- /dev/null +++ b/lib/netstd/Tests/Thrift.PublicInterfaces.Compile.Tests/Impl/Thrift5253/MyService.cs @@ -0,0 +1,59 @@ +// Licensed to the Apache Software Foundation(ASF) under one +// or more contributor license agreements.See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership.The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +using System; +using System.Collections.Generic; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using Thrift5253; + +namespace Thrift.PublicInterfaces.Compile.Tests.Impl.Thrift5253 +{ + class MyServiceImpl : MyService.IAsync + { + public Task AsyncProcessorAsync(AsyncProcessor input, CancellationToken cancellationToken = default) + { + return Task.FromResult(new AsyncProcessor() { Foo = input.Foo }); + } + + public Task BrokenAsync(BrokenArgs input, CancellationToken cancellationToken = default) + { + return Task.FromResult(new BrokenResult() { Foo = input.Foo }); + } + + public Task ClientAsync(Client input, CancellationToken cancellationToken = default) + { + return Task.FromResult(new Client() { Foo = input.Foo }); + } + + public Task IAsyncAsync(IAsync input, CancellationToken cancellationToken = default) + { + return Task.FromResult(new IAsync() { Foo = input.Foo }); + } + + public Task InternalStructsAsync(InternalStructs input, CancellationToken cancellationToken = default) + { + return Task.FromResult(new InternalStructs() { Foo = input.Foo }); + } + + public Task WorksAsync(WorksArrrgs input, CancellationToken cancellationToken = default) + { + return Task.FromResult(new WorksRslt() { Foo = input.Foo }); + } + } +} diff --git a/lib/netcore/Tests/Thrift.PublicInterfaces.Compile.Tests/Properties/AssemblyInfo.cs b/lib/netstd/Tests/Thrift.PublicInterfaces.Compile.Tests/Properties/AssemblyInfo.cs similarity index 100% rename from lib/netcore/Tests/Thrift.PublicInterfaces.Compile.Tests/Properties/AssemblyInfo.cs rename to lib/netstd/Tests/Thrift.PublicInterfaces.Compile.Tests/Properties/AssemblyInfo.cs diff --git a/lib/netstd/Tests/Thrift.PublicInterfaces.Compile.Tests/Thrift.PublicInterfaces.Compile.Tests.csproj b/lib/netstd/Tests/Thrift.PublicInterfaces.Compile.Tests/Thrift.PublicInterfaces.Compile.Tests.csproj new file mode 100644 index 00000000000..c43c71cd720 --- /dev/null +++ b/lib/netstd/Tests/Thrift.PublicInterfaces.Compile.Tests/Thrift.PublicInterfaces.Compile.Tests.csproj @@ -0,0 +1,63 @@ + + + + + netcoreapp3.1 + Thrift.PublicInterfaces.Compile.Tests + Thrift.PublicInterfaces.Compile.Tests + false + false + false + false + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/lib/netstd/Tests/Thrift.PublicInterfaces.Compile.Tests/Thrift5253.thrift b/lib/netstd/Tests/Thrift.PublicInterfaces.Compile.Tests/Thrift5253.thrift new file mode 100644 index 00000000000..ee3df9b7955 --- /dev/null +++ b/lib/netstd/Tests/Thrift.PublicInterfaces.Compile.Tests/Thrift5253.thrift @@ -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. + +// Testcase for THRIFT-5253 using Result in result name generates wrong IAsync interface + +namespace * Thrift5253 + + +// this works +struct WorksArrrgs { 1: i32 foo } +struct WorksRslt { 1: i32 foo } + +// this does not +struct BrokenResult{ 1: i32 foo } +struct BrokenArgs { 1: i32 foo } + +struct InternalStructs { 1: optional i32 foo } +struct AsyncProcessor { 1: optional i32 foo } +struct Client { 1: optional i32 foo } +struct IAsync { 1: optional i32 foo } + +struct ReservedMemberName { 1: optional i32 Isset } + +service MyService{ + BrokenResult Broken( 1 : BrokenArgs foo) + WorksRslt Works( 1 : WorksArrrgs foo) + + InternalStructs InternalStructs( 1: InternalStructs foo) + AsyncProcessor AsyncProcessor ( 1: AsyncProcessor foo) + Client Client ( 1: Client foo) + IAsync IAsync ( 1: IAsync foo) +} + diff --git a/lib/netstd/Tests/Thrift.PublicInterfaces.Compile.Tests/Thrift5320.enum.thrift b/lib/netstd/Tests/Thrift.PublicInterfaces.Compile.Tests/Thrift5320.enum.thrift new file mode 100644 index 00000000000..d4392d6a83a --- /dev/null +++ b/lib/netstd/Tests/Thrift.PublicInterfaces.Compile.Tests/Thrift5320.enum.thrift @@ -0,0 +1,25 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +// Testcase for THRIFT-5320 Usage of "Task" as IDL identifier generates uncompileable code + +namespace * Thrift5320.enums + +enum Task { + Zero, + More +} + diff --git a/lib/netstd/Tests/Thrift.PublicInterfaces.Compile.Tests/Thrift5320.exception.thrift b/lib/netstd/Tests/Thrift.PublicInterfaces.Compile.Tests/Thrift5320.exception.thrift new file mode 100644 index 00000000000..b6b8cdab150 --- /dev/null +++ b/lib/netstd/Tests/Thrift.PublicInterfaces.Compile.Tests/Thrift5320.exception.thrift @@ -0,0 +1,27 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +// Testcase for THRIFT-5320 Usage of "Task" as IDL identifier generates uncompileable code + +namespace * Thrift5320.exceptions + + + +exception Task { + 1: Task left + 2: Task right +} + diff --git a/lib/netstd/Tests/Thrift.PublicInterfaces.Compile.Tests/Thrift5320.struct.thrift b/lib/netstd/Tests/Thrift.PublicInterfaces.Compile.Tests/Thrift5320.struct.thrift new file mode 100644 index 00000000000..771c487023f --- /dev/null +++ b/lib/netstd/Tests/Thrift.PublicInterfaces.Compile.Tests/Thrift5320.struct.thrift @@ -0,0 +1,26 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +// Testcase for THRIFT-5320 Usage of "Task" as IDL identifier generates uncompileable code + +namespace * Thrift5320.structs + + +struct Task { + 1: Task left + 2: Task right +} + diff --git a/lib/netstd/Tests/Thrift.PublicInterfaces.Compile.Tests/Thrift5320.thrift b/lib/netstd/Tests/Thrift.PublicInterfaces.Compile.Tests/Thrift5320.thrift new file mode 100644 index 00000000000..f677973b02f --- /dev/null +++ b/lib/netstd/Tests/Thrift.PublicInterfaces.Compile.Tests/Thrift5320.thrift @@ -0,0 +1,40 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +// Testcase for THRIFT-5320 Usage of "Task" as IDL identifier generates uncompileable code + +namespace * Thrift5320.Task + +include "Thrift5320.enum.thrift" +include "Thrift5320.exception.thrift" +include "Thrift5320.struct.thrift" + +enum Foobar { + Task = 0 +} + + +service Task { + Thrift5320.enum.Task Task( + 1 : Thrift5320.struct.Task foo, + 2: Foobar bar + ) throws ( + 1: Thrift5320.exception.Task error + ) +} + + + diff --git a/lib/netstd/Tests/Thrift.PublicInterfaces.Compile.Tests/optional_required_default.thrift b/lib/netstd/Tests/Thrift.PublicInterfaces.Compile.Tests/optional_required_default.thrift new file mode 100644 index 00000000000..1f6c7ee5ead --- /dev/null +++ b/lib/netstd/Tests/Thrift.PublicInterfaces.Compile.Tests/optional_required_default.thrift @@ -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. + +// Testcase for THRIFT-5216 generate DeepCopy methods + +namespace netstd OptReqDefTest + +enum Distance +{ + foo = 0, + bar = 1, + baz = 2 +} + +struct RaceDetails +{ + // this is really the max field index used here, intentionally placed at the beginning + 666: required Distance triplesix + + // without default values + + 1: optional Distance opt_one + 2: optional double opt_two + 3: optional i16 opt_three + 4: optional string opt_four + 5: optional binary opt_five + 6: optional list opt_six + 7: optional set opt_seven + 8: optional map opt_eight + + 11: required Distance req_one + 12: required double req_two + 13: required i16 req_three + 14: required string req_four + 15: required binary req_five + 16: required list req_six + 17: required set req_seven + 18: required map req_eight + + 21: Distance def_one + 22: double def_two + 23: i16 def_three + 24: string def_four + 25: binary def_five + 26: list def_six + 27: set def_seven + 28: map def_eight + + // having default values + + 31: optional Distance opt_one_with_value = Distance.bar + 32: optional double opt_two_with_value = 2.22 + 33: optional i16 opt_three_with_value = 3 + 34: optional string opt_four_with_value = "four" + 35: optional binary opt_five_with_value = "five\t" + 36: optional list opt_six_with_value = [6] + 37: optional set opt_seven_with_value = [7] + 38: optional map opt_eight_with_value = { 8 : 8 } + + 41: required Distance req_one_with_value = Distance.bar + 42: required double req_two_with_value = 2.22 + 43: required i16 req_three_with_value = 3 + 44: required string req_four_with_value = "four" + 45: required binary req_five_with_value = "five" + 46: required list req_six_with_value = [6] + 47: required set req_seven_with_value = [7] + 48: required map req_eight_with_value = { 8 : 8 } + + 51: Distance def_one_with_value = Distance.bar + 52: double def_two_with_value = 2.22 + 53: i16 def_three_with_value = 3 + 54: string def_four_with_value = "four" + 55: binary def_five_with_value = "five" + 56: list def_six_with_value = [6] + 57: set def_seven_with_value = [7] + 58: map def_eight_with_value = { 8 : 8 } + + 90: optional bool last_of_the_mohicans + + // some more complicated ones, including recursion + + 300: required list far_list + 301: optional set far_set + 302: map far_map + + 310: required set> far_set_list + 311: optional list>> far_list_map_set + 312: map far_map_dist_to_rds + + 320: required RaceDetails req_nested + 321: optional RaceDetails opt_nested + 322: RaceDetails def_nested + + 330: required jack req_union + 331: optional jack opt_union + 332: jack def_union +} + +union jack { + 1: list stars + 2: list stripes + + 310: set> far_set_list + 311: list>> far_list_map_set + 312: map far_map_dist_to_rds + + 320: jack nested_union + 321: RaceDetails nested_struct + + 401: optional Distance opt_one + 402: optional double opt_two + 403: optional i16 opt_three + 404: optional string opt_four + 405: optional binary opt_five + 406: optional list opt_six + 407: optional set opt_seven + 408: optional map opt_eight +} + +typedef RaceDetails RaceDetails2 +typedef list RDs + +exception CrashBoomBang { + 1 : i32 MyErrorCode +} + +service foobar { + set>> DoItNow( 1 : list>> rd, 2: i32 mitDefault = 42) throws (1: CrashBoomBang cbb) +} + diff --git a/lib/netstd/Tests/Thrift.Tests/Collections/TCollectionsTests.cs b/lib/netstd/Tests/Thrift.Tests/Collections/TCollectionsTests.cs new file mode 100644 index 00000000000..061032ae0a3 --- /dev/null +++ b/lib/netstd/Tests/Thrift.Tests/Collections/TCollectionsTests.cs @@ -0,0 +1,240 @@ +// Licensed to the Apache Software Foundation(ASF) under one +// or more contributor license agreements.See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership.The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Security.Cryptography.Xml; +using System.Text; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Thrift.Collections; + +namespace Thrift.Tests.Collections +{ + // ReSharper disable once InconsistentNaming + [TestClass] + public class TCollectionsTests + { + //TODO: Add tests for IEnumerable with objects and primitive values inside + + [TestMethod] + public void TCollection_List_Equals_Primitive_Test() + { + var collection1 = new List {1,2,3}; + var collection2 = new List {1,2,3}; + Assert.IsTrue(TCollections.Equals(collection1, collection2)); + Assert.IsTrue(collection1.SequenceEqual(collection2)); + } + + [TestMethod] + public void TCollection_List_Equals_Primitive_Different_Test() + { + var collection1 = new List { 1, 2, 3 }; + var collection2 = new List { 1, 2 }; + Assert.IsFalse(TCollections.Equals(collection1, collection2)); + Assert.IsFalse(collection1.SequenceEqual(collection2)); + + collection2.Add(4); + Assert.IsFalse(TCollections.Equals(collection1, collection2)); + Assert.IsFalse(collection1.SequenceEqual(collection2)); + } + + [TestMethod] + public void TCollection_List_Equals_Objects_Test() + { + var collection1 = new List { new ExampleClass { X = 1 }, new ExampleClass { X = 2 } }; + var collection2 = new List { new ExampleClass { X = 1 }, new ExampleClass { X = 2 } }; + Assert.IsTrue(TCollections.Equals(collection1, collection2)); + Assert.IsTrue(collection1.SequenceEqual(collection2)); + } + + [TestMethod] + public void TCollection_List_List_Equals_Objects_Test() + { + var collection1 = new List> { new List { new ExampleClass { X = 1 }, new ExampleClass { X = 2 } } }; + var collection2 = new List> { new List { new ExampleClass { X = 1 }, new ExampleClass { X = 2 } } }; + Assert.IsTrue(TCollections.Equals(collection1, collection2)); + Assert.IsFalse(collection1.SequenceEqual(collection2)); // SequenceEqual() calls Equals() of the inner list instead of SequenceEqual() + } + + [TestMethod] + public void TCollection_List_Equals_OneAndTheSameObject_Test() + { + var collection1 = new List { new ExampleClass { X = 1 }, new ExampleClass { X = 2 } }; + var collection2 = collection1; + Assert.IsTrue(TCollections.Equals(collection1, collection2)); + Assert.IsTrue(collection1.SequenceEqual(collection2)); + } + + [TestMethod] + public void TCollection_Set_Equals_Primitive_Test() + { + var collection1 = new THashSet {1,2,3}; + var collection2 = new THashSet {1,2,3}; + Assert.IsTrue(TCollections.Equals(collection1, collection2)); + Assert.IsTrue(collection1.SequenceEqual(collection2)); + } + + [TestMethod] + public void TCollection_Set_Equals_Primitive_Different_Test() + { + var collection1 = new THashSet { 1, 2, 3 }; + var collection2 = new THashSet { 1, 2 }; + Assert.IsFalse(TCollections.Equals(collection1, collection2)); + Assert.IsFalse(collection1.SequenceEqual(collection2)); + + collection2.Add(4); + Assert.IsFalse(TCollections.Equals(collection1, collection2)); + Assert.IsFalse(collection1.SequenceEqual(collection2)); + } + + [TestMethod] + public void TCollection_Set_Equals_Objects_Test() + { + var collection1 = new THashSet { new ExampleClass { X = 1 }, new ExampleClass { X = 2 } }; + var collection2 = new THashSet { new ExampleClass { X = 1 }, new ExampleClass { X = 2 } }; + Assert.IsTrue(TCollections.Equals(collection1, collection2)); + Assert.IsTrue(collection1.SequenceEqual(collection2)); + } + + [TestMethod] + public void TCollection_Set_Set_Equals_Objects_Test() + { + var collection1 = new THashSet> { new THashSet { new ExampleClass { X = 1 }, new ExampleClass { X = 2 } } }; + var collection2 = new THashSet> { new THashSet { new ExampleClass { X = 1 }, new ExampleClass { X = 2 } } }; + Assert.IsTrue(TCollections.Equals(collection1, collection2)); + Assert.IsFalse(collection1.SequenceEqual(collection2)); // SequenceEqual() calls Equals() of the inner list instead of SequenceEqual() + } + + [TestMethod] + public void TCollection_Set_Equals_OneAndTheSameObject_Test() + { + var collection1 = new THashSet { new ExampleClass { X = 1 }, new ExampleClass { X = 2 } }; + var collection2 = collection1; // references to one and the same collection + Assert.IsTrue(TCollections.Equals(collection1, collection2)); + Assert.IsTrue(collection1.SequenceEqual(collection2)); + } + + + [TestMethod] + public void TCollection_Map_Equals_Primitive_Test() + { + var collection1 = new Dictionary { [1] = 1, [2] = 2, [3] = 3 }; + var collection2 = new Dictionary { [1] = 1, [2] = 2, [3] = 3 }; + Assert.IsTrue(TCollections.Equals(collection1, collection2)); + Assert.IsTrue(collection1.SequenceEqual(collection2)); + } + + [TestMethod] + public void TCollection_Map_Equals_Primitive_Different_Test() + { + var collection1 = new Dictionary { [1] = 1, [2] = 2, [3] = 3 }; + var collection2 = new Dictionary { [1] = 1, [2] = 2 }; + Assert.IsFalse(TCollections.Equals(collection1, collection2)); + Assert.IsFalse(collection1.SequenceEqual(collection2)); + + collection2[3] = 3; + Assert.IsTrue(TCollections.Equals(collection1, collection2)); + Assert.IsTrue(collection1.SequenceEqual(collection2)); + + collection2[3] = 4; + Assert.IsFalse(TCollections.Equals(collection1, collection2)); + } + + [TestMethod] + public void TCollection_Map_Equals_Objects_Test() + { + var collection1 = new Dictionary + { + [1] = new ExampleClass { X = 1 }, + [-1] = new ExampleClass { X = 2 } + }; + var collection2 = new Dictionary + { + [1] = new ExampleClass { X = 1 }, + [-1] = new ExampleClass { X = 2 } + }; + + Assert.IsTrue(TCollections.Equals(collection1, collection2)); + Assert.IsTrue(collection1.SequenceEqual(collection2)); + } + + [TestMethod] + public void TCollection_Map_Map_Equals_Objects_Test() + { + var collection1 = new Dictionary> + { + [0] = new Dictionary + { + [1] = new ExampleClass { X = 1 }, + [-1] = new ExampleClass { X = 2 } + } + }; + var collection2 = new Dictionary> + { + [0] = new Dictionary + { + [1] = new ExampleClass { X = 1 }, + [-1] = new ExampleClass { X = 2 } + } + }; + + Assert.IsTrue(TCollections.Equals(collection1, collection2)); + Assert.IsFalse(collection1.SequenceEqual(collection2)); // SequenceEqual() calls Equals() of the inner list instead of SequenceEqual() + } + + [TestMethod] + public void TCollection_Map_Equals_OneAndTheSameObject_Test() + { + var collection1 = new Dictionary + { + [1] = new ExampleClass { X = 1 }, + [-1] = new ExampleClass { X = 2 } + }; + var collection2 = collection1; + Assert.IsTrue(TCollections.Equals(collection1, collection2)); + Assert.IsTrue(collection1.SequenceEqual(collection2)); + } + + + private class ExampleClass + { + public int X { get; set; } + + // all Thrift-generated classes override Equals(), we do just the same + public override bool Equals(object that) + { + if (!(that is ExampleClass other)) return false; + if (ReferenceEquals(this, other)) return true; + + return this.X == other.X; + } + + // overriding Equals() requires GetHashCode() as well + public override int GetHashCode() + { + int hashcode = 157; + unchecked + { + hashcode = (hashcode * 397) + X.GetHashCode(); + } + return hashcode; + } + } + } +} + diff --git a/lib/netcore/Tests/Thrift.Tests/Collections/THashSetTests.cs b/lib/netstd/Tests/Thrift.Tests/Collections/THashSetTests.cs similarity index 100% rename from lib/netcore/Tests/Thrift.Tests/Collections/THashSetTests.cs rename to lib/netstd/Tests/Thrift.Tests/Collections/THashSetTests.cs diff --git a/lib/netstd/Tests/Thrift.Tests/DataModel/DeepCopy.cs b/lib/netstd/Tests/Thrift.Tests/DataModel/DeepCopy.cs new file mode 100644 index 00000000000..f717b4ddaa0 --- /dev/null +++ b/lib/netstd/Tests/Thrift.Tests/DataModel/DeepCopy.cs @@ -0,0 +1,603 @@ +// Licensed to the Apache Software Foundation(ASF) under one +// or more contributor license agreements.See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership.The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using System.Text; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using OptReqDefTest; +using Thrift.Collections; + +namespace Thrift.Tests.DataModel +{ + // ReSharper disable once InconsistentNaming + [TestClass] + public class DeepCopyTests + { + [TestMethod] + public void Test_Complex_DeepCopy() + { + var first = InitializeInstance(new RaceDetails()); + VerifyIdenticalContent(first, InitializeInstance(new RaceDetails())); + + var second = first.DeepCopy(); + VerifyIdenticalContent(first, second); + ModifyInstance(second,0); + VerifyDifferentContent(first, second); + VerifyIdenticalContent(first, InitializeInstance(new RaceDetails())); + + var third = second.DeepCopy(); + VerifyIdenticalContent(second, third); + ModifyInstance(third,0); + VerifyDifferentContent(second, third); + VerifyIdenticalContent(first, InitializeInstance(new RaceDetails())); + } + + private RaceDetails MakeNestedRaceDetails(int nesting) + { + if (++nesting > 1) + return null; + + var instance = new RaceDetails(); + InitializeInstance(instance,nesting); + return instance; + } + + private jack MakeNestedUnion(int nesting) + { + if (++nesting > 1) + return null; + + var details = new RaceDetails(); + InitializeInstance(details,nesting); + return new jack.nested_struct(details); + } + + + private RaceDetails InitializeInstance(RaceDetails instance, int nesting = 0) + { + // at init, we intentionally leave all non-required fields unset + Assert.IsFalse(instance.__isset.opt_one); + Assert.IsFalse(instance.__isset.opt_two); + Assert.IsFalse(instance.__isset.opt_three); + Assert.IsFalse(instance.__isset.opt_four); + Assert.IsFalse(instance.__isset.opt_five); + Assert.IsFalse(instance.__isset.opt_six); + Assert.IsFalse(instance.__isset.opt_seven); + Assert.IsFalse(instance.__isset.opt_eight); + + // set all required to null/default + instance.Req_one = default; + instance.Req_two = default; + instance.Req_three = default; + instance.Req_four = default; + instance.Req_five = default; + instance.Req_six = default; + instance.Req_seven = default;; + instance.Req_eight = default; + + // leave non-required fields unset again + Assert.IsFalse(instance.__isset.def_one); + Assert.IsFalse(instance.__isset.def_two); + Assert.IsFalse(instance.__isset.def_three); + Assert.IsFalse(instance.__isset.def_four); + Assert.IsFalse(instance.__isset.def_five); + Assert.IsFalse(instance.__isset.def_six); + Assert.IsFalse(instance.__isset.def_seven); + Assert.IsFalse(instance.__isset.def_eight); + + // these should have IDL defaults set + + Assert.IsTrue(instance.__isset.opt_one_with_value); + Assert.IsTrue(instance.__isset.opt_two_with_value); + Assert.IsTrue(instance.__isset.opt_three_with_value); + Assert.IsTrue(instance.__isset.opt_four_with_value); + Assert.IsTrue(instance.__isset.opt_five_with_value); + Assert.IsTrue(instance.__isset.opt_six_with_value); + Assert.IsTrue(instance.__isset.opt_seven_with_value); + Assert.IsTrue(instance.__isset.opt_eight_with_value); + + Assert.AreEqual(instance.Req_one_with_value, (Distance)1); + Assert.AreEqual(instance.Req_two_with_value, 2.22); + Assert.AreEqual(instance.Req_three_with_value, 3); + Assert.AreEqual(instance.Req_four_with_value, "four"); + Assert.AreEqual("five", Encoding.UTF8.GetString(instance.Req_five_with_value)); + + Assert.IsTrue(instance.Req_six_with_value.Count == 1); + Assert.AreEqual(instance.Req_six_with_value[0], 6 ); + + Assert.IsTrue(instance.Req_seven_with_value.Count == 1); + Assert.IsTrue(instance.Req_seven_with_value.Contains(7)); + + Assert.IsTrue(instance.Req_eight_with_value.Count == 1); + Assert.IsTrue(instance.Req_eight_with_value[8] == 8); + + Assert.IsTrue(instance.__isset.def_one_with_value); + Assert.IsTrue(instance.__isset.def_two_with_value); + Assert.IsTrue(instance.__isset.def_three_with_value); + Assert.IsTrue(instance.__isset.def_four_with_value); + Assert.IsTrue(instance.__isset.def_five_with_value); + Assert.IsTrue(instance.__isset.def_six_with_value); + Assert.IsTrue(instance.__isset.def_seven_with_value); + Assert.IsTrue(instance.__isset.def_eight_with_value); + + instance.Last_of_the_mohicans = true; + + if (nesting < 2) + { + instance.Far_list = new List() { Distance.foo, Distance.bar, Distance.baz }; + instance.Far_set = new THashSet() { Distance.foo, Distance.bar, Distance.baz }; + instance.Far_map = new Dictionary() { [Distance.foo] = Distance.foo, [Distance.bar] = Distance.bar, [Distance.baz] = Distance.baz }; + + instance.Far_set_list = new THashSet>() { new List() { Distance.foo } }; + instance.Far_list_map_set = new List>>() { new Dictionary>() { [1] = new THashSet() { Distance.baz } } }; + instance.Far_map_dist_to_rds = new Dictionary>() { [Distance.bar] = new List() { MakeNestedRaceDetails(nesting) } }; + + instance.Req_nested = MakeNestedRaceDetails(nesting); + Assert.IsFalse(instance.__isset.opt_nested); + Assert.IsFalse(instance.__isset.def_nested); + + instance.Req_union = MakeNestedUnion(nesting); + Assert.IsFalse(instance.__isset.opt_union); + Assert.IsFalse(instance.__isset.def_union); + } + + instance.Triplesix = (Distance)666; + + return instance; + } + + private void ModifyInstance(RaceDetails instance, int level) + { + if ((instance == null) || (++level > 4)) + return; + + instance.Opt_one = ModifyValue(instance.Opt_one); + instance.Opt_two = ModifyValue(instance.Opt_two); + instance.Opt_three = ModifyValue(instance.Opt_three); + instance.Opt_four = ModifyValue(instance.Opt_four); + instance.Opt_five = ModifyValue(instance.Opt_five); + instance.Opt_six = ModifyValue(instance.Opt_six); + instance.Opt_seven = ModifyValue(instance.Opt_seven); + instance.Opt_eight = ModifyValue(instance.Opt_eight); + + instance.Req_one = ModifyValue(instance.Req_one); + instance.Req_two = ModifyValue(instance.Req_two); + instance.Req_three = ModifyValue(instance.Req_three); + instance.Req_four = ModifyValue(instance.Req_four); + instance.Req_five = ModifyValue(instance.Req_five); + instance.Req_six = ModifyValue(instance.Req_six); + instance.Req_seven = ModifyValue(instance.Req_seven); + instance.Req_eight = ModifyValue(instance.Req_eight); + + instance.Def_one = ModifyValue(instance.Def_one); + instance.Def_two = ModifyValue(instance.Def_two); + instance.Def_three = ModifyValue(instance.Def_three); + instance.Def_four = ModifyValue(instance.Def_four); + instance.Def_five = ModifyValue(instance.Def_five); + instance.Def_six = ModifyValue(instance.Def_six); + instance.Def_seven = ModifyValue(instance.Def_seven); + instance.Def_eight = ModifyValue(instance.Def_eight); + + instance.Opt_one_with_value = ModifyValue(instance.Opt_one_with_value); + instance.Opt_two_with_value = ModifyValue(instance.Opt_two_with_value); + instance.Opt_three_with_value = ModifyValue(instance.Opt_three_with_value); + instance.Opt_four_with_value = ModifyValue(instance.Opt_four_with_value); + instance.Opt_five_with_value = ModifyValue(instance.Opt_five_with_value); + instance.Opt_six_with_value = ModifyValue(instance.Opt_six_with_value); + instance.Opt_seven_with_value = ModifyValue(instance.Opt_seven_with_value); + instance.Opt_eight_with_value = ModifyValue(instance.Opt_eight_with_value); + + instance.Req_one_with_value = ModifyValue(instance.Req_one_with_value); + instance.Req_two_with_value = ModifyValue(instance.Req_two_with_value); + instance.Req_three_with_value = ModifyValue(instance.Req_three_with_value); + instance.Req_four_with_value = ModifyValue(instance.Req_four_with_value); + instance.Req_five_with_value = ModifyValue(instance.Req_five_with_value); + instance.Req_six_with_value = ModifyValue(instance.Req_six_with_value); + instance.Req_seven_with_value = ModifyValue(instance.Req_seven_with_value); + instance.Req_eight_with_value = ModifyValue(instance.Req_eight_with_value); + + instance.Def_one_with_value = ModifyValue(instance.Def_one_with_value); + instance.Def_two_with_value = ModifyValue(instance.Def_two_with_value); + instance.Def_three_with_value = ModifyValue(instance.Def_three_with_value); + instance.Def_four_with_value = ModifyValue(instance.Def_four_with_value); + instance.Def_five_with_value = ModifyValue(instance.Def_five_with_value); + instance.Def_six_with_value = ModifyValue(instance.Def_six_with_value); + instance.Def_seven_with_value = ModifyValue(instance.Def_seven_with_value); + instance.Def_eight_with_value = ModifyValue(instance.Def_eight_with_value); + + instance.Last_of_the_mohicans = ModifyValue(instance.Last_of_the_mohicans); + + instance.Far_list = ModifyValue(instance.Far_list); + instance.Far_set = ModifyValue(instance.Far_set); + instance.Far_map = ModifyValue(instance.Far_map); + + instance.Far_set_list = ModifyValue(instance.Far_set_list); + instance.Far_list_map_set = ModifyValue(instance.Far_list_map_set); + instance.Far_map_dist_to_rds = ModifyValue(instance.Far_map_dist_to_rds, level); + + instance.Req_nested = ModifyValue(instance.Req_nested, level); + instance.Opt_nested = ModifyValue(instance.Opt_nested, level); + instance.Def_nested = ModifyValue(instance.Def_nested, level); + + instance.Req_union = ModifyValue(instance.Req_union, level); + instance.Opt_union = ModifyValue(instance.Opt_union, level); + instance.Def_union = ModifyValue(instance.Def_union, level); + + instance.Triplesix = ModifyValue(instance.Triplesix); + } + + private jack ModifyValue(jack value, int level) + { + if (++level > 4) + return value; + + if (value == null) + value = MakeNestedUnion(0); + Debug.Assert(value.As_nested_struct != null); + ModifyInstance(value.As_nested_struct, level); + return value; + } + + private RaceDetails ModifyValue(RaceDetails value, int level) + { + if (++level > 4) + return value; + + if (value == null) + value = new RaceDetails(); + ModifyInstance(value,level); + return value; + } + + private Dictionary> ModifyValue(Dictionary> value, int level) + { + if (value == null) + value = new Dictionary>(); + + if (++level > 4) + return value; + + var details = new RaceDetails(); + InitializeInstance(details); + value[Distance.foo] = new List() { details }; + + if (value.TryGetValue(Distance.bar, out var list) && (list.Count > 0)) + { + ModifyInstance(list[0], level); + list.Add(null); + } + + value[Distance.baz] = null; + + return value; + } + + private List>> ModifyValue(List>> value) + { + if (value == null) + value = new List>>(); + + if (value.Count == 0) + value.Add(new Dictionary>()); + else + value.Add(null); + + sbyte key = (sbyte)(value[0].Count + 10); + if (value[0].Count == 0) + value[0].Add(key, new THashSet()); + else + value[0].Add(key, null); + + foreach (var entry in value) + { + if (entry != null) + { + foreach (var pair in entry) + { + if (pair.Value != null) + { + if (pair.Value.Contains(Distance.baz)) + pair.Value.Remove(Distance.baz); + else + pair.Value.Add(Distance.baz); + } + } + } + } + + return value; + } + + private THashSet> ModifyValue(THashSet> value) + { + if (value == null) + value = new THashSet>(); + + if (value.Count == 0) + value.Add(new List()); + else + value.Add(null); + + foreach (var entry in value) + if( entry != null) + entry.Add(Distance.baz); + + return value; + } + + private Dictionary ModifyValue(Dictionary value) + { + if (value == null) + value = new Dictionary(); + value[Distance.foo] = value.ContainsKey(Distance.foo) ? ++value[Distance.foo] : Distance.foo; + value[Distance.bar] = value.ContainsKey(Distance.bar) ? ++value[Distance.bar] : Distance.bar; + value[Distance.baz] = value.ContainsKey(Distance.baz) ? ++value[Distance.baz] : Distance.baz; + return value; + } + + private THashSet ModifyValue(THashSet value) + { + if (value == null) + value = new THashSet(); + + if (value.Contains(Distance.foo)) + value.Remove(Distance.foo); + else + value.Add(Distance.foo); + + if (value.Contains(Distance.bar)) + value.Remove(Distance.bar); + else + value.Add(Distance.bar); + + if (value.Contains(Distance.baz)) + value.Remove(Distance.baz); + else + value.Add(Distance.baz); + + return value; + } + + private List ModifyValue(List value) + { + if (value == null) + value = new List(); + value.Add(Distance.foo); + value.Add(Distance.bar); + value.Add(Distance.baz); + return value; + } + + private bool ModifyValue(bool value) + { + return !value; + } + + private Dictionary ModifyValue(Dictionary value) + { + if (value == null) + value = new Dictionary(); + value.Add((sbyte)(value.Count + 10), (short)value.Count); + return value; + } + + private THashSet ModifyValue(THashSet value) + { + if (value == null) + value = new THashSet(); + value.Add(value.Count+100); + return value; + } + + private List ModifyValue(List value) + { + if (value == null) + value = new List(); + value.Add(value.Count); + return value; + } + + private byte[] ModifyValue(byte[] value) + { + if (value == null) + value = new byte[1] { 0 }; + if (value.Length > 0) + value[0] = (value[0] < 0xFF) ? ++value[0] : (byte)0; + return value; + } + + private string ModifyValue(string value) + { + return value + "1"; + } + + private double ModifyValue(double value) + { + return value + 1.1; + } + + private short ModifyValue(short value) + { + return ++value; + } + + private Distance ModifyValue(Distance value) + { + return ++value; + } + + private void VerifyDifferentContent(RaceDetails first, RaceDetails second) + { + Assert.AreNotEqual(first, second); + + Assert.AreNotEqual(first.Opt_two, second.Opt_two); + Assert.AreNotEqual(first.Opt_three, second.Opt_three); + Assert.AreNotEqual(first.Opt_four, second.Opt_four); + Assert.IsFalse(TCollections.Equals(first.Opt_five, second.Opt_five)); + Assert.IsFalse(TCollections.Equals(first.Opt_six, second.Opt_six)); + Assert.IsFalse(TCollections.Equals(first.Opt_seven, second.Opt_seven)); + Assert.IsFalse(TCollections.Equals(first.Opt_eight, second.Opt_eight)); + + Assert.AreNotEqual(first.Req_one, second.Req_one); + Assert.AreNotEqual(first.Req_two, second.Req_two); + Assert.AreNotEqual(first.Req_three, second.Req_three); + Assert.AreNotEqual(first.Req_four, second.Req_four); + Assert.IsFalse(TCollections.Equals(first.Req_five, second.Req_five)); + Assert.IsFalse(TCollections.Equals(first.Req_six, second.Req_six)); + Assert.IsFalse(TCollections.Equals(first.Req_seven, second.Req_seven)); + Assert.IsFalse(TCollections.Equals(first.Req_eight, second.Req_eight)); + + Assert.AreNotEqual(first.Def_one, second.Def_one); + Assert.AreNotEqual(first.Def_two, second.Def_two); + Assert.AreNotEqual(first.Def_three, second.Def_three); + Assert.AreNotEqual(first.Def_four, second.Def_four); + Assert.IsFalse(TCollections.Equals(first.Def_five, second.Def_five)); + Assert.IsFalse(TCollections.Equals(first.Def_six, second.Def_six)); + Assert.IsFalse(TCollections.Equals(first.Def_seven, second.Def_seven)); + Assert.IsFalse(TCollections.Equals(first.Def_eight, second.Def_eight)); + + Assert.AreNotEqual(first.Opt_one_with_value, second.Opt_one_with_value); + Assert.AreNotEqual(first.Opt_two_with_value, second.Opt_two_with_value); + Assert.AreNotEqual(first.Opt_three_with_value, second.Opt_three_with_value); + Assert.AreNotEqual(first.Opt_four_with_value, second.Opt_four_with_value); + Assert.IsFalse(TCollections.Equals(first.Opt_five_with_value, second.Opt_five_with_value)); + Assert.IsFalse(TCollections.Equals(first.Opt_six_with_value, second.Opt_six_with_value)); + Assert.IsFalse(TCollections.Equals(first.Opt_seven_with_value, second.Opt_seven_with_value)); + Assert.IsFalse(TCollections.Equals(first.Opt_eight_with_value, second.Opt_eight_with_value)); + + Assert.AreNotEqual(first.Req_one_with_value, second.Req_one_with_value); + Assert.AreNotEqual(first.Req_two_with_value, second.Req_two_with_value); + Assert.AreNotEqual(first.Req_three_with_value, second.Req_three_with_value); + Assert.AreNotEqual(first.Req_four_with_value, second.Req_four_with_value); + Assert.IsFalse(TCollections.Equals(first.Req_five_with_value, second.Req_five_with_value)); + Assert.IsFalse(TCollections.Equals(first.Req_six_with_value, second.Req_six_with_value)); + Assert.IsFalse(TCollections.Equals(first.Req_seven_with_value, second.Req_seven_with_value)); + Assert.IsFalse(TCollections.Equals(first.Req_eight_with_value, second.Req_eight_with_value)); + + Assert.AreNotEqual(first.Def_one_with_value, second.Def_one_with_value); + Assert.AreNotEqual(first.Def_two_with_value, second.Def_two_with_value); + Assert.AreNotEqual(first.Def_three_with_value, second.Def_three_with_value); + Assert.AreNotEqual(first.Def_four_with_value, second.Def_four_with_value); + Assert.IsFalse(TCollections.Equals(first.Def_five_with_value, second.Def_five_with_value)); + Assert.IsFalse(TCollections.Equals(first.Def_six_with_value, second.Def_six_with_value)); + Assert.IsFalse(TCollections.Equals(first.Def_seven_with_value, second.Def_seven_with_value)); + Assert.IsFalse(TCollections.Equals(first.Def_eight_with_value, second.Def_eight_with_value)); + + Assert.AreNotEqual(first.Last_of_the_mohicans, second.Last_of_the_mohicans); + + Assert.IsFalse(TCollections.Equals(first.Far_list, second.Far_list)); + Assert.IsFalse(TCollections.Equals(first.Far_set, second.Far_set)); + Assert.IsFalse(TCollections.Equals(first.Far_map, second.Far_map)); + + Assert.IsFalse(TCollections.Equals(first.Far_set_list, second.Far_set_list)); + Assert.IsFalse(TCollections.Equals(first.Far_list_map_set, second.Far_list_map_set)); + Assert.IsFalse(TCollections.Equals(first.Far_map_dist_to_rds, second.Far_map_dist_to_rds)); + + Assert.AreNotEqual(first.Req_nested, second.Req_nested); + Assert.AreNotEqual(first.Opt_nested, second.Opt_nested); + Assert.AreNotEqual(first.Def_nested, second.Def_nested); + + Assert.AreNotEqual(first.Req_union, second.Req_union); + Assert.AreNotEqual(first.Opt_union, second.Opt_union); + Assert.AreNotEqual(first.Def_union, second.Def_union); + + Assert.AreNotEqual(first.Triplesix, second.Triplesix); + } + + private void VerifyIdenticalContent(RaceDetails first, RaceDetails second) + { + Assert.AreEqual(first, second); + + Assert.AreEqual(first.Opt_two, second.Opt_two); + Assert.AreEqual(first.Opt_three, second.Opt_three); + Assert.AreEqual(first.Opt_four, second.Opt_four); + Assert.IsTrue(TCollections.Equals(first.Opt_five, second.Opt_five)); + Assert.IsTrue(TCollections.Equals(first.Opt_six, second.Opt_six)); + Assert.IsTrue(TCollections.Equals(first.Opt_seven, second.Opt_seven)); + Assert.IsTrue(TCollections.Equals(first.Opt_eight, second.Opt_eight)); + + Assert.AreEqual(first.Req_one, second.Req_one); + Assert.AreEqual(first.Req_two, second.Req_two); + Assert.AreEqual(first.Req_three, second.Req_three); + Assert.AreEqual(first.Req_four, second.Req_four); + Assert.IsTrue(TCollections.Equals(first.Req_five, second.Req_five)); + Assert.IsTrue(TCollections.Equals(first.Req_six, second.Req_six)); + Assert.IsTrue(TCollections.Equals(first.Req_seven, second.Req_seven)); + Assert.IsTrue(TCollections.Equals(first.Req_eight, second.Req_eight)); + + Assert.AreEqual(first.Def_one, second.Def_one); + Assert.AreEqual(first.Def_two, second.Def_two); + Assert.AreEqual(first.Def_three, second.Def_three); + Assert.AreEqual(first.Def_four, second.Def_four); + Assert.IsTrue(TCollections.Equals(first.Def_five, second.Def_five)); + Assert.IsTrue(TCollections.Equals(first.Def_six, second.Def_six)); + Assert.IsTrue(TCollections.Equals(first.Def_seven, second.Def_seven)); + Assert.IsTrue(TCollections.Equals(first.Def_eight, second.Def_eight)); + + Assert.AreEqual(first.Opt_one_with_value, second.Opt_one_with_value); + Assert.AreEqual(first.Opt_two_with_value, second.Opt_two_with_value); + Assert.AreEqual(first.Opt_three_with_value, second.Opt_three_with_value); + Assert.AreEqual(first.Opt_four_with_value, second.Opt_four_with_value); + Assert.IsTrue(TCollections.Equals(first.Opt_five_with_value, second.Opt_five_with_value)); + Assert.IsTrue(TCollections.Equals(first.Opt_six_with_value, second.Opt_six_with_value)); + Assert.IsTrue(TCollections.Equals(first.Opt_seven_with_value, second.Opt_seven_with_value)); + Assert.IsTrue(TCollections.Equals(first.Opt_eight_with_value, second.Opt_eight_with_value)); + + Assert.AreEqual(first.Req_one_with_value, second.Req_one_with_value); + Assert.AreEqual(first.Req_two_with_value, second.Req_two_with_value); + Assert.AreEqual(first.Req_three_with_value, second.Req_three_with_value); + Assert.AreEqual(first.Req_four_with_value, second.Req_four_with_value); + Assert.IsTrue(TCollections.Equals(first.Req_five_with_value, second.Req_five_with_value)); + Assert.IsTrue(TCollections.Equals(first.Req_six_with_value, second.Req_six_with_value)); + Assert.IsTrue(TCollections.Equals(first.Req_seven_with_value, second.Req_seven_with_value)); + Assert.IsTrue(TCollections.Equals(first.Req_eight_with_value, second.Req_eight_with_value)); + + Assert.AreEqual(first.Def_one_with_value, second.Def_one_with_value); + Assert.AreEqual(first.Def_two_with_value, second.Def_two_with_value); + Assert.AreEqual(first.Def_three_with_value, second.Def_three_with_value); + Assert.AreEqual(first.Def_four_with_value, second.Def_four_with_value); + Assert.IsTrue(TCollections.Equals(first.Def_five_with_value, second.Def_five_with_value)); + Assert.IsTrue(TCollections.Equals(first.Def_six_with_value, second.Def_six_with_value)); + Assert.IsTrue(TCollections.Equals(first.Def_seven_with_value, second.Def_seven_with_value)); + Assert.IsTrue(TCollections.Equals(first.Def_eight_with_value, second.Def_eight_with_value)); + + Assert.AreEqual(first.Last_of_the_mohicans, second.Last_of_the_mohicans); + + Assert.IsTrue(TCollections.Equals(first.Far_list, second.Far_list)); + Assert.IsTrue(TCollections.Equals(first.Far_set, second.Far_set)); + Assert.IsTrue(TCollections.Equals(first.Far_map, second.Far_map)); + + Assert.IsTrue(TCollections.Equals(first.Far_set_list, second.Far_set_list)); + Assert.IsTrue(TCollections.Equals(first.Far_list_map_set, second.Far_list_map_set)); + Assert.IsTrue(TCollections.Equals(first.Far_map_dist_to_rds, second.Far_map_dist_to_rds)); + + Assert.AreEqual(first.Req_nested, second.Req_nested); + Assert.AreEqual(first.Opt_nested, second.Opt_nested); + Assert.AreEqual(first.Def_nested, second.Def_nested); + + Assert.AreEqual(first.Req_union, second.Req_union); + Assert.AreEqual(first.Opt_union, second.Opt_union); + Assert.AreEqual(first.Def_union, second.Def_union); + + Assert.AreEqual(first.Triplesix, second.Triplesix); + } + + } +} diff --git a/lib/netstd/Tests/Thrift.Tests/DataModel/NullValuesSet.cs b/lib/netstd/Tests/Thrift.Tests/DataModel/NullValuesSet.cs new file mode 100644 index 00000000000..693b68ecc90 --- /dev/null +++ b/lib/netstd/Tests/Thrift.Tests/DataModel/NullValuesSet.cs @@ -0,0 +1,101 @@ +// Licensed to the Apache Software Foundation(ASF) under one +// or more contributor license agreements.See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership.The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using System.Text; +using Microsoft.VisualStudio.TestPlatform.ObjectModel; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using OptReqDefTest; +using Thrift.Collections; + +namespace Thrift.Tests.DataModel +{ + // ReSharper disable once InconsistentNaming + [TestClass] + public class Thrift_5238 + { + private void CheckInstance(RaceDetails instance) + { + // object + Assert.IsTrue(instance.__isset.def_nested); + Assert.IsTrue(instance.__isset.opt_nested); + Assert.IsNull(instance.Def_nested); + Assert.IsNull(instance.Opt_nested); + + // string + Assert.IsTrue(instance.__isset.def_four); + Assert.IsTrue(instance.__isset.opt_four); + Assert.IsNull(instance.Req_four); + Assert.IsNull(instance.Def_four); + Assert.IsNull(instance.Opt_four); + + // byte[] + Assert.IsTrue(instance.__isset.def_five); + Assert.IsTrue(instance.__isset.opt_five); + Assert.IsNull(instance.Req_five); + Assert.IsNull(instance.Def_five); + Assert.IsNull(instance.Opt_five); + + // list<> + Assert.IsTrue(instance.__isset.def_six); + Assert.IsTrue(instance.__isset.opt_six); + Assert.IsNull(instance.Req_six); + Assert.IsNull(instance.Opt_six); + Assert.IsNull(instance.Def_six); + } + + [TestMethod] + public void Thrift_5238_ProperNullChecks() + { + var instance = new OptReqDefTest.RaceDetails(); + + // object + instance.Def_nested = null; + instance.Opt_nested = null; + + // string + instance.Req_four = null; + instance.Def_four = null; + instance.Opt_four = null; + + // byte[] + instance.Req_five = null; + instance.Def_five = null; + instance.Opt_five = null; + + // list<> + instance.Req_six = null; + instance.Opt_six = null; + instance.Def_six = null; + + // test the setup + CheckInstance(instance); + + // validate proper null checks , any of these throws if not + instance.ToString(); + instance.GetHashCode(); + + // validate proper null checks , any of these throws if not + var copy = instance.DeepCopy(); + CheckInstance(copy); + } + + } +} diff --git a/lib/netcore/Tests/Thrift.Tests/Protocols/TJsonProtocolHelperTests.cs b/lib/netstd/Tests/Thrift.Tests/Protocols/TJsonProtocolHelperTests.cs similarity index 98% rename from lib/netcore/Tests/Thrift.Tests/Protocols/TJsonProtocolHelperTests.cs rename to lib/netstd/Tests/Thrift.Tests/Protocols/TJsonProtocolHelperTests.cs index cdc8317e249..6d391516e44 100644 --- a/lib/netcore/Tests/Thrift.Tests/Protocols/TJsonProtocolHelperTests.cs +++ b/lib/netstd/Tests/Thrift.Tests/Protocols/TJsonProtocolHelperTests.cs @@ -19,9 +19,9 @@ using System.Collections.Generic; using System.Linq; using Microsoft.VisualStudio.TestTools.UnitTesting; -using Thrift.Protocols; -using Thrift.Protocols.Entities; -using Thrift.Protocols.Utilities; +using Thrift.Protocol; +using Thrift.Protocol.Entities; +using Thrift.Protocol.Utilities; namespace Thrift.Tests.Protocols { diff --git a/lib/netcore/Tests/Thrift.Tests/Protocols/TJsonProtocolTests.cs b/lib/netstd/Tests/Thrift.Tests/Protocols/TJsonProtocolTests.cs similarity index 84% rename from lib/netcore/Tests/Thrift.Tests/Protocols/TJsonProtocolTests.cs rename to lib/netstd/Tests/Thrift.Tests/Protocols/TJsonProtocolTests.cs index 523736086e7..4054a29f255 100644 --- a/lib/netcore/Tests/Thrift.Tests/Protocols/TJsonProtocolTests.cs +++ b/lib/netstd/Tests/Thrift.Tests/Protocols/TJsonProtocolTests.cs @@ -21,11 +21,10 @@ using System.Threading; using System.Threading.Tasks; using Microsoft.VisualStudio.TestTools.UnitTesting; -using NSubstitute; -using Thrift.Protocols; -using Thrift.Protocols.Entities; -using Thrift.Transports; -using Thrift.Transports.Client; +using Thrift.Protocol; +using Thrift.Protocol.Entities; +using Thrift.Transport; +using Thrift.Transport.Client; namespace Thrift.Tests.Protocols { @@ -36,7 +35,7 @@ public class TJSONProtocolTests [TestMethod] public void TJSONProtocol_Can_Create_Instance_Test() { - var httpClientTransport = Substitute.For(new Uri("http://localhost"), null); + var httpClientTransport = new THttpTransport( new Uri("http://localhost"), null, null, null); var result = new TJSONProtocolWrapper(httpClientTransport); @@ -45,7 +44,7 @@ public void TJSONProtocol_Can_Create_Instance_Test() Assert.IsNotNull(result.WrappedReader); Assert.IsNotNull(result.Transport); Assert.IsTrue(result.WrappedRecursionDepth == 0); - Assert.IsTrue(result.WrappedRecursionLimit == TProtocol.DefaultRecursionDepth); + Assert.IsTrue(result.WrappedRecursionLimit == TConfiguration.DEFAULT_RECURSION_DEPTH); Assert.IsTrue(result.Transport.Equals(httpClientTransport)); Assert.IsTrue(result.WrappedContext.GetType().Name.Equals("JSONBaseContext", StringComparison.OrdinalIgnoreCase)); @@ -54,7 +53,7 @@ public void TJSONProtocol_Can_Create_Instance_Test() private class TJSONProtocolWrapper : TJsonProtocol { - public TJSONProtocolWrapper(TClientTransport trans) : base(trans) + public TJSONProtocolWrapper(TTransport trans) : base(trans) { } diff --git a/lib/netstd/Tests/Thrift.Tests/Thrift.Tests.csproj b/lib/netstd/Tests/Thrift.Tests/Thrift.Tests.csproj new file mode 100644 index 00000000000..4b39e7d4b3e --- /dev/null +++ b/lib/netstd/Tests/Thrift.Tests/Thrift.Tests.csproj @@ -0,0 +1,37 @@ + + + + netcoreapp3.1 + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/lib/netstd/Thrift.sln b/lib/netstd/Thrift.sln new file mode 100644 index 00000000000..58c76ced98e --- /dev/null +++ b/lib/netstd/Thrift.sln @@ -0,0 +1,102 @@ +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.29905.134 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tests", "Tests", "{ED5A45B0-07D1-4507-96B7-83FBD3D031CA}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Thrift", "Thrift\Thrift.csproj", "{5B501D21-D428-408D-AB5C-32D6F5355294}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Thrift.IntegrationTests", "Tests\Thrift.IntegrationTests\Thrift.IntegrationTests.csproj", "{837F4084-AAD7-45F5-BC96-10E05A669DB4}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Thrift.Tests", "Tests\Thrift.Tests\Thrift.Tests.csproj", "{0790D388-1A3C-4423-8CF2-C97074A8B68B}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Thrift.PublicInterfaces.Compile.Tests", "Tests\Thrift.PublicInterfaces.Compile.Tests\Thrift.PublicInterfaces.Compile.Tests.csproj", "{A6AE021D-61CB-4D84-A103-0B663C62AE2C}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Benchmarks", "Benchmarks", "{BF7B896B-8BB6-447C-84F8-26871882A14A}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Thrift.Benchmarks", "Benchmarks\Thrift.Benchmarks\Thrift.Benchmarks.csproj", "{D0559DFF-6632-446C-9EFC-C750DA20B1D9}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|Any CPU = Release|Any CPU + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {5B501D21-D428-408D-AB5C-32D6F5355294}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {5B501D21-D428-408D-AB5C-32D6F5355294}.Debug|Any CPU.Build.0 = Debug|Any CPU + {5B501D21-D428-408D-AB5C-32D6F5355294}.Debug|x64.ActiveCfg = Debug|Any CPU + {5B501D21-D428-408D-AB5C-32D6F5355294}.Debug|x64.Build.0 = Debug|Any CPU + {5B501D21-D428-408D-AB5C-32D6F5355294}.Debug|x86.ActiveCfg = Debug|Any CPU + {5B501D21-D428-408D-AB5C-32D6F5355294}.Debug|x86.Build.0 = Debug|Any CPU + {5B501D21-D428-408D-AB5C-32D6F5355294}.Release|Any CPU.ActiveCfg = Release|Any CPU + {5B501D21-D428-408D-AB5C-32D6F5355294}.Release|Any CPU.Build.0 = Release|Any CPU + {5B501D21-D428-408D-AB5C-32D6F5355294}.Release|x64.ActiveCfg = Release|Any CPU + {5B501D21-D428-408D-AB5C-32D6F5355294}.Release|x64.Build.0 = Release|Any CPU + {5B501D21-D428-408D-AB5C-32D6F5355294}.Release|x86.ActiveCfg = Release|Any CPU + {5B501D21-D428-408D-AB5C-32D6F5355294}.Release|x86.Build.0 = Release|Any CPU + {837F4084-AAD7-45F5-BC96-10E05A669DB4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {837F4084-AAD7-45F5-BC96-10E05A669DB4}.Debug|Any CPU.Build.0 = Debug|Any CPU + {837F4084-AAD7-45F5-BC96-10E05A669DB4}.Debug|x64.ActiveCfg = Debug|Any CPU + {837F4084-AAD7-45F5-BC96-10E05A669DB4}.Debug|x64.Build.0 = Debug|Any CPU + {837F4084-AAD7-45F5-BC96-10E05A669DB4}.Debug|x86.ActiveCfg = Debug|Any CPU + {837F4084-AAD7-45F5-BC96-10E05A669DB4}.Debug|x86.Build.0 = Debug|Any CPU + {837F4084-AAD7-45F5-BC96-10E05A669DB4}.Release|Any CPU.ActiveCfg = Release|Any CPU + {837F4084-AAD7-45F5-BC96-10E05A669DB4}.Release|Any CPU.Build.0 = Release|Any CPU + {837F4084-AAD7-45F5-BC96-10E05A669DB4}.Release|x64.ActiveCfg = Release|Any CPU + {837F4084-AAD7-45F5-BC96-10E05A669DB4}.Release|x64.Build.0 = Release|Any CPU + {837F4084-AAD7-45F5-BC96-10E05A669DB4}.Release|x86.ActiveCfg = Release|Any CPU + {837F4084-AAD7-45F5-BC96-10E05A669DB4}.Release|x86.Build.0 = Release|Any CPU + {0790D388-1A3C-4423-8CF2-C97074A8B68B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {0790D388-1A3C-4423-8CF2-C97074A8B68B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {0790D388-1A3C-4423-8CF2-C97074A8B68B}.Debug|x64.ActiveCfg = Debug|Any CPU + {0790D388-1A3C-4423-8CF2-C97074A8B68B}.Debug|x64.Build.0 = Debug|Any CPU + {0790D388-1A3C-4423-8CF2-C97074A8B68B}.Debug|x86.ActiveCfg = Debug|Any CPU + {0790D388-1A3C-4423-8CF2-C97074A8B68B}.Debug|x86.Build.0 = Debug|Any CPU + {0790D388-1A3C-4423-8CF2-C97074A8B68B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {0790D388-1A3C-4423-8CF2-C97074A8B68B}.Release|Any CPU.Build.0 = Release|Any CPU + {0790D388-1A3C-4423-8CF2-C97074A8B68B}.Release|x64.ActiveCfg = Release|Any CPU + {0790D388-1A3C-4423-8CF2-C97074A8B68B}.Release|x64.Build.0 = Release|Any CPU + {0790D388-1A3C-4423-8CF2-C97074A8B68B}.Release|x86.ActiveCfg = Release|Any CPU + {0790D388-1A3C-4423-8CF2-C97074A8B68B}.Release|x86.Build.0 = Release|Any CPU + {A6AE021D-61CB-4D84-A103-0B663C62AE2C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A6AE021D-61CB-4D84-A103-0B663C62AE2C}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A6AE021D-61CB-4D84-A103-0B663C62AE2C}.Debug|x64.ActiveCfg = Debug|Any CPU + {A6AE021D-61CB-4D84-A103-0B663C62AE2C}.Debug|x64.Build.0 = Debug|Any CPU + {A6AE021D-61CB-4D84-A103-0B663C62AE2C}.Debug|x86.ActiveCfg = Debug|Any CPU + {A6AE021D-61CB-4D84-A103-0B663C62AE2C}.Debug|x86.Build.0 = Debug|Any CPU + {A6AE021D-61CB-4D84-A103-0B663C62AE2C}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A6AE021D-61CB-4D84-A103-0B663C62AE2C}.Release|Any CPU.Build.0 = Release|Any CPU + {A6AE021D-61CB-4D84-A103-0B663C62AE2C}.Release|x64.ActiveCfg = Release|Any CPU + {A6AE021D-61CB-4D84-A103-0B663C62AE2C}.Release|x64.Build.0 = Release|Any CPU + {A6AE021D-61CB-4D84-A103-0B663C62AE2C}.Release|x86.ActiveCfg = Release|Any CPU + {A6AE021D-61CB-4D84-A103-0B663C62AE2C}.Release|x86.Build.0 = Release|Any CPU + {D0559DFF-6632-446C-9EFC-C750DA20B1D9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D0559DFF-6632-446C-9EFC-C750DA20B1D9}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D0559DFF-6632-446C-9EFC-C750DA20B1D9}.Debug|x64.ActiveCfg = Debug|Any CPU + {D0559DFF-6632-446C-9EFC-C750DA20B1D9}.Debug|x64.Build.0 = Debug|Any CPU + {D0559DFF-6632-446C-9EFC-C750DA20B1D9}.Debug|x86.ActiveCfg = Debug|Any CPU + {D0559DFF-6632-446C-9EFC-C750DA20B1D9}.Debug|x86.Build.0 = Debug|Any CPU + {D0559DFF-6632-446C-9EFC-C750DA20B1D9}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D0559DFF-6632-446C-9EFC-C750DA20B1D9}.Release|Any CPU.Build.0 = Release|Any CPU + {D0559DFF-6632-446C-9EFC-C750DA20B1D9}.Release|x64.ActiveCfg = Release|Any CPU + {D0559DFF-6632-446C-9EFC-C750DA20B1D9}.Release|x64.Build.0 = Release|Any CPU + {D0559DFF-6632-446C-9EFC-C750DA20B1D9}.Release|x86.ActiveCfg = Release|Any CPU + {D0559DFF-6632-446C-9EFC-C750DA20B1D9}.Release|x86.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {837F4084-AAD7-45F5-BC96-10E05A669DB4} = {ED5A45B0-07D1-4507-96B7-83FBD3D031CA} + {0790D388-1A3C-4423-8CF2-C97074A8B68B} = {ED5A45B0-07D1-4507-96B7-83FBD3D031CA} + {A6AE021D-61CB-4D84-A103-0B663C62AE2C} = {ED5A45B0-07D1-4507-96B7-83FBD3D031CA} + {D0559DFF-6632-446C-9EFC-C750DA20B1D9} = {BF7B896B-8BB6-447C-84F8-26871882A14A} + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {FD20BC4A-0109-41D8-8C0C-893E784D7EF9} + EndGlobalSection +EndGlobal diff --git a/lib/netcore/Thrift/Collections/TCollections.cs b/lib/netstd/Thrift/Collections/TCollections.cs similarity index 81% rename from lib/netcore/Thrift/Collections/TCollections.cs rename to lib/netstd/Thrift/Collections/TCollections.cs index 147bfc7d31c..b386c37b6c4 100644 --- a/lib/netcore/Thrift/Collections/TCollections.cs +++ b/lib/netstd/Thrift/Collections/TCollections.cs @@ -16,11 +16,12 @@ // under the License. using System.Collections; +using System.Collections.Generic; namespace Thrift.Collections { // ReSharper disable once InconsistentNaming - public class TCollections + public static class TCollections { /// /// This will return true if the two collections are value-wise the same. @@ -38,6 +39,18 @@ public static bool Equals(IEnumerable first, IEnumerable second) return false; } + // for dictionaries, we need to compare keys and values separately + // because KeyValuePair.Equals() will not do what we want + var fdict = first as IDictionary; + var sdict = second as IDictionary; + if ((fdict != null) || (sdict != null)) + { + if ((fdict == null) || (sdict == null)) + return false; + return TCollections.Equals(fdict.Keys, sdict.Keys) + && TCollections.Equals(fdict.Values, sdict.Values); + } + var fiter = first.GetEnumerator(); var siter = second.GetEnumerator(); @@ -91,11 +104,13 @@ public static int GetHashCode(IEnumerable enumerable) unchecked { - hashcode = (hashcode*397) ^ (objHash); + hashcode = (hashcode * 397) ^ (objHash); } } return hashcode; } + + } -} \ No newline at end of file +} diff --git a/lib/netcore/Thrift/Collections/THashSet.cs b/lib/netstd/Thrift/Collections/THashSet.cs similarity index 65% rename from lib/netcore/Thrift/Collections/THashSet.cs rename to lib/netstd/Thrift/Collections/THashSet.cs index 011f0a0d629..8dfb9e3f885 100644 --- a/lib/netcore/Thrift/Collections/THashSet.cs +++ b/lib/netstd/Thrift/Collections/THashSet.cs @@ -23,45 +23,56 @@ namespace Thrift.Collections // ReSharper disable once InconsistentNaming public class THashSet : ICollection { - private readonly HashSet _set = new HashSet(); + private readonly HashSet Items; - public int Count => _set.Count; + public THashSet() + { + Items = new HashSet(); + } + + public THashSet(int capacity) + { + // TODO: uncomment capacity when NET Standard also implements it + Items = new HashSet(/*capacity*/); + } + + public int Count => Items.Count; public bool IsReadOnly => false; public void Add(T item) { - _set.Add(item); + Items.Add(item); } public void Clear() { - _set.Clear(); + Items.Clear(); } public bool Contains(T item) { - return _set.Contains(item); + return Items.Contains(item); } public void CopyTo(T[] array, int arrayIndex) { - _set.CopyTo(array, arrayIndex); + Items.CopyTo(array, arrayIndex); } - public IEnumerator GetEnumerator() + IEnumerator IEnumerable.GetEnumerator() { - return _set.GetEnumerator(); + return Items.GetEnumerator(); } - IEnumerator IEnumerable.GetEnumerator() + public IEnumerator GetEnumerator() { - return ((IEnumerable) _set).GetEnumerator(); + return ((IEnumerable) Items).GetEnumerator(); } public bool Remove(T item) { - return _set.Remove(item); + return Items.Remove(item); } } -} \ No newline at end of file +} diff --git a/lib/netcore/Thrift/ITAsyncProcessor.cs b/lib/netstd/Thrift/Processor/ITAsyncProcessor.cs similarity index 86% rename from lib/netcore/Thrift/ITAsyncProcessor.cs rename to lib/netstd/Thrift/Processor/ITAsyncProcessor.cs index db8e40aefe8..f5b8d16e359 100644 --- a/lib/netcore/Thrift/ITAsyncProcessor.cs +++ b/lib/netstd/Thrift/Processor/ITAsyncProcessor.cs @@ -17,13 +17,12 @@ using System.Threading; using System.Threading.Tasks; -using Thrift.Protocols; +using Thrift.Protocol; -namespace Thrift +namespace Thrift.Processor { public interface ITAsyncProcessor { - Task ProcessAsync(TProtocol iprot, TProtocol oprot); - Task ProcessAsync(TProtocol iprot, TProtocol oprot, CancellationToken cancellationToken); + Task ProcessAsync(TProtocol iprot, TProtocol oprot, CancellationToken cancellationToken = default(CancellationToken)); } -} \ No newline at end of file +} diff --git a/lib/netcore/Thrift/ITProcessorFactory.cs b/lib/netstd/Thrift/Processor/ITProcessorFactory.cs similarity index 86% rename from lib/netcore/Thrift/ITProcessorFactory.cs rename to lib/netstd/Thrift/Processor/ITProcessorFactory.cs index 5133e5c4801..e0fe3d0a8d8 100644 --- a/lib/netcore/Thrift/ITProcessorFactory.cs +++ b/lib/netstd/Thrift/Processor/ITProcessorFactory.cs @@ -16,13 +16,13 @@ // under the License. using Thrift.Server; -using Thrift.Transports; +using Thrift.Transport; -namespace Thrift +namespace Thrift.Processor { // ReSharper disable once InconsistentNaming public interface ITProcessorFactory { - ITAsyncProcessor GetAsyncProcessor(TClientTransport trans, TBaseServer baseServer = null); + ITAsyncProcessor GetAsyncProcessor(TTransport trans, TServer baseServer = null); } } \ No newline at end of file diff --git a/lib/netcore/Thrift/TMultiplexedProcessor.cs b/lib/netstd/Thrift/Processor/TMultiplexedProcessor.cs similarity index 90% rename from lib/netcore/Thrift/TMultiplexedProcessor.cs rename to lib/netstd/Thrift/Processor/TMultiplexedProcessor.cs index ad0e749e071..b192210544c 100644 --- a/lib/netcore/Thrift/TMultiplexedProcessor.cs +++ b/lib/netstd/Thrift/Processor/TMultiplexedProcessor.cs @@ -20,10 +20,10 @@ using System.IO; using System.Threading; using System.Threading.Tasks; -using Thrift.Protocols; -using Thrift.Protocols.Entities; +using Thrift.Protocol; +using Thrift.Protocol.Entities; -namespace Thrift +namespace Thrift.Processor { // ReSharper disable once InconsistentNaming public class TMultiplexedProcessor : ITAsyncProcessor @@ -40,10 +40,7 @@ public async Task ProcessAsync(TProtocol iprot, TProtocol oprot) public async Task ProcessAsync(TProtocol iprot, TProtocol oprot, CancellationToken cancellationToken) { - if (cancellationToken.IsCancellationRequested) - { - return await Task.FromCanceled(cancellationToken); - } + cancellationToken.ThrowIfCancellationRequested(); try { @@ -129,15 +126,11 @@ public StoredMessageProtocol(TProtocol protocol, TMessage messageBegin) _msgBegin = messageBegin; } - public override async Task ReadMessageBeginAsync(CancellationToken cancellationToken) + public override ValueTask ReadMessageBeginAsync(CancellationToken cancellationToken) { - if (cancellationToken.IsCancellationRequested) - { - return await Task.FromCanceled(cancellationToken); - } - - return _msgBegin; + cancellationToken.ThrowIfCancellationRequested(); + return new ValueTask(_msgBegin); } } } -} \ No newline at end of file +} diff --git a/lib/netcore/Thrift/SingletonTProcessorFactory.cs b/lib/netstd/Thrift/Processor/TSingletonProcessorFactory.cs similarity index 68% rename from lib/netcore/Thrift/SingletonTProcessorFactory.cs rename to lib/netstd/Thrift/Processor/TSingletonProcessorFactory.cs index c35123348ba..97ecff65c45 100644 --- a/lib/netcore/Thrift/SingletonTProcessorFactory.cs +++ b/lib/netstd/Thrift/Processor/TSingletonProcessorFactory.cs @@ -16,23 +16,23 @@ // under the License. using Thrift.Server; -using Thrift.Transports; +using Thrift.Transport; -namespace Thrift +namespace Thrift.Processor { // ReSharper disable once InconsistentNaming - public class SingletonTProcessorFactory : ITProcessorFactory + public class TSingletonProcessorFactory : ITProcessorFactory { - private readonly ITAsyncProcessor _tAsyncProcessor; + private readonly ITAsyncProcessor _asyncProcessor; - public SingletonTProcessorFactory(ITAsyncProcessor tAsyncProcessor) + public TSingletonProcessorFactory(ITAsyncProcessor asyncProcessor) { - _tAsyncProcessor = tAsyncProcessor; + _asyncProcessor = asyncProcessor; } - public ITAsyncProcessor GetAsyncProcessor(TClientTransport trans, TBaseServer baseServer = null) + public ITAsyncProcessor GetAsyncProcessor(TTransport trans, TServer baseServer = null) { - return _tAsyncProcessor; + return _asyncProcessor; } } } \ No newline at end of file diff --git a/lib/netcore/Thrift/Properties/AssemblyInfo.cs b/lib/netstd/Thrift/Properties/AssemblyInfo.cs similarity index 96% rename from lib/netcore/Thrift/Properties/AssemblyInfo.cs rename to lib/netstd/Thrift/Properties/AssemblyInfo.cs index 68cd49cb100..58aa84495d2 100644 --- a/lib/netcore/Thrift/Properties/AssemblyInfo.cs +++ b/lib/netstd/Thrift/Properties/AssemblyInfo.cs @@ -52,5 +52,5 @@ // You can specify all the values or you can default the Build and Revision Numbers // by using the '*' as shown below: -[assembly: AssemblyVersion("0.12.0.0")] -[assembly: AssemblyFileVersion("0.12.0.0")] +[assembly: AssemblyVersion("0.14.0.0")] +[assembly: AssemblyFileVersion("0.14.0.0")] diff --git a/lib/netcore/Thrift/Protocols/Entities/TField.cs b/lib/netstd/Thrift/Protocol/Entities/TField.cs similarity index 97% rename from lib/netcore/Thrift/Protocols/Entities/TField.cs rename to lib/netstd/Thrift/Protocol/Entities/TField.cs index d311535e73c..4e29bb5d4e4 100644 --- a/lib/netcore/Thrift/Protocols/Entities/TField.cs +++ b/lib/netstd/Thrift/Protocol/Entities/TField.cs @@ -15,7 +15,7 @@ // specific language governing permissions and limitations // under the License. -namespace Thrift.Protocols.Entities +namespace Thrift.Protocol.Entities { // ReSharper disable once InconsistentNaming public struct TField diff --git a/lib/netcore/Thrift/Protocols/Entities/TList.cs b/lib/netstd/Thrift/Protocol/Entities/TList.cs similarity index 96% rename from lib/netcore/Thrift/Protocols/Entities/TList.cs rename to lib/netstd/Thrift/Protocol/Entities/TList.cs index ce232207cba..f5992256444 100644 --- a/lib/netcore/Thrift/Protocols/Entities/TList.cs +++ b/lib/netstd/Thrift/Protocol/Entities/TList.cs @@ -15,7 +15,7 @@ // specific language governing permissions and limitations // under the License. -namespace Thrift.Protocols.Entities +namespace Thrift.Protocol.Entities { // ReSharper disable once InconsistentNaming public struct TList diff --git a/lib/netcore/Thrift/Protocols/Entities/TMap.cs b/lib/netstd/Thrift/Protocol/Entities/TMap.cs similarity index 97% rename from lib/netcore/Thrift/Protocols/Entities/TMap.cs rename to lib/netstd/Thrift/Protocol/Entities/TMap.cs index 9195593db27..1efebe7a199 100644 --- a/lib/netcore/Thrift/Protocols/Entities/TMap.cs +++ b/lib/netstd/Thrift/Protocol/Entities/TMap.cs @@ -15,7 +15,7 @@ // specific language governing permissions and limitations // under the License. -namespace Thrift.Protocols.Entities +namespace Thrift.Protocol.Entities { // ReSharper disable once InconsistentNaming public struct TMap diff --git a/lib/netcore/Thrift/Protocols/Entities/TMessage.cs b/lib/netstd/Thrift/Protocol/Entities/TMessage.cs similarity index 97% rename from lib/netcore/Thrift/Protocols/Entities/TMessage.cs rename to lib/netstd/Thrift/Protocol/Entities/TMessage.cs index 17f49298f77..08d741d65ec 100644 --- a/lib/netcore/Thrift/Protocols/Entities/TMessage.cs +++ b/lib/netstd/Thrift/Protocol/Entities/TMessage.cs @@ -15,7 +15,7 @@ // specific language governing permissions and limitations // under the License. -namespace Thrift.Protocols.Entities +namespace Thrift.Protocol.Entities { // ReSharper disable once InconsistentNaming public struct TMessage diff --git a/lib/netcore/Thrift/Protocols/Entities/TMessageType.cs b/lib/netstd/Thrift/Protocol/Entities/TMessageType.cs similarity index 96% rename from lib/netcore/Thrift/Protocols/Entities/TMessageType.cs rename to lib/netstd/Thrift/Protocol/Entities/TMessageType.cs index d7b9a227595..24d663e2d32 100644 --- a/lib/netcore/Thrift/Protocols/Entities/TMessageType.cs +++ b/lib/netstd/Thrift/Protocol/Entities/TMessageType.cs @@ -15,7 +15,7 @@ // specific language governing permissions and limitations // under the License. -namespace Thrift.Protocols.Entities +namespace Thrift.Protocol.Entities { // ReSharper disable once InconsistentNaming public enum TMessageType diff --git a/lib/netcore/Thrift/Protocols/Entities/TSet.cs b/lib/netstd/Thrift/Protocol/Entities/TSet.cs similarity index 97% rename from lib/netcore/Thrift/Protocols/Entities/TSet.cs rename to lib/netstd/Thrift/Protocol/Entities/TSet.cs index a583b54a681..692d5642c4e 100644 --- a/lib/netcore/Thrift/Protocols/Entities/TSet.cs +++ b/lib/netstd/Thrift/Protocol/Entities/TSet.cs @@ -15,7 +15,7 @@ // specific language governing permissions and limitations // under the License. -namespace Thrift.Protocols.Entities +namespace Thrift.Protocol.Entities { // ReSharper disable once InconsistentNaming public struct TSet diff --git a/lib/netcore/Thrift/Protocols/Entities/TStruct.cs b/lib/netstd/Thrift/Protocol/Entities/TStruct.cs similarity index 89% rename from lib/netcore/Thrift/Protocols/Entities/TStruct.cs rename to lib/netstd/Thrift/Protocol/Entities/TStruct.cs index a28dcc3d001..d87608799c1 100644 --- a/lib/netcore/Thrift/Protocols/Entities/TStruct.cs +++ b/lib/netstd/Thrift/Protocol/Entities/TStruct.cs @@ -15,16 +15,16 @@ // specific language governing permissions and limitations // under the License. -namespace Thrift.Protocols.Entities +namespace Thrift.Protocol.Entities { // ReSharper disable once InconsistentNaming - public struct TStruct + public readonly struct TStruct { public TStruct(string name) { Name = name; } - public string Name { get; set; } + public string Name { get; } } -} \ No newline at end of file +} diff --git a/lib/netcore/Thrift/Protocols/Entities/TType.cs b/lib/netstd/Thrift/Protocol/Entities/TType.cs similarity index 96% rename from lib/netcore/Thrift/Protocols/Entities/TType.cs rename to lib/netstd/Thrift/Protocol/Entities/TType.cs index ebe781c9547..4e922a7e756 100644 --- a/lib/netcore/Thrift/Protocols/Entities/TType.cs +++ b/lib/netstd/Thrift/Protocol/Entities/TType.cs @@ -15,7 +15,7 @@ // specific language governing permissions and limitations // under the License. -namespace Thrift.Protocols.Entities +namespace Thrift.Protocol.Entities { // ReSharper disable once InconsistentNaming public enum TType : byte diff --git a/lib/netcore/Thrift/Protocols/TBase.cs b/lib/netstd/Thrift/Protocol/TBase.cs similarity index 75% rename from lib/netcore/Thrift/Protocols/TBase.cs rename to lib/netstd/Thrift/Protocol/TBase.cs index 014e1aee856..df9dd348cc5 100644 --- a/lib/netcore/Thrift/Protocols/TBase.cs +++ b/lib/netstd/Thrift/Protocol/TBase.cs @@ -1,4 +1,4 @@ -// Licensed to the Apache Software Foundation(ASF) under one +// Licensed to the Apache Software Foundation(ASF) under one // or more contributor license agreements.See the NOTICE file // distributed with this work for additional information // regarding copyright ownership.The ASF licenses this file @@ -18,11 +18,17 @@ using System.Threading; using System.Threading.Tasks; -namespace Thrift.Protocols +namespace Thrift.Protocol { + public interface TUnionBase + { + Task WriteAsync(TProtocol tProtocol, CancellationToken cancellationToken = default); + } + // ReSharper disable once InconsistentNaming - public interface TBase : TAbstractBase + public interface TBase : TUnionBase { - Task ReadAsync(TProtocol tProtocol, CancellationToken cancellationToken); + Task ReadAsync(TProtocol tProtocol, CancellationToken cancellationToken = default); } -} \ No newline at end of file + +} diff --git a/lib/netstd/Thrift/Protocol/TBinaryProtocol.cs b/lib/netstd/Thrift/Protocol/TBinaryProtocol.cs new file mode 100644 index 00000000000..28b7d297839 --- /dev/null +++ b/lib/netstd/Thrift/Protocol/TBinaryProtocol.cs @@ -0,0 +1,471 @@ +// Licensed to the Apache Software Foundation(ASF) under one +// or more contributor license agreements.See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership.The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +using System; +using System.Buffers.Binary; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using Thrift.Protocol.Entities; +using Thrift.Transport; + +namespace Thrift.Protocol +{ + // ReSharper disable once InconsistentNaming + public class TBinaryProtocol : TProtocol + { + protected const uint VersionMask = 0xffff0000; + protected const uint Version1 = 0x80010000; + + protected bool StrictRead; + protected bool StrictWrite; + + // minimize memory allocations by means of an preallocated bytes buffer + // The value of 128 is arbitrarily chosen, the required minimum size must be sizeof(long) + private byte[] PreAllocatedBuffer = new byte[128]; + + public TBinaryProtocol(TTransport trans) + : this(trans, false, true) + { + } + + public TBinaryProtocol(TTransport trans, bool strictRead, bool strictWrite) + : base(trans) + { + StrictRead = strictRead; + StrictWrite = strictWrite; + } + + public override async Task WriteMessageBeginAsync(TMessage message, CancellationToken cancellationToken) + { + cancellationToken.ThrowIfCancellationRequested(); + + if (StrictWrite) + { + var version = Version1 | (uint) message.Type; + await WriteI32Async((int) version, cancellationToken); + await WriteStringAsync(message.Name, cancellationToken); + await WriteI32Async(message.SeqID, cancellationToken); + } + else + { + await WriteStringAsync(message.Name, cancellationToken); + await WriteByteAsync((sbyte) message.Type, cancellationToken); + await WriteI32Async(message.SeqID, cancellationToken); + } + } + + public override Task WriteMessageEndAsync(CancellationToken cancellationToken) + { + cancellationToken.ThrowIfCancellationRequested(); + return Task.CompletedTask; + } + + public override Task WriteStructBeginAsync(TStruct @struct, CancellationToken cancellationToken) + { + cancellationToken.ThrowIfCancellationRequested(); + return Task.CompletedTask; + } + + public override Task WriteStructEndAsync(CancellationToken cancellationToken) + { + cancellationToken.ThrowIfCancellationRequested(); + return Task.CompletedTask; + } + + public override async Task WriteFieldBeginAsync(TField field, CancellationToken cancellationToken) + { + cancellationToken.ThrowIfCancellationRequested(); + await WriteByteAsync((sbyte) field.Type, cancellationToken); + await WriteI16Async(field.ID, cancellationToken); + } + + public override Task WriteFieldEndAsync(CancellationToken cancellationToken) + { + cancellationToken.ThrowIfCancellationRequested(); + return Task.CompletedTask; + } + + public override async Task WriteFieldStopAsync(CancellationToken cancellationToken) + { + cancellationToken.ThrowIfCancellationRequested(); + + await WriteByteAsync((sbyte) TType.Stop, cancellationToken); + } + + public override async Task WriteMapBeginAsync(TMap map, CancellationToken cancellationToken) + { + cancellationToken.ThrowIfCancellationRequested(); + + PreAllocatedBuffer[0] = (byte)map.KeyType; + PreAllocatedBuffer[1] = (byte)map.ValueType; + await Trans.WriteAsync(PreAllocatedBuffer, 0, 2, cancellationToken); + + await WriteI32Async(map.Count, cancellationToken); + } + + public override Task WriteMapEndAsync(CancellationToken cancellationToken) + { + cancellationToken.ThrowIfCancellationRequested(); + return Task.CompletedTask; + } + + public override async Task WriteListBeginAsync(TList list, CancellationToken cancellationToken) + { + cancellationToken.ThrowIfCancellationRequested(); + await WriteByteAsync((sbyte) list.ElementType, cancellationToken); + await WriteI32Async(list.Count, cancellationToken); + } + + public override Task WriteListEndAsync(CancellationToken cancellationToken) + { + cancellationToken.ThrowIfCancellationRequested(); + return Task.CompletedTask; + } + + public override async Task WriteSetBeginAsync(TSet set, CancellationToken cancellationToken) + { + cancellationToken.ThrowIfCancellationRequested(); + await WriteByteAsync((sbyte) set.ElementType, cancellationToken); + await WriteI32Async(set.Count, cancellationToken); + } + + public override Task WriteSetEndAsync(CancellationToken cancellationToken) + { + cancellationToken.ThrowIfCancellationRequested(); + return Task.CompletedTask; + } + + public override async Task WriteBoolAsync(bool b, CancellationToken cancellationToken) + { + cancellationToken.ThrowIfCancellationRequested(); + await WriteByteAsync(b ? (sbyte) 1 : (sbyte) 0, cancellationToken); + } + + public override async Task WriteByteAsync(sbyte b, CancellationToken cancellationToken) + { + cancellationToken.ThrowIfCancellationRequested(); + + PreAllocatedBuffer[0] = (byte)b; + + await Trans.WriteAsync(PreAllocatedBuffer, 0, 1, cancellationToken); + } + public override async Task WriteI16Async(short i16, CancellationToken cancellationToken) + { + cancellationToken.ThrowIfCancellationRequested(); + + BinaryPrimitives.WriteInt16BigEndian(PreAllocatedBuffer, i16); + + await Trans.WriteAsync(PreAllocatedBuffer, 0, 2, cancellationToken); + } + + public override async Task WriteI32Async(int i32, CancellationToken cancellationToken) + { + cancellationToken.ThrowIfCancellationRequested(); + + BinaryPrimitives.WriteInt32BigEndian(PreAllocatedBuffer, i32); + + await Trans.WriteAsync(PreAllocatedBuffer, 0, 4, cancellationToken); + } + + + public override async Task WriteI64Async(long i64, CancellationToken cancellationToken) + { + cancellationToken.ThrowIfCancellationRequested(); + + BinaryPrimitives.WriteInt64BigEndian(PreAllocatedBuffer, i64); + + await Trans.WriteAsync(PreAllocatedBuffer, 0, 8, cancellationToken); + } + + public override async Task WriteDoubleAsync(double d, CancellationToken cancellationToken) + { + cancellationToken.ThrowIfCancellationRequested(); + + await WriteI64Async(BitConverter.DoubleToInt64Bits(d), cancellationToken); + } + + + public override async Task WriteBinaryAsync(byte[] bytes, CancellationToken cancellationToken) + { + cancellationToken.ThrowIfCancellationRequested(); + + await WriteI32Async(bytes.Length, cancellationToken); + await Trans.WriteAsync(bytes, 0, bytes.Length, cancellationToken); + } + + public override async ValueTask ReadMessageBeginAsync(CancellationToken cancellationToken) + { + cancellationToken.ThrowIfCancellationRequested(); + + var message = new TMessage(); + var size = await ReadI32Async(cancellationToken); + if (size < 0) + { + var version = (uint) size & VersionMask; + if (version != Version1) + { + throw new TProtocolException(TProtocolException.BAD_VERSION, + $"Bad version in ReadMessageBegin: {version}"); + } + message.Type = (TMessageType) (size & 0x000000ff); + message.Name = await ReadStringAsync(cancellationToken); + message.SeqID = await ReadI32Async(cancellationToken); + } + else + { + if (StrictRead) + { + throw new TProtocolException(TProtocolException.BAD_VERSION, + "Missing version in ReadMessageBegin, old client?"); + } + message.Name = (size > 0) ? await ReadStringBodyAsync(size, cancellationToken) : string.Empty; + message.Type = (TMessageType) await ReadByteAsync(cancellationToken); + message.SeqID = await ReadI32Async(cancellationToken); + } + return message; + } + + public override Task ReadMessageEndAsync(CancellationToken cancellationToken) + { + cancellationToken.ThrowIfCancellationRequested(); + return Task.CompletedTask; + } + + public override ValueTask ReadStructBeginAsync(CancellationToken cancellationToken) + { + cancellationToken.ThrowIfCancellationRequested(); + return new ValueTask(AnonymousStruct); + } + + public override Task ReadStructEndAsync(CancellationToken cancellationToken) + { + cancellationToken.ThrowIfCancellationRequested(); + return Task.CompletedTask; + } + + public override async ValueTask ReadFieldBeginAsync(CancellationToken cancellationToken) + { + cancellationToken.ThrowIfCancellationRequested(); + + var type = (TType)await ReadByteAsync(cancellationToken); + if (type == TType.Stop) + { + return StopField; + } + + return new TField { + Type = type, + ID = await ReadI16Async(cancellationToken) + }; + } + + public override Task ReadFieldEndAsync(CancellationToken cancellationToken) + { + cancellationToken.ThrowIfCancellationRequested(); + return Task.CompletedTask; + } + + public override async ValueTask ReadMapBeginAsync(CancellationToken cancellationToken) + { + cancellationToken.ThrowIfCancellationRequested(); + + var map = new TMap + { + KeyType = (TType) await ReadByteAsync(cancellationToken), + ValueType = (TType) await ReadByteAsync(cancellationToken), + Count = await ReadI32Async(cancellationToken) + }; + CheckReadBytesAvailable(map); + return map; + } + + public override Task ReadMapEndAsync(CancellationToken cancellationToken) + { + cancellationToken.ThrowIfCancellationRequested(); + return Task.CompletedTask; + } + + public override async ValueTask ReadListBeginAsync(CancellationToken cancellationToken) + { + cancellationToken.ThrowIfCancellationRequested(); + + var list = new TList + { + ElementType = (TType) await ReadByteAsync(cancellationToken), + Count = await ReadI32Async(cancellationToken) + }; + CheckReadBytesAvailable(list); + return list; + } + + public override Task ReadListEndAsync(CancellationToken cancellationToken) + { + cancellationToken.ThrowIfCancellationRequested(); + return Task.CompletedTask; + } + + public override async ValueTask ReadSetBeginAsync(CancellationToken cancellationToken) + { + cancellationToken.ThrowIfCancellationRequested(); + + var set = new TSet + { + ElementType = (TType) await ReadByteAsync(cancellationToken), + Count = await ReadI32Async(cancellationToken) + }; + CheckReadBytesAvailable(set); + return set; + } + + public override Task ReadSetEndAsync(CancellationToken cancellationToken) + { + cancellationToken.ThrowIfCancellationRequested(); + return Task.CompletedTask; + } + + public override async ValueTask ReadBoolAsync(CancellationToken cancellationToken) + { + cancellationToken.ThrowIfCancellationRequested(); + + return await ReadByteAsync(cancellationToken) == 1; + } + + public override async ValueTask ReadByteAsync(CancellationToken cancellationToken) + { + cancellationToken.ThrowIfCancellationRequested(); + + await Trans.ReadAllAsync(PreAllocatedBuffer, 0, 1, cancellationToken); + return (sbyte)PreAllocatedBuffer[0]; + } + + public override async ValueTask ReadI16Async(CancellationToken cancellationToken) + { + cancellationToken.ThrowIfCancellationRequested(); + + await Trans.ReadAllAsync(PreAllocatedBuffer, 0, 2, cancellationToken); + var result = BinaryPrimitives.ReadInt16BigEndian(PreAllocatedBuffer); + return result; + } + + public override async ValueTask ReadI32Async(CancellationToken cancellationToken) + { + cancellationToken.ThrowIfCancellationRequested(); + + await Trans.ReadAllAsync(PreAllocatedBuffer, 0, 4, cancellationToken); + + var result = BinaryPrimitives.ReadInt32BigEndian(PreAllocatedBuffer); + + return result; + } + + public override async ValueTask ReadI64Async(CancellationToken cancellationToken) + { + cancellationToken.ThrowIfCancellationRequested(); + + await Trans.ReadAllAsync(PreAllocatedBuffer, 0, 8, cancellationToken); + return BinaryPrimitives.ReadInt64BigEndian(PreAllocatedBuffer); + } + + public override async ValueTask ReadDoubleAsync(CancellationToken cancellationToken) + { + cancellationToken.ThrowIfCancellationRequested(); + + var d = await ReadI64Async(cancellationToken); + return BitConverter.Int64BitsToDouble(d); + } + + public override async ValueTask ReadBinaryAsync(CancellationToken cancellationToken) + { + cancellationToken.ThrowIfCancellationRequested(); + + var size = await ReadI32Async(cancellationToken); + Transport.CheckReadBytesAvailable(size); + var buf = new byte[size]; + await Trans.ReadAllAsync(buf, 0, size, cancellationToken); + return buf; + } + + public override async ValueTask ReadStringAsync(CancellationToken cancellationToken) + { + cancellationToken.ThrowIfCancellationRequested(); + + var size = await ReadI32Async(cancellationToken); + return size > 0 ? await ReadStringBodyAsync(size, cancellationToken) : string.Empty; + } + + private async ValueTask ReadStringBodyAsync(int size, CancellationToken cancellationToken) + { + cancellationToken.ThrowIfCancellationRequested(); + + if (size <= PreAllocatedBuffer.Length) + { + await Trans.ReadAllAsync(PreAllocatedBuffer, 0, size, cancellationToken); + return Encoding.UTF8.GetString(PreAllocatedBuffer, 0, size); + } + + Transport.CheckReadBytesAvailable(size); + var buf = new byte[size]; + await Trans.ReadAllAsync(buf, 0, size, cancellationToken); + return Encoding.UTF8.GetString(buf, 0, buf.Length); + } + + // Return the minimum number of bytes a type will consume on the wire + public override int GetMinSerializedSize(TType type) + { + switch (type) + { + case TType.Stop: return 0; + case TType.Void: return 0; + case TType.Bool: return sizeof(byte); + case TType.Byte: return sizeof(byte); + case TType.Double: return sizeof(double); + case TType.I16: return sizeof(short); + case TType.I32: return sizeof(int); + case TType.I64: return sizeof(long); + case TType.String: return sizeof(int); // string length + case TType.Struct: return 0; // empty struct + case TType.Map: return sizeof(int); // element count + case TType.Set: return sizeof(int); // element count + case TType.List: return sizeof(int); // element count + default: throw new TTransportException(TTransportException.ExceptionType.Unknown, "unrecognized type code"); + } + } + + public class Factory : TProtocolFactory + { + protected bool StrictRead; + protected bool StrictWrite; + + public Factory() + : this(false, true) + { + } + + public Factory(bool strictRead, bool strictWrite) + { + StrictRead = strictRead; + StrictWrite = strictWrite; + } + + public override TProtocol GetProtocol(TTransport trans) + { + return new TBinaryProtocol(trans, StrictRead, StrictWrite); + } + } + } +} diff --git a/lib/netstd/Thrift/Protocol/TCompactProtocol.cs b/lib/netstd/Thrift/Protocol/TCompactProtocol.cs new file mode 100644 index 00000000000..066b327227c --- /dev/null +++ b/lib/netstd/Thrift/Protocol/TCompactProtocol.cs @@ -0,0 +1,840 @@ +// Licensed to the Apache Software Foundation(ASF) under one +// or more contributor license agreements.See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership.The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +using System; +using System.Buffers; +using System.Buffers.Binary; +using System.Collections.Generic; +using System.Diagnostics; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using Thrift.Protocol.Entities; +using Thrift.Transport; + +namespace Thrift.Protocol +{ + //TODO: implementation of TProtocol + + // ReSharper disable once InconsistentNaming + public class TCompactProtocol : TProtocol + { + private const byte ProtocolId = 0x82; + private const byte Version = 1; + private const byte VersionMask = 0x1f; // 0001 1111 + private const byte TypeMask = 0xE0; // 1110 0000 + private const byte TypeBits = 0x07; // 0000 0111 + private const int TypeShiftAmount = 5; + + private const byte NoTypeOverride = 0xFF; + + // ReSharper disable once InconsistentNaming + private static readonly byte[] TTypeToCompactType = new byte[16]; + private static readonly TType[] CompactTypeToTType = new TType[13]; + + /// + /// Used to keep track of the last field for the current and previous structs, so we can do the delta stuff. + /// + private readonly Stack _lastField = new Stack(15); + + /// + /// If we encounter a boolean field begin, save the TField here so it can have the value incorporated. + /// + private TField? _booleanField; + + /// + /// If we Read a field header, and it's a boolean field, save the boolean value here so that ReadBool can use it. + /// + private bool? _boolValue; + + private short _lastFieldId; + + // minimize memory allocations by means of an preallocated bytes buffer + // The value of 128 is arbitrarily chosen, the required minimum size must be sizeof(long) + private readonly byte[] PreAllocatedBuffer = new byte[128]; + + private struct VarInt + { + public byte[] bytes; + public int count; + } + + // minimize memory allocations by means of an preallocated VarInt buffer + private VarInt PreAllocatedVarInt = new VarInt() + { + bytes = new byte[10], // see Int64ToVarInt() + count = 0 + }; + + + + + public TCompactProtocol(TTransport trans) + : base(trans) + { + TTypeToCompactType[(int) TType.Stop] = Types.Stop; + TTypeToCompactType[(int) TType.Bool] = Types.BooleanTrue; + TTypeToCompactType[(int) TType.Byte] = Types.Byte; + TTypeToCompactType[(int) TType.I16] = Types.I16; + TTypeToCompactType[(int) TType.I32] = Types.I32; + TTypeToCompactType[(int) TType.I64] = Types.I64; + TTypeToCompactType[(int) TType.Double] = Types.Double; + TTypeToCompactType[(int) TType.String] = Types.Binary; + TTypeToCompactType[(int) TType.List] = Types.List; + TTypeToCompactType[(int) TType.Set] = Types.Set; + TTypeToCompactType[(int) TType.Map] = Types.Map; + TTypeToCompactType[(int) TType.Struct] = Types.Struct; + + CompactTypeToTType[Types.Stop] = TType.Stop; + CompactTypeToTType[Types.BooleanTrue] = TType.Bool; + CompactTypeToTType[Types.BooleanFalse] = TType.Bool; + CompactTypeToTType[Types.Byte] = TType.Byte; + CompactTypeToTType[Types.I16] = TType.I16; + CompactTypeToTType[Types.I32] = TType.I32; + CompactTypeToTType[Types.I64] = TType.I64; + CompactTypeToTType[Types.Double] = TType.Double; + CompactTypeToTType[Types.Binary] = TType.String; + CompactTypeToTType[Types.List] = TType.List; + CompactTypeToTType[Types.Set] = TType.Set; + CompactTypeToTType[Types.Map] = TType.Map; + CompactTypeToTType[Types.Struct] = TType.Struct; + } + + public void Reset() + { + _lastField.Clear(); + _lastFieldId = 0; + } + + public override async Task WriteMessageBeginAsync(TMessage message, CancellationToken cancellationToken) + { + PreAllocatedBuffer[0] = ProtocolId; + PreAllocatedBuffer[1] = (byte)((Version & VersionMask) | (((uint)message.Type << TypeShiftAmount) & TypeMask)); + await Trans.WriteAsync(PreAllocatedBuffer, 0, 2, cancellationToken); + + Int32ToVarInt((uint) message.SeqID, ref PreAllocatedVarInt); + await Trans.WriteAsync(PreAllocatedVarInt.bytes, 0, PreAllocatedVarInt.count, cancellationToken); + + await WriteStringAsync(message.Name, cancellationToken); + } + + public override Task WriteMessageEndAsync(CancellationToken cancellationToken) + { + cancellationToken.ThrowIfCancellationRequested(); + return Task.CompletedTask; + } + + /// + /// Write a struct begin. This doesn't actually put anything on the wire. We + /// use it as an opportunity to put special placeholder markers on the field + /// stack so we can get the field id deltas correct. + /// + public override Task WriteStructBeginAsync(TStruct @struct, CancellationToken cancellationToken) + { + cancellationToken.ThrowIfCancellationRequested(); + + _lastField.Push(_lastFieldId); + _lastFieldId = 0; + + return Task.CompletedTask; + } + + public override Task WriteStructEndAsync(CancellationToken cancellationToken) + { + cancellationToken.ThrowIfCancellationRequested(); + + _lastFieldId = _lastField.Pop(); + + return Task.CompletedTask; + } + + private async Task WriteFieldBeginInternalAsync(TField field, byte fieldType, CancellationToken cancellationToken) + { + // if there's a exType override passed in, use that. Otherwise ask GetCompactType(). + if (fieldType == NoTypeOverride) + fieldType = GetCompactType(field.Type); + + + // check if we can use delta encoding for the field id + if (field.ID > _lastFieldId) + { + var delta = field.ID - _lastFieldId; + if (delta <= 15) + { + // Write them together + PreAllocatedBuffer[0] = (byte)((delta << 4) | fieldType); + await Trans.WriteAsync(PreAllocatedBuffer, 0, 1, cancellationToken); + _lastFieldId = field.ID; + return; + } + } + + // Write them separate + PreAllocatedBuffer[0] = fieldType; + await Trans.WriteAsync(PreAllocatedBuffer, 0, 1, cancellationToken); + await WriteI16Async(field.ID, cancellationToken); + _lastFieldId = field.ID; + } + + public override async Task WriteFieldBeginAsync(TField field, CancellationToken cancellationToken) + { + if (field.Type == TType.Bool) + { + _booleanField = field; + } + else + { + await WriteFieldBeginInternalAsync(field, NoTypeOverride, cancellationToken); + } + } + + public override Task WriteFieldEndAsync(CancellationToken cancellationToken) + { + cancellationToken.ThrowIfCancellationRequested(); + return Task.CompletedTask; + } + + public override async Task WriteFieldStopAsync(CancellationToken cancellationToken) + { + cancellationToken.ThrowIfCancellationRequested(); + + PreAllocatedBuffer[0] = Types.Stop; + await Trans.WriteAsync(PreAllocatedBuffer, 0, 1, cancellationToken); + } + + protected async Task WriteCollectionBeginAsync(TType elemType, int size, CancellationToken cancellationToken) + { + cancellationToken.ThrowIfCancellationRequested(); + + /* + Abstract method for writing the start of lists and sets. List and sets on + the wire differ only by the exType indicator. + */ + + if (size <= 14) + { + PreAllocatedBuffer[0] = (byte)((size << 4) | GetCompactType(elemType)); + await Trans.WriteAsync(PreAllocatedBuffer, 0, 1, cancellationToken); + } + else + { + PreAllocatedBuffer[0] = (byte)(0xf0 | GetCompactType(elemType)); + await Trans.WriteAsync(PreAllocatedBuffer, 0, 1, cancellationToken); + + Int32ToVarInt((uint) size, ref PreAllocatedVarInt); + await Trans.WriteAsync(PreAllocatedVarInt.bytes, 0, PreAllocatedVarInt.count, cancellationToken); + } + } + + public override async Task WriteListBeginAsync(TList list, CancellationToken cancellationToken) + { + await WriteCollectionBeginAsync(list.ElementType, list.Count, cancellationToken); + } + + public override Task WriteListEndAsync(CancellationToken cancellationToken) + { + cancellationToken.ThrowIfCancellationRequested(); + return Task.CompletedTask; + } + + public override async Task WriteSetBeginAsync(TSet set, CancellationToken cancellationToken) + { + cancellationToken.ThrowIfCancellationRequested(); + + await WriteCollectionBeginAsync(set.ElementType, set.Count, cancellationToken); + } + + public override Task WriteSetEndAsync(CancellationToken cancellationToken) + { + cancellationToken.ThrowIfCancellationRequested(); + return Task.CompletedTask; + } + + public override async Task WriteBoolAsync(bool b, CancellationToken cancellationToken) + { + cancellationToken.ThrowIfCancellationRequested(); + + /* + Write a boolean value. Potentially, this could be a boolean field, in + which case the field header info isn't written yet. If so, decide what the + right exType header is for the value and then Write the field header. + Otherwise, Write a single byte. + */ + + if (_booleanField != null) + { + // we haven't written the field header yet + var type = b ? Types.BooleanTrue : Types.BooleanFalse; + await WriteFieldBeginInternalAsync(_booleanField.Value, type, cancellationToken); + _booleanField = null; + } + else + { + // we're not part of a field, so just write the value. + PreAllocatedBuffer[0] = b ? Types.BooleanTrue : Types.BooleanFalse; + await Trans.WriteAsync(PreAllocatedBuffer, 0, 1, cancellationToken); + } + } + + public override async Task WriteByteAsync(sbyte b, CancellationToken cancellationToken) + { + cancellationToken.ThrowIfCancellationRequested(); + + PreAllocatedBuffer[0] = (byte)b; + await Trans.WriteAsync(PreAllocatedBuffer, 0, 1, cancellationToken); + } + + public override async Task WriteI16Async(short i16, CancellationToken cancellationToken) + { + cancellationToken.ThrowIfCancellationRequested(); + + Int32ToVarInt(IntToZigzag(i16), ref PreAllocatedVarInt); + await Trans.WriteAsync(PreAllocatedVarInt.bytes, 0, PreAllocatedVarInt.count, cancellationToken); + } + + private static void Int32ToVarInt(uint n, ref VarInt varint) + { + // Write an i32 as a varint. Results in 1 - 5 bytes on the wire. + varint.count = 0; + Debug.Assert(varint.bytes.Length >= 5); + + while (true) + { + if ((n & ~0x7F) == 0) + { + varint.bytes[varint.count++] = (byte)n; + break; + } + + varint.bytes[varint.count++] = (byte)((n & 0x7F) | 0x80); + n >>= 7; + } + } + + public override async Task WriteI32Async(int i32, CancellationToken cancellationToken) + { + cancellationToken.ThrowIfCancellationRequested(); + + Int32ToVarInt(IntToZigzag(i32), ref PreAllocatedVarInt); + await Trans.WriteAsync(PreAllocatedVarInt.bytes, 0, PreAllocatedVarInt.count, cancellationToken); + } + + static private void Int64ToVarInt(ulong n, ref VarInt varint) + { + // Write an i64 as a varint. Results in 1-10 bytes on the wire. + varint.count = 0; + Debug.Assert(varint.bytes.Length >= 10); + + while (true) + { + if ((n & ~(ulong)0x7FL) == 0) + { + varint.bytes[varint.count++] = (byte)n; + break; + } + varint.bytes[varint.count++] = (byte)((n & 0x7F) | 0x80); + n >>= 7; + } + } + + public override async Task WriteI64Async(long i64, CancellationToken cancellationToken) + { + cancellationToken.ThrowIfCancellationRequested(); + + Int64ToVarInt(LongToZigzag(i64), ref PreAllocatedVarInt); + await Trans.WriteAsync(PreAllocatedVarInt.bytes, 0, PreAllocatedVarInt.count, cancellationToken); + } + + public override async Task WriteDoubleAsync(double d, CancellationToken cancellationToken) + { + cancellationToken.ThrowIfCancellationRequested(); + + BinaryPrimitives.WriteInt64LittleEndian(PreAllocatedBuffer, BitConverter.DoubleToInt64Bits(d)); + await Trans.WriteAsync(PreAllocatedBuffer, 0, 8, cancellationToken); + } + + public override async Task WriteStringAsync(string str, CancellationToken cancellationToken) + { + cancellationToken.ThrowIfCancellationRequested(); + + var buf = ArrayPool.Shared.Rent(Encoding.UTF8.GetByteCount(str)); + try + { + var numberOfBytes = Encoding.UTF8.GetBytes(str, 0, str.Length, buf, 0); + + Int32ToVarInt((uint)numberOfBytes, ref PreAllocatedVarInt); + await Trans.WriteAsync(PreAllocatedVarInt.bytes, 0, PreAllocatedVarInt.count, cancellationToken); + await Trans.WriteAsync(buf, 0, numberOfBytes, cancellationToken); + } + finally + { + ArrayPool.Shared.Return(buf); + } + } + + public override async Task WriteBinaryAsync(byte[] bytes, CancellationToken cancellationToken) + { + cancellationToken.ThrowIfCancellationRequested(); + + Int32ToVarInt((uint) bytes.Length, ref PreAllocatedVarInt); + await Trans.WriteAsync(PreAllocatedVarInt.bytes, 0, PreAllocatedVarInt.count, cancellationToken); + await Trans.WriteAsync(bytes, 0, bytes.Length, cancellationToken); + } + + public override async Task WriteMapBeginAsync(TMap map, CancellationToken cancellationToken) + { + cancellationToken.ThrowIfCancellationRequested(); + + if (map.Count == 0) + { + PreAllocatedBuffer[0] = 0; + await Trans.WriteAsync( PreAllocatedBuffer, 0, 1, cancellationToken); + } + else + { + Int32ToVarInt((uint) map.Count, ref PreAllocatedVarInt); + await Trans.WriteAsync(PreAllocatedVarInt.bytes, 0, PreAllocatedVarInt.count, cancellationToken); + + PreAllocatedBuffer[0] = (byte)((GetCompactType(map.KeyType) << 4) | GetCompactType(map.ValueType)); + await Trans.WriteAsync(PreAllocatedBuffer, 0, 1, cancellationToken); + } + } + + public override Task WriteMapEndAsync(CancellationToken cancellationToken) + { + cancellationToken.ThrowIfCancellationRequested(); + return Task.CompletedTask; + } + + public override async ValueTask ReadMessageBeginAsync(CancellationToken cancellationToken) + { + cancellationToken.ThrowIfCancellationRequested(); + + var protocolId = (byte) await ReadByteAsync(cancellationToken); + if (protocolId != ProtocolId) + { + throw new TProtocolException($"Expected protocol id {ProtocolId:X} but got {protocolId:X}"); + } + + var versionAndType = (byte) await ReadByteAsync(cancellationToken); + var version = (byte) (versionAndType & VersionMask); + + if (version != Version) + { + throw new TProtocolException($"Expected version {Version} but got {version}"); + } + + var type = (byte) ((versionAndType >> TypeShiftAmount) & TypeBits); + var seqid = (int) await ReadVarInt32Async(cancellationToken); + var messageName = await ReadStringAsync(cancellationToken); + + return new TMessage(messageName, (TMessageType) type, seqid); + } + + public override Task ReadMessageEndAsync(CancellationToken cancellationToken) + { + cancellationToken.ThrowIfCancellationRequested(); + return Task.CompletedTask; + } + + public override ValueTask ReadStructBeginAsync(CancellationToken cancellationToken) + { + cancellationToken.ThrowIfCancellationRequested(); + + _lastField.Push(_lastFieldId); + _lastFieldId = 0; + + return new ValueTask(AnonymousStruct); + } + + public override Task ReadStructEndAsync(CancellationToken cancellationToken) + { + cancellationToken.ThrowIfCancellationRequested(); + + /* + Doesn't actually consume any wire data, just removes the last field for + this struct from the field stack. + */ + + // consume the last field we Read off the wire. + _lastFieldId = _lastField.Pop(); + + return Task.CompletedTask; + } + + public override async ValueTask ReadFieldBeginAsync(CancellationToken cancellationToken) + { + // Read a field header off the wire. + var type = (byte) await ReadByteAsync(cancellationToken); + + // if it's a stop, then we can return immediately, as the struct is over. + if (type == Types.Stop) + { + return StopField; + } + + + // mask off the 4 MSB of the exType header. it could contain a field id delta. + var modifier = (short) ((type & 0xf0) >> 4); + var compactType = (byte)(type & 0x0f); + + short fieldId; + if (modifier == 0) + { + fieldId = await ReadI16Async(cancellationToken); + } + else + { + fieldId = (short) (_lastFieldId + modifier); + } + + var ttype = GetTType(compactType); + var field = new TField(string.Empty, ttype, fieldId); + + // if this happens to be a boolean field, the value is encoded in the exType + if( ttype == TType.Bool) + { + _boolValue = (compactType == Types.BooleanTrue); + } + + // push the new field onto the field stack so we can keep the deltas going. + _lastFieldId = field.ID; + return field; + } + + public override Task ReadFieldEndAsync(CancellationToken cancellationToken) + { + cancellationToken.ThrowIfCancellationRequested(); + return Task.CompletedTask; + } + + public override async ValueTask ReadMapBeginAsync(CancellationToken cancellationToken) + { + cancellationToken.ThrowIfCancellationRequested(); + + /* + Read a map header off the wire. If the size is zero, skip Reading the key + and value exType. This means that 0-length maps will yield TMaps without the + "correct" types. + */ + + var size = (int) await ReadVarInt32Async(cancellationToken); + var keyAndValueType = size == 0 ? (byte) 0 : (byte) await ReadByteAsync(cancellationToken); + var map = new TMap(GetTType((byte) (keyAndValueType >> 4)), GetTType((byte) (keyAndValueType & 0xf)), size); + CheckReadBytesAvailable(map); + return map; + } + + public override Task ReadMapEndAsync(CancellationToken cancellationToken) + { + cancellationToken.ThrowIfCancellationRequested(); + return Task.CompletedTask; + } + + public override async ValueTask ReadSetBeginAsync(CancellationToken cancellationToken) + { + /* + Read a set header off the wire. If the set size is 0-14, the size will + be packed into the element exType header. If it's a longer set, the 4 MSB + of the element exType header will be 0xF, and a varint will follow with the + true size. + */ + + return new TSet(await ReadListBeginAsync(cancellationToken)); + } + + public override ValueTask ReadBoolAsync(CancellationToken cancellationToken) + { + /* + Read a boolean off the wire. If this is a boolean field, the value should + already have been Read during ReadFieldBegin, so we'll just consume the + pre-stored value. Otherwise, Read a byte. + */ + + if (_boolValue != null) + { + var result = _boolValue.Value; + _boolValue = null; + return new ValueTask(result); + } + + return InternalCall(); + + async ValueTask InternalCall() + { + var data = await ReadByteAsync(cancellationToken); + return (data == Types.BooleanTrue); + } + } + + + public override async ValueTask ReadByteAsync(CancellationToken cancellationToken) + { + // Read a single byte off the wire. Nothing interesting here. + await Trans.ReadAllAsync(PreAllocatedBuffer, 0, 1, cancellationToken); + return (sbyte)PreAllocatedBuffer[0]; + } + + public override async ValueTask ReadI16Async(CancellationToken cancellationToken) + { + cancellationToken.ThrowIfCancellationRequested(); + + return (short) ZigzagToInt(await ReadVarInt32Async(cancellationToken)); + } + + public override async ValueTask ReadI32Async(CancellationToken cancellationToken) + { + cancellationToken.ThrowIfCancellationRequested(); + + return ZigzagToInt(await ReadVarInt32Async(cancellationToken)); + } + + public override async ValueTask ReadI64Async(CancellationToken cancellationToken) + { + cancellationToken.ThrowIfCancellationRequested(); + + return ZigzagToLong(await ReadVarInt64Async(cancellationToken)); + } + + public override async ValueTask ReadDoubleAsync(CancellationToken cancellationToken) + { + cancellationToken.ThrowIfCancellationRequested(); + + await Trans.ReadAllAsync(PreAllocatedBuffer, 0, 8, cancellationToken); + + return BitConverter.Int64BitsToDouble(BinaryPrimitives.ReadInt64LittleEndian(PreAllocatedBuffer)); + } + + public override async ValueTask ReadStringAsync(CancellationToken cancellationToken) + { + // read length + var length = (int) await ReadVarInt32Async(cancellationToken); + if (length == 0) + { + return string.Empty; + } + + // read and decode data + if (length < PreAllocatedBuffer.Length) + { + await Trans.ReadAllAsync(PreAllocatedBuffer, 0, length, cancellationToken); + return Encoding.UTF8.GetString(PreAllocatedBuffer, 0, length); + } + + Transport.CheckReadBytesAvailable(length); + + var buf = ArrayPool.Shared.Rent(length); + try + { + await Trans.ReadAllAsync(buf, 0, length, cancellationToken); + return Encoding.UTF8.GetString(buf, 0, length); + } + finally + { + ArrayPool.Shared.Return(buf); + } + } + + public override async ValueTask ReadBinaryAsync(CancellationToken cancellationToken) + { + // read length + var length = (int) await ReadVarInt32Async(cancellationToken); + if (length == 0) + { + return Array.Empty(); + } + + // read data + Transport.CheckReadBytesAvailable(length); + var buf = new byte[length]; + await Trans.ReadAllAsync(buf, 0, length, cancellationToken); + return buf; + } + + public override async ValueTask ReadListBeginAsync(CancellationToken cancellationToken) + { + cancellationToken.ThrowIfCancellationRequested(); + + /* + Read a list header off the wire. If the list size is 0-14, the size will + be packed into the element exType header. If it's a longer list, the 4 MSB + of the element exType header will be 0xF, and a varint will follow with the + true size. + */ + + var sizeAndType = (byte) await ReadByteAsync(cancellationToken); + var size = (sizeAndType >> 4) & 0x0f; + if (size == 15) + { + size = (int) await ReadVarInt32Async(cancellationToken); + } + + var type = GetTType(sizeAndType); + var list = new TList(type, size); + CheckReadBytesAvailable(list); + return list; + } + + public override Task ReadListEndAsync(CancellationToken cancellationToken) + { + cancellationToken.ThrowIfCancellationRequested(); + return Task.CompletedTask; + } + + public override Task ReadSetEndAsync(CancellationToken cancellationToken) + { + cancellationToken.ThrowIfCancellationRequested(); + return Task.CompletedTask; + } + + private static byte GetCompactType(TType ttype) + { + // Given a TType value, find the appropriate TCompactProtocol.Types constant. + return TTypeToCompactType[(int) ttype]; + } + + + private async ValueTask ReadVarInt32Async(CancellationToken cancellationToken) + { + cancellationToken.ThrowIfCancellationRequested(); + + /* + Read an i32 from the wire as a varint. The MSB of each byte is set + if there is another byte to follow. This can Read up to 5 bytes. + */ + + uint result = 0; + var shift = 0; + + while (true) + { + var b = (byte) await ReadByteAsync(cancellationToken); + result |= (uint) (b & 0x7f) << shift; + if ((b & 0x80) != 0x80) + { + break; + } + shift += 7; + } + + return result; + } + + private async ValueTask ReadVarInt64Async(CancellationToken cancellationToken) + { + cancellationToken.ThrowIfCancellationRequested(); + + /* + Read an i64 from the wire as a proper varint. The MSB of each byte is set + if there is another byte to follow. This can Read up to 10 bytes. + */ + + var shift = 0; + ulong result = 0; + while (true) + { + var b = (byte) await ReadByteAsync(cancellationToken); + result |= (ulong) (b & 0x7f) << shift; + if ((b & 0x80) != 0x80) + { + break; + } + shift += 7; + } + + return result; + } + + private static int ZigzagToInt(uint n) + { + return (int) (n >> 1) ^ -(int) (n & 1); + } + + private static long ZigzagToLong(ulong n) + { + return (long) (n >> 1) ^ -(long) (n & 1); + } + + private static TType GetTType(byte type) + { + // Given a TCompactProtocol.Types constant, convert it to its corresponding TType value. + return CompactTypeToTType[type & 0x0f]; + } + + private static ulong LongToZigzag(long n) + { + // Convert l into a zigzag long. This allows negative numbers to be represented compactly as a varint + return (ulong) (n << 1) ^ (ulong) (n >> 63); + } + + private static uint IntToZigzag(int n) + { + // Convert n into a zigzag int. This allows negative numbers to be represented compactly as a varint + return (uint) (n << 1) ^ (uint) (n >> 31); + } + + // Return the minimum number of bytes a type will consume on the wire + public override int GetMinSerializedSize(TType type) + { + switch (type) + { + case TType.Stop: return 0; + case TType.Void: return 0; + case TType.Bool: return sizeof(byte); + case TType.Double: return 8; // uses fixedLongToBytes() which always writes 8 bytes + case TType.Byte: return sizeof(byte); + case TType.I16: return sizeof(byte); // zigzag + case TType.I32: return sizeof(byte); // zigzag + case TType.I64: return sizeof(byte); // zigzag + case TType.String: return sizeof(byte); // string length + case TType.Struct: return 0; // empty struct + case TType.Map: return sizeof(byte); // element count + case TType.Set: return sizeof(byte); // element count + case TType.List: return sizeof(byte); // element count + default: throw new TTransportException(TTransportException.ExceptionType.Unknown, "unrecognized type code"); + } + } + + public class Factory : TProtocolFactory + { + public override TProtocol GetProtocol(TTransport trans) + { + return new TCompactProtocol(trans); + } + } + + /// + /// All of the on-wire exType codes. + /// + private static class Types + { + public const byte Stop = 0x00; + public const byte BooleanTrue = 0x01; + public const byte BooleanFalse = 0x02; + public const byte Byte = 0x03; + public const byte I16 = 0x04; + public const byte I32 = 0x05; + public const byte I64 = 0x06; + public const byte Double = 0x07; + public const byte Binary = 0x08; + public const byte List = 0x09; + public const byte Set = 0x0A; + public const byte Map = 0x0B; + public const byte Struct = 0x0C; + } + } +} diff --git a/lib/netcore/Thrift/Protocols/TJSONProtocol.cs b/lib/netstd/Thrift/Protocol/TJSONProtocol.cs similarity index 83% rename from lib/netcore/Thrift/Protocols/TJSONProtocol.cs rename to lib/netstd/Thrift/Protocol/TJSONProtocol.cs index 6d33f029e57..2f1ccdb8de8 100644 --- a/lib/netcore/Thrift/Protocols/TJSONProtocol.cs +++ b/lib/netstd/Thrift/Protocol/TJSONProtocol.cs @@ -23,11 +23,11 @@ using System.Text; using System.Threading; using System.Threading.Tasks; -using Thrift.Protocols.Entities; -using Thrift.Protocols.Utilities; -using Thrift.Transports; +using Thrift.Protocol.Entities; +using Thrift.Protocol.Utilities; +using Thrift.Transport; -namespace Thrift.Protocols +namespace Thrift.Protocol { /// /// JSON protocol implementation for thrift. @@ -59,7 +59,7 @@ public class TJsonProtocol : TProtocol /// /// TJsonProtocol Constructor /// - public TJsonProtocol(TClientTransport trans) + public TJsonProtocol(TTransport trans) : base(trans) { Context = new JSONBaseContext(this); @@ -83,6 +83,16 @@ protected void PopContext() Context = ContextStack.Pop(); } + /// + /// Resets the context stack to pristine state. Allows for reusal of the protocol + /// even in cases where the protocol instance was in an undefined state due to + /// dangling/stale/obsolete contexts + /// + private void resetContext() + { + ContextStack.Clear(); + Context = new JSONBaseContext(this); + } /// /// Read a byte that must match b[0]; otherwise an exception is thrown. /// Marked protected to avoid synthetic accessor in JSONListContext.Read @@ -102,7 +112,7 @@ protected async Task ReadJsonSyntaxCharAsync(byte[] bytes, CancellationToken can /// private async Task WriteJsonStringAsync(byte[] bytes, CancellationToken cancellationToken) { - await Context.WriteAsync(cancellationToken); + await Context.WriteConditionalDelimiterAsync(cancellationToken); await Trans.WriteAsync(TJSONProtocolConstants.Quote, cancellationToken); var len = bytes.Length; @@ -117,7 +127,7 @@ private async Task WriteJsonStringAsync(byte[] bytes, CancellationToken cancella } else { - await Trans.WriteAsync(bytes.ToArray(), i, 1, cancellationToken); + await Trans.WriteAsync(bytes, i, 1, cancellationToken); } } else @@ -150,7 +160,7 @@ private async Task WriteJsonStringAsync(byte[] bytes, CancellationToken cancella /// private async Task WriteJsonIntegerAsync(long num, CancellationToken cancellationToken) { - await Context.WriteAsync(cancellationToken); + await Context.WriteConditionalDelimiterAsync(cancellationToken); var str = num.ToString(); var escapeNum = Context.EscapeNumbers(); @@ -174,7 +184,7 @@ private async Task WriteJsonIntegerAsync(long num, CancellationToken cancellatio /// private async Task WriteJsonDoubleAsync(double num, CancellationToken cancellationToken) { - await Context.WriteAsync(cancellationToken); + await Context.WriteConditionalDelimiterAsync(cancellationToken); var str = num.ToString("G17", CultureInfo.InvariantCulture); var special = false; @@ -214,7 +224,7 @@ private async Task WriteJsonDoubleAsync(double num, CancellationToken cancellati /// private async Task WriteJsonBase64Async(byte[] bytes, CancellationToken cancellationToken) { - await Context.WriteAsync(cancellationToken); + await Context.WriteConditionalDelimiterAsync(cancellationToken); await Trans.WriteAsync(TJSONProtocolConstants.Quote, cancellationToken); var len = bytes.Length; @@ -223,7 +233,7 @@ private async Task WriteJsonBase64Async(byte[] bytes, CancellationToken cancella while (len >= 3) { // Encode 3 bytes at a time - TBase64Helper.Encode(bytes, off, 3, _tempBuffer, 0); + TBase64Utils.Encode(bytes, off, 3, _tempBuffer, 0); await Trans.WriteAsync(_tempBuffer, 0, 4, cancellationToken); off += 3; len -= 3; @@ -232,7 +242,7 @@ private async Task WriteJsonBase64Async(byte[] bytes, CancellationToken cancella if (len > 0) { // Encode remainder - TBase64Helper.Encode(bytes, off, len, _tempBuffer, 0); + TBase64Utils.Encode(bytes, off, len, _tempBuffer, 0); await Trans.WriteAsync(_tempBuffer, 0, len + 1, cancellationToken); } @@ -241,7 +251,7 @@ private async Task WriteJsonBase64Async(byte[] bytes, CancellationToken cancella private async Task WriteJsonObjectStartAsync(CancellationToken cancellationToken) { - await Context.WriteAsync(cancellationToken); + await Context.WriteConditionalDelimiterAsync(cancellationToken); await Trans.WriteAsync(TJSONProtocolConstants.LeftBrace, cancellationToken); PushContext(new JSONPairContext(this)); } @@ -254,7 +264,7 @@ private async Task WriteJsonObjectEndAsync(CancellationToken cancellationToken) private async Task WriteJsonArrayStartAsync(CancellationToken cancellationToken) { - await Context.WriteAsync(cancellationToken); + await Context.WriteConditionalDelimiterAsync(cancellationToken); await Trans.WriteAsync(TJSONProtocolConstants.LeftBracket, cancellationToken); PushContext(new JSONListContext(this)); } @@ -267,6 +277,7 @@ private async Task WriteJsonArrayEndAsync(CancellationToken cancellationToken) public override async Task WriteMessageBeginAsync(TMessage message, CancellationToken cancellationToken) { + resetContext(); await WriteJsonArrayStartAsync(cancellationToken); await WriteJsonIntegerAsync(Version, cancellationToken); @@ -304,12 +315,10 @@ public override async Task WriteFieldEndAsync(CancellationToken cancellationToke await WriteJsonObjectEndAsync(cancellationToken); } - public override async Task WriteFieldStopAsync(CancellationToken cancellationToken) + public override Task WriteFieldStopAsync(CancellationToken cancellationToken) { - if (cancellationToken.IsCancellationRequested) - { - await Task.FromCanceled(cancellationToken); - } + cancellationToken.ThrowIfCancellationRequested(); + return Task.CompletedTask; } public override async Task WriteMapBeginAsync(TMap map, CancellationToken cancellationToken) @@ -396,7 +405,7 @@ public override async Task WriteBinaryAsync(byte[] bytes, CancellationToken canc /// Read in a JSON string, unescaping as appropriate.. Skip Reading from the /// context if skipContext is true. /// - private async Task ReadJsonStringAsync(bool skipContext, CancellationToken cancellationToken) + private async ValueTask ReadJsonStringAsync(bool skipContext, CancellationToken cancellationToken) { using (var buffer = new MemoryStream()) { @@ -405,7 +414,7 @@ private async Task ReadJsonStringAsync(bool skipContext, CancellationTok if (!skipContext) { - await Context.ReadAsync(cancellationToken); + await Context.ReadConditionalDelimiterAsync(cancellationToken); } await ReadJsonSyntaxCharAsync(TJSONProtocolConstants.Quote, cancellationToken); @@ -487,7 +496,7 @@ private async Task ReadJsonStringAsync(bool skipContext, CancellationTok /// Read in a sequence of characters that are all valid in JSON numbers. Does /// not do a complete regex check to validate that this is actually a number. /// - private async Task ReadJsonNumericCharsAsync(CancellationToken cancellationToken) + private async ValueTask ReadJsonNumericCharsAsync(CancellationToken cancellationToken) { var strbld = new StringBuilder(); while (true) @@ -514,9 +523,9 @@ private async Task ReadJsonNumericCharsAsync(CancellationToken cancellat /// /// Read in a JSON number. If the context dictates, Read in enclosing quotes. /// - private async Task ReadJsonIntegerAsync(CancellationToken cancellationToken) + private async ValueTask ReadJsonIntegerAsync(CancellationToken cancellationToken) { - await Context.ReadAsync(cancellationToken); + await Context.ReadConditionalDelimiterAsync(cancellationToken); if (Context.EscapeNumbers()) { await ReadJsonSyntaxCharAsync(TJSONProtocolConstants.Quote, cancellationToken); @@ -542,9 +551,9 @@ private async Task ReadJsonIntegerAsync(CancellationToken cancellationToke /// Read in a JSON double value. Throw if the value is not wrapped in quotes /// when expected or if wrapped in quotes when not expected. /// - private async Task ReadJsonDoubleAsync(CancellationToken cancellationToken) + private async ValueTask ReadJsonDoubleAsync(CancellationToken cancellationToken) { - await Context.ReadAsync(cancellationToken); + await Context.ReadConditionalDelimiterAsync(cancellationToken); if (await Reader.PeekAsync(cancellationToken) == TJSONProtocolConstants.Quote[0]) { var arr = await ReadJsonStringAsync(true, cancellationToken); @@ -578,7 +587,7 @@ private async Task ReadJsonDoubleAsync(CancellationToken cancellationTok /// /// Read in a JSON string containing base-64 encoded data and decode it. /// - private async Task ReadJsonBase64Async(CancellationToken cancellationToken) + private async ValueTask ReadJsonBase64Async(CancellationToken cancellationToken) { var b = await ReadJsonStringAsync(false, cancellationToken); var len = b.Length; @@ -595,7 +604,7 @@ private async Task ReadJsonBase64Async(CancellationToken cancellationTok while (len > 4) { // Decode 4 bytes at a time - TBase64Helper.Decode(b, off, 4, b, size); // NB: decoded in place + TBase64Utils.Decode(b, off, 4, b, size); // NB: decoded in place off += 4; len -= 4; size += 3; @@ -606,7 +615,7 @@ private async Task ReadJsonBase64Async(CancellationToken cancellationTok if (len > 1) { // Decode remainder - TBase64Helper.Decode(b, off, len, b, size); // NB: decoded in place + TBase64Utils.Decode(b, off, len, b, size); // NB: decoded in place size += len - 1; } @@ -618,7 +627,7 @@ private async Task ReadJsonBase64Async(CancellationToken cancellationTok private async Task ReadJsonObjectStartAsync(CancellationToken cancellationToken) { - await Context.ReadAsync(cancellationToken); + await Context.ReadConditionalDelimiterAsync(cancellationToken); await ReadJsonSyntaxCharAsync(TJSONProtocolConstants.LeftBrace, cancellationToken); PushContext(new JSONPairContext(this)); } @@ -631,7 +640,7 @@ private async Task ReadJsonObjectEndAsync(CancellationToken cancellationToken) private async Task ReadJsonArrayStartAsync(CancellationToken cancellationToken) { - await Context.ReadAsync(cancellationToken); + await Context.ReadConditionalDelimiterAsync(cancellationToken); await ReadJsonSyntaxCharAsync(TJSONProtocolConstants.LeftBracket, cancellationToken); PushContext(new JSONListContext(this)); } @@ -642,9 +651,11 @@ private async Task ReadJsonArrayEndAsync(CancellationToken cancellationToken) PopContext(); } - public override async Task ReadMessageBeginAsync(CancellationToken cancellationToken) + public override async ValueTask ReadMessageBeginAsync(CancellationToken cancellationToken) { var message = new TMessage(); + + resetContext(); await ReadJsonArrayStartAsync(cancellationToken); if (await ReadJsonIntegerAsync(cancellationToken) != Version) { @@ -663,10 +674,11 @@ public override async Task ReadMessageEndAsync(CancellationToken cancellationTok await ReadJsonArrayEndAsync(cancellationToken); } - public override async Task ReadStructBeginAsync(CancellationToken cancellationToken) + public override async ValueTask ReadStructBeginAsync(CancellationToken cancellationToken) { await ReadJsonObjectStartAsync(cancellationToken); - return new TStruct(); + + return AnonymousStruct; } public override async Task ReadStructEndAsync(CancellationToken cancellationToken) @@ -674,20 +686,21 @@ public override async Task ReadStructEndAsync(CancellationToken cancellationToke await ReadJsonObjectEndAsync(cancellationToken); } - public override async Task ReadFieldBeginAsync(CancellationToken cancellationToken) + public override async ValueTask ReadFieldBeginAsync(CancellationToken cancellationToken) { - var field = new TField(); var ch = await Reader.PeekAsync(cancellationToken); if (ch == TJSONProtocolConstants.RightBrace[0]) { - field.Type = TType.Stop; + return StopField; } - else + + var field = new TField() { - field.ID = (short) await ReadJsonIntegerAsync(cancellationToken); - await ReadJsonObjectStartAsync(cancellationToken); - field.Type = TJSONProtocolHelper.GetTypeIdForTypeName(await ReadJsonStringAsync(false, cancellationToken)); - } + ID = (short)await ReadJsonIntegerAsync(cancellationToken) + }; + + await ReadJsonObjectStartAsync(cancellationToken); + field.Type = TJSONProtocolHelper.GetTypeIdForTypeName(await ReadJsonStringAsync(false, cancellationToken)); return field; } @@ -696,13 +709,14 @@ public override async Task ReadFieldEndAsync(CancellationToken cancellationToken await ReadJsonObjectEndAsync(cancellationToken); } - public override async Task ReadMapBeginAsync(CancellationToken cancellationToken) + public override async ValueTask ReadMapBeginAsync(CancellationToken cancellationToken) { var map = new TMap(); await ReadJsonArrayStartAsync(cancellationToken); map.KeyType = TJSONProtocolHelper.GetTypeIdForTypeName(await ReadJsonStringAsync(false, cancellationToken)); map.ValueType = TJSONProtocolHelper.GetTypeIdForTypeName(await ReadJsonStringAsync(false, cancellationToken)); map.Count = (int) await ReadJsonIntegerAsync(cancellationToken); + CheckReadBytesAvailable(map); await ReadJsonObjectStartAsync(cancellationToken); return map; } @@ -713,12 +727,13 @@ public override async Task ReadMapEndAsync(CancellationToken cancellationToken) await ReadJsonArrayEndAsync(cancellationToken); } - public override async Task ReadListBeginAsync(CancellationToken cancellationToken) + public override async ValueTask ReadListBeginAsync(CancellationToken cancellationToken) { var list = new TList(); await ReadJsonArrayStartAsync(cancellationToken); list.ElementType = TJSONProtocolHelper.GetTypeIdForTypeName(await ReadJsonStringAsync(false, cancellationToken)); list.Count = (int) await ReadJsonIntegerAsync(cancellationToken); + CheckReadBytesAvailable(list); return list; } @@ -727,12 +742,13 @@ public override async Task ReadListEndAsync(CancellationToken cancellationToken) await ReadJsonArrayEndAsync(cancellationToken); } - public override async Task ReadSetBeginAsync(CancellationToken cancellationToken) + public override async ValueTask ReadSetBeginAsync(CancellationToken cancellationToken) { var set = new TSet(); await ReadJsonArrayStartAsync(cancellationToken); set.ElementType = TJSONProtocolHelper.GetTypeIdForTypeName(await ReadJsonStringAsync(false, cancellationToken)); set.Count = (int) await ReadJsonIntegerAsync(cancellationToken); + CheckReadBytesAvailable(set); return set; } @@ -741,53 +757,75 @@ public override async Task ReadSetEndAsync(CancellationToken cancellationToken) await ReadJsonArrayEndAsync(cancellationToken); } - public override async Task ReadBoolAsync(CancellationToken cancellationToken) + public override async ValueTask ReadBoolAsync(CancellationToken cancellationToken) { return await ReadJsonIntegerAsync(cancellationToken) != 0; } - public override async Task ReadByteAsync(CancellationToken cancellationToken) + public override async ValueTask ReadByteAsync(CancellationToken cancellationToken) { return (sbyte) await ReadJsonIntegerAsync(cancellationToken); } - public override async Task ReadI16Async(CancellationToken cancellationToken) + public override async ValueTask ReadI16Async(CancellationToken cancellationToken) { return (short) await ReadJsonIntegerAsync(cancellationToken); } - public override async Task ReadI32Async(CancellationToken cancellationToken) + public override async ValueTask ReadI32Async(CancellationToken cancellationToken) { return (int) await ReadJsonIntegerAsync(cancellationToken); } - public override async Task ReadI64Async(CancellationToken cancellationToken) + public override async ValueTask ReadI64Async(CancellationToken cancellationToken) { return await ReadJsonIntegerAsync(cancellationToken); } - public override async Task ReadDoubleAsync(CancellationToken cancellationToken) + public override async ValueTask ReadDoubleAsync(CancellationToken cancellationToken) { return await ReadJsonDoubleAsync(cancellationToken); } - public override async Task ReadStringAsync(CancellationToken cancellationToken) + public override async ValueTask ReadStringAsync(CancellationToken cancellationToken) { var buf = await ReadJsonStringAsync(false, cancellationToken); return Utf8Encoding.GetString(buf, 0, buf.Length); } - public override async Task ReadBinaryAsync(CancellationToken cancellationToken) + public override async ValueTask ReadBinaryAsync(CancellationToken cancellationToken) { return await ReadJsonBase64Async(cancellationToken); } + // Return the minimum number of bytes a type will consume on the wire + public override int GetMinSerializedSize(TType type) + { + switch (type) + { + case TType.Stop: return 0; + case TType.Void: return 0; + case TType.Bool: return 1; // written as int + case TType.Byte: return 1; + case TType.Double: return 1; + case TType.I16: return 1; + case TType.I32: return 1; + case TType.I64: return 1; + case TType.String: return 2; // empty string + case TType.Struct: return 2; // empty struct + case TType.Map: return 2; // empty map + case TType.Set: return 2; // empty set + case TType.List: return 2; // empty list + default: throw new TTransportException(TTransportException.ExceptionType.Unknown, "unrecognized type code"); + } + } + /// /// Factory for JSON protocol objects /// - public class Factory : ITProtocolFactory + public class Factory : TProtocolFactory { - public TProtocol GetProtocol(TClientTransport trans) + public override TProtocol GetProtocol(TTransport trans) { return new TJsonProtocol(trans); } @@ -807,20 +845,16 @@ public JSONBaseContext(TJsonProtocol proto) Proto = proto; } - public virtual async Task WriteAsync(CancellationToken cancellationToken) + public virtual Task WriteConditionalDelimiterAsync(CancellationToken cancellationToken) { - if (cancellationToken.IsCancellationRequested) - { - await Task.FromCanceled(cancellationToken); - } + cancellationToken.ThrowIfCancellationRequested(); + return Task.CompletedTask; } - public virtual async Task ReadAsync(CancellationToken cancellationToken) + public virtual Task ReadConditionalDelimiterAsync(CancellationToken cancellationToken) { - if (cancellationToken.IsCancellationRequested) - { - await Task.FromCanceled(cancellationToken); - } + cancellationToken.ThrowIfCancellationRequested(); + return Task.CompletedTask; } public virtual bool EscapeNumbers() @@ -842,7 +876,7 @@ public JSONListContext(TJsonProtocol protocol) { } - public override async Task WriteAsync(CancellationToken cancellationToken) + public override async Task WriteConditionalDelimiterAsync(CancellationToken cancellationToken) { if (_first) { @@ -854,7 +888,7 @@ public override async Task WriteAsync(CancellationToken cancellationToken) } } - public override async Task ReadAsync(CancellationToken cancellationToken) + public override async Task ReadConditionalDelimiterAsync(CancellationToken cancellationToken) { if (_first) { @@ -885,7 +919,7 @@ public JSONPairContext(TJsonProtocol proto) { } - public override async Task WriteAsync(CancellationToken cancellationToken) + public override async Task WriteConditionalDelimiterAsync(CancellationToken cancellationToken) { if (_first) { @@ -899,7 +933,7 @@ public override async Task WriteAsync(CancellationToken cancellationToken) } } - public override async Task ReadAsync(CancellationToken cancellationToken) + public override async Task ReadConditionalDelimiterAsync(CancellationToken cancellationToken) { if (_first) { @@ -938,12 +972,9 @@ public LookaheadReader(TJsonProtocol proto) /// Return and consume the next byte to be Read, either taking it from the /// data buffer if present or getting it from the transport otherwise. /// - public async Task ReadAsync(CancellationToken cancellationToken) + public async ValueTask ReadAsync(CancellationToken cancellationToken) { - if (cancellationToken.IsCancellationRequested) - { - return await Task.FromCanceled(cancellationToken); - } + cancellationToken.ThrowIfCancellationRequested(); if (_hasData) { @@ -961,19 +992,16 @@ public async Task ReadAsync(CancellationToken cancellationToken) /// Return the next byte to be Read without consuming, filling the data /// buffer if it has not been filled alReady. /// - public async Task PeekAsync(CancellationToken cancellationToken) + public async ValueTask PeekAsync(CancellationToken cancellationToken) { - if (cancellationToken.IsCancellationRequested) - { - return await Task.FromCanceled(cancellationToken); - } + cancellationToken.ThrowIfCancellationRequested(); if (!_hasData) { // find more easy way to avoid exception on reading primitive types await Proto.Trans.ReadAllAsync(_data, 0, 1, cancellationToken); + _hasData = true; } - _hasData = true; return _data[0]; } } diff --git a/lib/netcore/Thrift/Protocols/TMultiplexedProtocol.cs b/lib/netstd/Thrift/Protocol/TMultiplexedProtocol.cs similarity index 95% rename from lib/netcore/Thrift/Protocols/TMultiplexedProtocol.cs rename to lib/netstd/Thrift/Protocol/TMultiplexedProtocol.cs index 367e4e64486..fbc8c05ccd7 100644 --- a/lib/netcore/Thrift/Protocols/TMultiplexedProtocol.cs +++ b/lib/netstd/Thrift/Protocol/TMultiplexedProtocol.cs @@ -17,9 +17,9 @@ using System.Threading; using System.Threading.Tasks; -using Thrift.Protocols.Entities; +using Thrift.Protocol.Entities; -namespace Thrift.Protocols +namespace Thrift.Protocol { /** * TMultiplexedProtocol is a protocol-independent concrete decorator that allows a Thrift @@ -31,7 +31,7 @@ namespace Thrift.Protocols * * This example uses a single socket transport to invoke two services: * - * TSocketClientTransport transport = new TSocketClientTransport("localhost", 9090); + * TSocketTransport transport = new TSocketTransport("localhost", 9090); * transport.open(); * * TBinaryProtocol protocol = new TBinaryProtocol(transport); diff --git a/lib/netstd/Thrift/Protocol/TProtocol.cs b/lib/netstd/Thrift/Protocol/TProtocol.cs new file mode 100644 index 00000000000..5b1bec16b53 --- /dev/null +++ b/lib/netstd/Thrift/Protocol/TProtocol.cs @@ -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. + +using System; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using Thrift.Protocol.Entities; +using Thrift.Transport; + +namespace Thrift.Protocol +{ + // ReSharper disable once InconsistentNaming + public abstract class TProtocol : IDisposable + { + private bool _isDisposed; + protected int RecursionDepth; + + protected TTransport Trans; + + protected static readonly TStruct AnonymousStruct = new TStruct(string.Empty); + protected static readonly TField StopField = new TField() { Type = TType.Stop }; + + + protected TProtocol(TTransport trans) + { + Trans = trans; + RecursionLimit = trans.Configuration.RecursionLimit; + RecursionDepth = 0; + } + + public TTransport Transport => Trans; + + protected int RecursionLimit { get; set; } + + public void Dispose() + { + Dispose(true); + } + + public void IncrementRecursionDepth() + { + if (RecursionDepth < RecursionLimit) + { + ++RecursionDepth; + } + else + { + throw new TProtocolException(TProtocolException.DEPTH_LIMIT, "Depth limit exceeded"); + } + } + + public void DecrementRecursionDepth() + { + --RecursionDepth; + } + + protected virtual void Dispose(bool disposing) + { + if (!_isDisposed) + { + if (disposing) + { + (Trans as IDisposable)?.Dispose(); + } + } + _isDisposed = true; + } + + + protected void CheckReadBytesAvailable(TSet set) + { + Transport.CheckReadBytesAvailable(set.Count * GetMinSerializedSize(set.ElementType)); + } + + protected void CheckReadBytesAvailable(TList list) + { + Transport.CheckReadBytesAvailable(list.Count * GetMinSerializedSize(list.ElementType)); + } + + protected void CheckReadBytesAvailable(TMap map) + { + var elmSize = GetMinSerializedSize(map.KeyType) + GetMinSerializedSize(map.ValueType); + Transport.CheckReadBytesAvailable(map.Count * elmSize); + } + + // Returns the minimum amount of bytes needed to store the smallest possible instance of TType. + public abstract int GetMinSerializedSize(TType type); + + + public abstract Task WriteMessageBeginAsync(TMessage message, CancellationToken cancellationToken = default); + + public abstract Task WriteMessageEndAsync(CancellationToken cancellationToken = default); + + public abstract Task WriteStructBeginAsync(TStruct @struct, CancellationToken cancellationToken = default); + + public abstract Task WriteStructEndAsync(CancellationToken cancellationToken = default); + + public abstract Task WriteFieldBeginAsync(TField field, CancellationToken cancellationToken = default); + + public abstract Task WriteFieldEndAsync(CancellationToken cancellationToken = default); + + public abstract Task WriteFieldStopAsync(CancellationToken cancellationToken = default); + + public abstract Task WriteMapBeginAsync(TMap map, CancellationToken cancellationToken = default); + + public abstract Task WriteMapEndAsync(CancellationToken cancellationToken = default); + + public abstract Task WriteListBeginAsync(TList list, CancellationToken cancellationToken = default); + + public abstract Task WriteListEndAsync(CancellationToken cancellationToken = default); + + public abstract Task WriteSetBeginAsync(TSet set, CancellationToken cancellationToken = default); + + public abstract Task WriteSetEndAsync(CancellationToken cancellationToken = default); + + public abstract Task WriteBoolAsync(bool b, CancellationToken cancellationToken = default); + + public abstract Task WriteByteAsync(sbyte b, CancellationToken cancellationToken = default); + + public abstract Task WriteI16Async(short i16, CancellationToken cancellationToken = default); + + public abstract Task WriteI32Async(int i32, CancellationToken cancellationToken = default); + + public abstract Task WriteI64Async(long i64, CancellationToken cancellationToken = default); + + public abstract Task WriteDoubleAsync(double d, CancellationToken cancellationToken = default); + + public virtual async Task WriteStringAsync(string s, CancellationToken cancellationToken = default) + { + var bytes = Encoding.UTF8.GetBytes(s); + await WriteBinaryAsync(bytes, cancellationToken); + } + + public abstract Task WriteBinaryAsync(byte[] bytes, CancellationToken cancellationToken = default); + + public abstract ValueTask ReadMessageBeginAsync(CancellationToken cancellationToken = default); + + public abstract Task ReadMessageEndAsync(CancellationToken cancellationToken = default); + + public abstract ValueTask ReadStructBeginAsync(CancellationToken cancellationToken = default); + + public abstract Task ReadStructEndAsync(CancellationToken cancellationToken = default); + + public abstract ValueTask ReadFieldBeginAsync(CancellationToken cancellationToken = default); + + public abstract Task ReadFieldEndAsync(CancellationToken cancellationToken = default); + + public abstract ValueTask ReadMapBeginAsync(CancellationToken cancellationToken = default); + + public abstract Task ReadMapEndAsync(CancellationToken cancellationToken = default); + + public abstract ValueTask ReadListBeginAsync(CancellationToken cancellationToken = default); + + public abstract Task ReadListEndAsync(CancellationToken cancellationToken = default); + + public abstract ValueTask ReadSetBeginAsync(CancellationToken cancellationToken = default); + + public abstract Task ReadSetEndAsync(CancellationToken cancellationToken = default); + + public abstract ValueTask ReadBoolAsync(CancellationToken cancellationToken = default); + + public abstract ValueTask ReadByteAsync(CancellationToken cancellationToken = default); + + public abstract ValueTask ReadI16Async(CancellationToken cancellationToken = default); + + public abstract ValueTask ReadI32Async(CancellationToken cancellationToken = default); + + public abstract ValueTask ReadI64Async(CancellationToken cancellationToken = default); + + public abstract ValueTask ReadDoubleAsync(CancellationToken cancellationToken = default); + + public virtual async ValueTask ReadStringAsync(CancellationToken cancellationToken = default) + { + var buf = await ReadBinaryAsync(cancellationToken); + return Encoding.UTF8.GetString(buf, 0, buf.Length); + } + + public abstract ValueTask ReadBinaryAsync(CancellationToken cancellationToken = default); + } +} diff --git a/lib/netcore/Thrift/Protocols/TProtocolDecorator.cs b/lib/netstd/Thrift/Protocol/TProtocolDecorator.cs similarity index 82% rename from lib/netcore/Thrift/Protocols/TProtocolDecorator.cs rename to lib/netstd/Thrift/Protocol/TProtocolDecorator.cs index 3222754a871..b032e836a60 100644 --- a/lib/netcore/Thrift/Protocols/TProtocolDecorator.cs +++ b/lib/netstd/Thrift/Protocol/TProtocolDecorator.cs @@ -1,4 +1,4 @@ -// Licensed to the Apache Software Foundation(ASF) under one +// Licensed to the Apache Software Foundation(ASF) under one // or more contributor license agreements.See the NOTICE file // distributed with this work for additional information // regarding copyright ownership.The ASF licenses this file @@ -18,9 +18,9 @@ using System; using System.Threading; using System.Threading.Tasks; -using Thrift.Protocols.Entities; +using Thrift.Protocol.Entities; -namespace Thrift.Protocols +namespace Thrift.Protocol { // ReSharper disable once InconsistentNaming /// @@ -144,7 +144,7 @@ public override async Task WriteBinaryAsync(byte[] bytes, CancellationToken canc await _wrappedProtocol.WriteBinaryAsync(bytes, cancellationToken); } - public override async Task ReadMessageBeginAsync(CancellationToken cancellationToken) + public override async ValueTask ReadMessageBeginAsync(CancellationToken cancellationToken) { return await _wrappedProtocol.ReadMessageBeginAsync(cancellationToken); } @@ -154,7 +154,7 @@ public override async Task ReadMessageEndAsync(CancellationToken cancellationTok await _wrappedProtocol.ReadMessageEndAsync(cancellationToken); } - public override async Task ReadStructBeginAsync(CancellationToken cancellationToken) + public override async ValueTask ReadStructBeginAsync(CancellationToken cancellationToken) { return await _wrappedProtocol.ReadStructBeginAsync(cancellationToken); } @@ -164,7 +164,7 @@ public override async Task ReadStructEndAsync(CancellationToken cancellationToke await _wrappedProtocol.ReadStructEndAsync(cancellationToken); } - public override async Task ReadFieldBeginAsync(CancellationToken cancellationToken) + public override async ValueTask ReadFieldBeginAsync(CancellationToken cancellationToken) { return await _wrappedProtocol.ReadFieldBeginAsync(cancellationToken); } @@ -174,7 +174,7 @@ public override async Task ReadFieldEndAsync(CancellationToken cancellationToken await _wrappedProtocol.ReadFieldEndAsync(cancellationToken); } - public override async Task ReadMapBeginAsync(CancellationToken cancellationToken) + public override async ValueTask ReadMapBeginAsync(CancellationToken cancellationToken) { return await _wrappedProtocol.ReadMapBeginAsync(cancellationToken); } @@ -184,7 +184,7 @@ public override async Task ReadMapEndAsync(CancellationToken cancellationToken) await _wrappedProtocol.ReadMapEndAsync(cancellationToken); } - public override async Task ReadListBeginAsync(CancellationToken cancellationToken) + public override async ValueTask ReadListBeginAsync(CancellationToken cancellationToken) { return await _wrappedProtocol.ReadListBeginAsync(cancellationToken); } @@ -194,7 +194,7 @@ public override async Task ReadListEndAsync(CancellationToken cancellationToken) await _wrappedProtocol.ReadListEndAsync(cancellationToken); } - public override async Task ReadSetBeginAsync(CancellationToken cancellationToken) + public override async ValueTask ReadSetBeginAsync(CancellationToken cancellationToken) { return await _wrappedProtocol.ReadSetBeginAsync(cancellationToken); } @@ -204,44 +204,52 @@ public override async Task ReadSetEndAsync(CancellationToken cancellationToken) await _wrappedProtocol.ReadSetEndAsync(cancellationToken); } - public override async Task ReadBoolAsync(CancellationToken cancellationToken) + public override async ValueTask ReadBoolAsync(CancellationToken cancellationToken) { return await _wrappedProtocol.ReadBoolAsync(cancellationToken); } - public override async Task ReadByteAsync(CancellationToken cancellationToken) + public override async ValueTask ReadByteAsync(CancellationToken cancellationToken) { return await _wrappedProtocol.ReadByteAsync(cancellationToken); } - public override async Task ReadI16Async(CancellationToken cancellationToken) + public override async ValueTask ReadI16Async(CancellationToken cancellationToken) { return await _wrappedProtocol.ReadI16Async(cancellationToken); } - public override async Task ReadI32Async(CancellationToken cancellationToken) + public override async ValueTask ReadI32Async(CancellationToken cancellationToken) { return await _wrappedProtocol.ReadI32Async(cancellationToken); } - public override async Task ReadI64Async(CancellationToken cancellationToken) + public override async ValueTask ReadI64Async(CancellationToken cancellationToken) { return await _wrappedProtocol.ReadI64Async(cancellationToken); } - public override async Task ReadDoubleAsync(CancellationToken cancellationToken) + public override async ValueTask ReadDoubleAsync(CancellationToken cancellationToken) { return await _wrappedProtocol.ReadDoubleAsync(cancellationToken); } - public override async Task ReadStringAsync(CancellationToken cancellationToken) + public override async ValueTask ReadStringAsync(CancellationToken cancellationToken) { return await _wrappedProtocol.ReadStringAsync(cancellationToken); } - public override async Task ReadBinaryAsync(CancellationToken cancellationToken) + public override async ValueTask ReadBinaryAsync(CancellationToken cancellationToken) { return await _wrappedProtocol.ReadBinaryAsync(cancellationToken); } + + // Returns the minimum amount of bytes needed to store the smallest possible instance of TType. + public override int GetMinSerializedSize(TType type) + { + return _wrappedProtocol.GetMinSerializedSize(type); + } + + } -} \ No newline at end of file +} diff --git a/lib/netcore/Thrift/Protocols/TProtocolException.cs b/lib/netstd/Thrift/Protocol/TProtocolException.cs similarity index 80% rename from lib/netcore/Thrift/Protocols/TProtocolException.cs rename to lib/netstd/Thrift/Protocol/TProtocolException.cs index 8c67c3bfdf9..328babd051b 100644 --- a/lib/netcore/Thrift/Protocols/TProtocolException.cs +++ b/lib/netstd/Thrift/Protocol/TProtocolException.cs @@ -16,7 +16,9 @@ // under the License. // ReSharper disable InconsistentNaming -namespace Thrift.Protocols +using System; + +namespace Thrift.Protocol { public class TProtocolException : TException { @@ -35,19 +37,20 @@ public TProtocolException() { } - public TProtocolException(int type) + public TProtocolException(int type, Exception inner = null) + : base(string.Empty, inner) { Type = type; } - public TProtocolException(int type, string message) - : base(message) + public TProtocolException(int type, string message, Exception inner = null) + : base(message, inner) { Type = type; } - public TProtocolException(string message) - : base(message) + public TProtocolException(string message, Exception inner = null) + : base(message, inner) { } diff --git a/lib/netcore/Thrift/Protocols/ITProtocolFactory.cs b/lib/netstd/Thrift/Protocol/TProtocolFactory.cs similarity index 84% rename from lib/netcore/Thrift/Protocols/ITProtocolFactory.cs rename to lib/netstd/Thrift/Protocol/TProtocolFactory.cs index ecc5cc494d4..31b05148b32 100644 --- a/lib/netcore/Thrift/Protocols/ITProtocolFactory.cs +++ b/lib/netstd/Thrift/Protocol/TProtocolFactory.cs @@ -15,13 +15,13 @@ // specific language governing permissions and limitations // under the License. -using Thrift.Transports; +using Thrift.Transport; -namespace Thrift.Protocols +namespace Thrift.Protocol { // ReSharper disable once InconsistentNaming - public interface ITProtocolFactory + public abstract class TProtocolFactory { - TProtocol GetProtocol(TClientTransport trans); + public abstract TProtocol GetProtocol(TTransport trans); } -} \ No newline at end of file +} diff --git a/lib/netstd/Thrift/Protocol/ToString.cs b/lib/netstd/Thrift/Protocol/ToString.cs new file mode 100644 index 00000000000..14fa5204fa0 --- /dev/null +++ b/lib/netstd/Thrift/Protocol/ToString.cs @@ -0,0 +1,82 @@ +// Licensed to the Apache Software Foundation(ASF) under one +// or more contributor license agreements.See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership.The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Text; +using Thrift.Protocol; + +namespace Thrift.Protocol +{ + + + public static class ToStringExtensions + { + public static void ToString(this object self, StringBuilder sb, bool first = true) + { + if (!first) + sb.Append(", "); + + bool first_child = true; + if (self is string) // string is IEnumerable + { + sb.Append('"'); + sb.Append(self); + sb.Append('"'); + } + else if (self is IDictionary) + { + sb.Append("{ "); + foreach (DictionaryEntry pair in (self as IDictionary)) + { + if (first_child) + first_child = false; + else + sb.Append(","); + + sb.Append("{ "); + pair.Key.ToString(sb); + sb.Append(", "); + pair.Value.ToString(sb); + sb.Append("}"); + } + sb.Append("}"); + } + else if (self is IEnumerable) + { + sb.Append("{ "); + foreach (var elm in (self as IEnumerable)) + { + elm.ToString(sb, first_child); + first_child = false; + } + sb.Append("}"); + } + else if (self is TBase) + { + sb.Append((self as TBase).ToString()); + } + else + { + sb.Append(self != null? self.ToString() : ""); + } + } + } + + +} diff --git a/lib/netcore/Thrift/Protocols/Utilities/TBase64Utils.cs b/lib/netstd/Thrift/Protocol/Utilities/TBase64Utils.cs similarity index 99% rename from lib/netcore/Thrift/Protocols/Utilities/TBase64Utils.cs rename to lib/netstd/Thrift/Protocol/Utilities/TBase64Utils.cs index 15fd45cbef9..90b8f8867b1 100644 --- a/lib/netcore/Thrift/Protocols/Utilities/TBase64Utils.cs +++ b/lib/netstd/Thrift/Protocol/Utilities/TBase64Utils.cs @@ -17,7 +17,7 @@ using System; -namespace Thrift.Protocols.Utilities +namespace Thrift.Protocol.Utilities { // ReSharper disable once InconsistentNaming internal static class TBase64Utils diff --git a/lib/netcore/Thrift/Protocols/Utilities/TJsonProtocolConstants.cs b/lib/netstd/Thrift/Protocol/Utilities/TJsonProtocolConstants.cs similarity index 98% rename from lib/netcore/Thrift/Protocols/Utilities/TJsonProtocolConstants.cs rename to lib/netstd/Thrift/Protocol/Utilities/TJsonProtocolConstants.cs index 93eff785555..6cc1302e910 100644 --- a/lib/netcore/Thrift/Protocols/Utilities/TJsonProtocolConstants.cs +++ b/lib/netstd/Thrift/Protocol/Utilities/TJsonProtocolConstants.cs @@ -15,7 +15,7 @@ // specific language governing permissions and limitations // under the License. -namespace Thrift.Protocols.Utilities +namespace Thrift.Protocol.Utilities { // ReSharper disable once InconsistentNaming public static class TJSONProtocolConstants diff --git a/lib/netcore/Thrift/Protocols/Utilities/TJsonProtocolHelper.cs b/lib/netstd/Thrift/Protocol/Utilities/TJsonProtocolHelper.cs similarity index 98% rename from lib/netcore/Thrift/Protocols/Utilities/TJsonProtocolHelper.cs rename to lib/netstd/Thrift/Protocol/Utilities/TJsonProtocolHelper.cs index adc26a9af01..ff49ebe240e 100644 --- a/lib/netcore/Thrift/Protocols/Utilities/TJsonProtocolHelper.cs +++ b/lib/netstd/Thrift/Protocol/Utilities/TJsonProtocolHelper.cs @@ -15,9 +15,9 @@ // specific language governing permissions and limitations // under the License. -using Thrift.Protocols.Entities; +using Thrift.Protocol.Entities; -namespace Thrift.Protocols.Utilities +namespace Thrift.Protocol.Utilities { // ReSharper disable once InconsistentNaming public static class TJSONProtocolHelper diff --git a/lib/netcore/Thrift/Protocols/Utilities/TProtocolUtil.cs b/lib/netstd/Thrift/Protocol/Utilities/TProtocolUtil.cs similarity index 95% rename from lib/netcore/Thrift/Protocols/Utilities/TProtocolUtil.cs rename to lib/netstd/Thrift/Protocol/Utilities/TProtocolUtil.cs index 50b038566e1..832e46e6c09 100644 --- a/lib/netcore/Thrift/Protocols/Utilities/TProtocolUtil.cs +++ b/lib/netstd/Thrift/Protocol/Utilities/TProtocolUtil.cs @@ -17,19 +17,16 @@ using System.Threading; using System.Threading.Tasks; -using Thrift.Protocols.Entities; +using Thrift.Protocol.Entities; -namespace Thrift.Protocols.Utilities +namespace Thrift.Protocol.Utilities { // ReSharper disable once InconsistentNaming public static class TProtocolUtil { public static async Task SkipAsync(TProtocol protocol, TType type, CancellationToken cancellationToken) { - if (cancellationToken.IsCancellationRequested) - { - await Task.FromCanceled(cancellationToken); - } + cancellationToken.ThrowIfCancellationRequested(); protocol.IncrementRecursionDepth(); try diff --git a/lib/netcore/Thrift/Server/TBaseServer.cs b/lib/netstd/Thrift/Server/TServer.cs similarity index 62% rename from lib/netcore/Thrift/Server/TBaseServer.cs rename to lib/netstd/Thrift/Server/TServer.cs index 741dd5c95ad..3fd0bc5312a 100644 --- a/lib/netcore/Thrift/Server/TBaseServer.cs +++ b/lib/netstd/Thrift/Server/TServer.cs @@ -19,36 +19,37 @@ using System.Threading; using System.Threading.Tasks; using Microsoft.Extensions.Logging; -using Thrift.Protocols; -using Thrift.Transports; +using Thrift.Protocol; +using Thrift.Transport; +using Thrift.Processor; namespace Thrift.Server { // ReSharper disable once InconsistentNaming - public abstract class TBaseServer + public abstract class TServer { protected readonly ILogger Logger; - protected ITProtocolFactory InputProtocolFactory; + protected TProtocolFactory InputProtocolFactory; protected TTransportFactory InputTransportFactory; - protected ITProcessorFactory ItProcessorFactory; - protected ITProtocolFactory OutputProtocolFactory; + protected ITProcessorFactory ProcessorFactory; + protected TProtocolFactory OutputProtocolFactory; protected TTransportFactory OutputTransportFactory; protected TServerEventHandler ServerEventHandler; protected TServerTransport ServerTransport; - protected TBaseServer(ITProcessorFactory itProcessorFactory, TServerTransport serverTransport, + protected TServer(ITProcessorFactory processorFactory, TServerTransport serverTransport, TTransportFactory inputTransportFactory, TTransportFactory outputTransportFactory, - ITProtocolFactory inputProtocolFactory, ITProtocolFactory outputProtocolFactory, - ILogger logger) + TProtocolFactory inputProtocolFactory, TProtocolFactory outputProtocolFactory, + ILogger logger = null) { - ItProcessorFactory = itProcessorFactory ?? throw new ArgumentNullException(nameof(itProcessorFactory)); + ProcessorFactory = processorFactory ?? throw new ArgumentNullException(nameof(processorFactory)); ServerTransport = serverTransport; - InputTransportFactory = inputTransportFactory ?? throw new ArgumentNullException(nameof(inputTransportFactory)); - OutputTransportFactory = outputTransportFactory ?? throw new ArgumentNullException(nameof(outputTransportFactory)); + InputTransportFactory = inputTransportFactory ?? new TTransportFactory(); + OutputTransportFactory = outputTransportFactory ?? new TTransportFactory(); InputProtocolFactory = inputProtocolFactory ?? throw new ArgumentNullException(nameof(inputProtocolFactory)); OutputProtocolFactory = outputProtocolFactory ?? throw new ArgumentNullException(nameof(outputProtocolFactory)); - Logger = logger ?? throw new ArgumentNullException(nameof(logger)); + Logger = logger; // null is absolutely legal } public void SetEventHandler(TServerEventHandler seh) @@ -61,6 +62,13 @@ public TServerEventHandler GetEventHandler() return ServerEventHandler; } + // Log delegation? deprecated, use ILogger + protected void LogError( string msg) + { + if (Logger != null) + Logger.LogError(msg); + } + public abstract void Stop(); public virtual void Start() @@ -68,12 +76,10 @@ public virtual void Start() // do nothing } - public virtual async Task ServeAsync(CancellationToken cancellationToken) + public virtual Task ServeAsync(CancellationToken cancellationToken) { - if (cancellationToken.IsCancellationRequested) - { - await Task.FromCanceled(cancellationToken); - } + cancellationToken.ThrowIfCancellationRequested(); + return Task.CompletedTask; } } -} \ No newline at end of file +} diff --git a/lib/netcore/Thrift/Server/TServerEventHandler.cs b/lib/netstd/Thrift/Server/TServerEventHandler.cs similarity index 88% rename from lib/netcore/Thrift/Server/TServerEventHandler.cs rename to lib/netstd/Thrift/Server/TServerEventHandler.cs index 733bb4bef7c..69314efd624 100644 --- a/lib/netcore/Thrift/Server/TServerEventHandler.cs +++ b/lib/netstd/Thrift/Server/TServerEventHandler.cs @@ -1,4 +1,4 @@ -// Licensed to the Apache Software Foundation(ASF) under one +// Licensed to the Apache Software Foundation(ASF) under one // or more contributor license agreements.See the NOTICE file // distributed with this work for additional information // regarding copyright ownership.The ASF licenses this file @@ -17,8 +17,8 @@ using System.Threading; using System.Threading.Tasks; -using Thrift.Protocols; -using Thrift.Transports; +using Thrift.Protocol; +using Thrift.Transport; namespace Thrift.Server { @@ -49,6 +49,6 @@ Task DeleteContextAsync(object serverContext, TProtocol input, TProtocol output, /// /// Called when a client is about to call the processor */ /// - Task ProcessContextAsync(object serverContext, TClientTransport transport, CancellationToken cancellationToken); + Task ProcessContextAsync(object serverContext, TTransport transport, CancellationToken cancellationToken); } -} \ No newline at end of file +} diff --git a/lib/netstd/Thrift/Server/TSimpleAsyncServer.cs b/lib/netstd/Thrift/Server/TSimpleAsyncServer.cs new file mode 100644 index 00000000000..45e55139c44 --- /dev/null +++ b/lib/netstd/Thrift/Server/TSimpleAsyncServer.cs @@ -0,0 +1,231 @@ +// Licensed to the Apache Software Foundation(ASF) under one +// or more contributor license agreements.See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership.The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +using System; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.Extensions.Logging; +using Thrift.Protocol; +using Thrift.Processor; +using Thrift.Transport; + +namespace Thrift.Server +{ + //TODO: unhandled exceptions, etc. + + // ReSharper disable once InconsistentNaming + public class TSimpleAsyncServer : TServer + { + private readonly int _clientWaitingDelay; + private volatile Task _serverTask; + + public TSimpleAsyncServer(ITProcessorFactory itProcessorFactory, + TServerTransport serverTransport, + TTransportFactory inputTransportFactory, + TTransportFactory outputTransportFactory, + TProtocolFactory inputProtocolFactory, + TProtocolFactory outputProtocolFactory, + ILogger logger, + int clientWaitingDelay = 10) + : base(itProcessorFactory, + serverTransport, + inputTransportFactory, + outputTransportFactory, + inputProtocolFactory, + outputProtocolFactory, + logger) + { + _clientWaitingDelay = clientWaitingDelay; + } + + public TSimpleAsyncServer(ITProcessorFactory itProcessorFactory, + TServerTransport serverTransport, + TTransportFactory inputTransportFactory, + TTransportFactory outputTransportFactory, + TProtocolFactory inputProtocolFactory, + TProtocolFactory outputProtocolFactory, + ILoggerFactory loggerFactory, + int clientWaitingDelay = 10) + : this(itProcessorFactory, + serverTransport, + inputTransportFactory, + outputTransportFactory, + inputProtocolFactory, + outputProtocolFactory, + loggerFactory.CreateLogger(), + clientWaitingDelay) + { + } + + public TSimpleAsyncServer(ITAsyncProcessor processor, + TServerTransport serverTransport, + TProtocolFactory inputProtocolFactory, + TProtocolFactory outputProtocolFactory, + ILoggerFactory loggerFactory, + int clientWaitingDelay = 10) + : this(new TSingletonProcessorFactory(processor), + serverTransport, + null, // defaults to TTransportFactory() + null, // defaults to TTransportFactory() + inputProtocolFactory, + outputProtocolFactory, + loggerFactory.CreateLogger(nameof(TSimpleAsyncServer)), + clientWaitingDelay) + { + } + + public override async Task ServeAsync(CancellationToken cancellationToken) + { + try + { + // cancelation token + _serverTask = Task.Factory.StartNew(() => StartListening(cancellationToken), TaskCreationOptions.LongRunning); + await _serverTask; + } + catch (Exception ex) + { + Logger.LogError(ex.ToString()); + } + } + + private async Task StartListening(CancellationToken cancellationToken) + { + ServerTransport.Listen(); + + Logger.LogTrace("Started listening at server"); + + if (ServerEventHandler != null) + { + await ServerEventHandler.PreServeAsync(cancellationToken); + } + + while (!cancellationToken.IsCancellationRequested) + { + if (ServerTransport.IsClientPending()) + { + Logger.LogTrace("Waiting for client connection"); + + try + { + var client = await ServerTransport.AcceptAsync(cancellationToken); + await Task.Factory.StartNew(() => Execute(client, cancellationToken), cancellationToken); + } + catch (TTransportException ttx) + { + Logger.LogTrace($"Transport exception: {ttx}"); + + if (ttx.Type != TTransportException.ExceptionType.Interrupted) + { + Logger.LogError(ttx.ToString()); + } + } + } + else + { + try + { + await Task.Delay(TimeSpan.FromMilliseconds(_clientWaitingDelay), cancellationToken); + } + catch (TaskCanceledException) { } + } + } + + ServerTransport.Close(); + + Logger.LogTrace("Completed listening at server"); + } + + public override void Stop() + { + } + + private async Task Execute(TTransport client, CancellationToken cancellationToken) + { + Logger.LogTrace("Started client request processing"); + + var processor = ProcessorFactory.GetAsyncProcessor(client, this); + + TTransport inputTransport = null; + TTransport outputTransport = null; + TProtocol inputProtocol = null; + TProtocol outputProtocol = null; + object connectionContext = null; + + try + { + try + { + inputTransport = InputTransportFactory.GetTransport(client); + outputTransport = OutputTransportFactory.GetTransport(client); + inputProtocol = InputProtocolFactory.GetProtocol(inputTransport); + outputProtocol = OutputProtocolFactory.GetProtocol(outputTransport); + + if (ServerEventHandler != null) + { + connectionContext = await ServerEventHandler.CreateContextAsync(inputProtocol, outputProtocol, cancellationToken); + } + + while (!cancellationToken.IsCancellationRequested) + { + if (!await inputTransport.PeekAsync(cancellationToken)) + { + break; + } + + if (ServerEventHandler != null) + { + await ServerEventHandler.ProcessContextAsync(connectionContext, inputTransport, cancellationToken); + } + + if (!await processor.ProcessAsync(inputProtocol, outputProtocol, cancellationToken)) + { + break; + } + } + } + catch (TTransportException ttx) + { + Logger.LogTrace($"Transport exception: {ttx}"); + } + catch (Exception x) + { + Logger.LogError($"Error: {x}"); + } + + if (ServerEventHandler != null) + { + await ServerEventHandler.DeleteContextAsync(connectionContext, inputProtocol, outputProtocol, cancellationToken); + } + + } + finally + { + //Close transports + inputTransport?.Close(); + outputTransport?.Close(); + + // disposable stuff should be disposed + inputProtocol?.Dispose(); + outputProtocol?.Dispose(); + inputTransport?.Dispose(); + outputTransport?.Dispose(); + } + + Logger.LogTrace("Completed client request processing"); + } + } +} diff --git a/lib/csharp/src/Server/TThreadPoolServer.cs b/lib/netstd/Thrift/Server/TThreadPoolAsyncServer.cs similarity index 66% rename from lib/csharp/src/Server/TThreadPoolServer.cs rename to lib/netstd/Thrift/Server/TThreadPoolAsyncServer.cs index a494ce7bddd..20e659d3aa2 100644 --- a/lib/csharp/src/Server/TThreadPoolServer.cs +++ b/lib/netstd/Thrift/Server/TThreadPoolAsyncServer.cs @@ -25,18 +25,23 @@ using System.Threading; using Thrift.Protocol; using Thrift.Transport; +using Thrift.Processor; +using System.Threading.Tasks; +using Microsoft.Extensions.Logging; namespace Thrift.Server { /// /// Server that uses C# built-in ThreadPool to spawn threads when handling requests. /// - public class TThreadPoolServer : TServer + public class TThreadPoolAsyncServer : TServer { private const int DEFAULT_MIN_THREADS = -1; // use .NET ThreadPool defaults private const int DEFAULT_MAX_THREADS = -1; // use .NET ThreadPool defaults private volatile bool stop = false; + private CancellationToken ServerCancellationToken; + public struct Configuration { public int MinWorkerThreads; @@ -61,70 +66,62 @@ public Configuration(int minWork, int maxWork, int minIO, int maxIO) } } - public TThreadPoolServer(TProcessor processor, TServerTransport serverTransport) + public TThreadPoolAsyncServer(ITAsyncProcessor processor, TServerTransport serverTransport, ILogger logger = null) : this(new TSingletonProcessorFactory(processor), serverTransport, - new TTransportFactory(), new TTransportFactory(), + null, null, // defaults to TTransportFactory() new TBinaryProtocol.Factory(), new TBinaryProtocol.Factory(), - new Configuration(), DefaultLogDelegate) + new Configuration(), logger) { } - public TThreadPoolServer(TProcessor processor, TServerTransport serverTransport, LogDelegate logDelegate) - : this(new TSingletonProcessorFactory(processor), serverTransport, - new TTransportFactory(), new TTransportFactory(), - new TBinaryProtocol.Factory(), new TBinaryProtocol.Factory(), - new Configuration(), logDelegate) - { - } - - public TThreadPoolServer(TProcessor processor, + public TThreadPoolAsyncServer(ITAsyncProcessor processor, TServerTransport serverTransport, TTransportFactory transportFactory, TProtocolFactory protocolFactory) : this(new TSingletonProcessorFactory(processor), serverTransport, transportFactory, transportFactory, protocolFactory, protocolFactory, - new Configuration(), DefaultLogDelegate) + new Configuration()) { } - public TThreadPoolServer(TProcessorFactory processorFactory, + public TThreadPoolAsyncServer(ITProcessorFactory processorFactory, TServerTransport serverTransport, TTransportFactory transportFactory, TProtocolFactory protocolFactory) : this(processorFactory, serverTransport, transportFactory, transportFactory, protocolFactory, protocolFactory, - new Configuration(), DefaultLogDelegate) + new Configuration()) { } - public TThreadPoolServer(TProcessorFactory processorFactory, + public TThreadPoolAsyncServer(ITProcessorFactory processorFactory, TServerTransport serverTransport, TTransportFactory inputTransportFactory, TTransportFactory outputTransportFactory, TProtocolFactory inputProtocolFactory, TProtocolFactory outputProtocolFactory, - int minThreadPoolThreads, int maxThreadPoolThreads, LogDelegate logDel) + int minThreadPoolThreads, int maxThreadPoolThreads, ILogger logger= null) : this(processorFactory, serverTransport, inputTransportFactory, outputTransportFactory, inputProtocolFactory, outputProtocolFactory, new Configuration(minThreadPoolThreads, maxThreadPoolThreads), - logDel) + logger) { } - public TThreadPoolServer(TProcessorFactory processorFactory, + public TThreadPoolAsyncServer(ITProcessorFactory processorFactory, TServerTransport serverTransport, TTransportFactory inputTransportFactory, TTransportFactory outputTransportFactory, TProtocolFactory inputProtocolFactory, TProtocolFactory outputProtocolFactory, Configuration threadConfig, - LogDelegate logDel) + ILogger logger = null) : base(processorFactory, serverTransport, inputTransportFactory, outputTransportFactory, - inputProtocolFactory, outputProtocolFactory, logDel) + inputProtocolFactory, outputProtocolFactory, logger) { - lock (typeof(TThreadPoolServer)) + lock (typeof(TThreadPoolAsyncServer)) { if ((threadConfig.MaxWorkerThreads > 0) || (threadConfig.MaxIOThreads > 0)) { @@ -156,52 +153,61 @@ public TThreadPoolServer(TProcessorFactory processorFactory, /// /// Use new ThreadPool thread for each new client connection. /// - public override void Serve() + public override async Task ServeAsync(CancellationToken cancellationToken) { + ServerCancellationToken = cancellationToken; try { - serverTransport.Listen(); - } - catch (TTransportException ttx) - { - logDelegate("Error, could not listen on ServerTransport: " + ttx); - return; - } - - //Fire the preServe server event when server is up but before any client connections - if (serverEventHandler != null) - serverEventHandler.preServe(); - - while (!stop) - { - int failureCount = 0; try { - TTransport client = serverTransport.Accept(); - ThreadPool.QueueUserWorkItem(this.Execute, client); + ServerTransport.Listen(); } catch (TTransportException ttx) { - if (!stop || ttx.Type != TTransportException.ExceptionType.Interrupted) + LogError("Error, could not listen on ServerTransport: " + ttx); + return; + } + + //Fire the preServe server event when server is up but before any client connections + if (ServerEventHandler != null) + await ServerEventHandler.PreServeAsync(cancellationToken); + + while (!stop) + { + int failureCount = 0; + try { - ++failureCount; - logDelegate(ttx.ToString()); + TTransport client = await ServerTransport.AcceptAsync(cancellationToken); + ThreadPool.QueueUserWorkItem(this.Execute, client); } + catch (TTransportException ttx) + { + if (!stop || ttx.Type != TTransportException.ExceptionType.Interrupted) + { + ++failureCount; + LogError(ttx.ToString()); + } + } } - } - if (stop) - { - try - { - serverTransport.Close(); - } - catch (TTransportException ttx) + if (stop) { - logDelegate("TServerTransport failed on close: " + ttx.Message); + try + { + ServerTransport.Close(); + } + catch (TTransportException ttx) + { + LogError("TServerTransport failed on close: " + ttx.Message); + } + stop = false; } - stop = false; + + } + finally + { + ServerCancellationToken = default(CancellationToken); } } @@ -212,9 +218,11 @@ public override void Serve() /// private void Execute(object threadContext) { + var cancellationToken = ServerCancellationToken; + using (TTransport client = (TTransport)threadContext) { - TProcessor processor = processorFactory.GetProcessor(client, this); + ITAsyncProcessor processor = ProcessorFactory.GetAsyncProcessor(client, this); TTransport inputTransport = null; TTransport outputTransport = null; TProtocol inputProtocol = null; @@ -224,29 +232,29 @@ private void Execute(object threadContext) { try { - inputTransport = inputTransportFactory.GetTransport(client); - outputTransport = outputTransportFactory.GetTransport(client); - inputProtocol = inputProtocolFactory.GetProtocol(inputTransport); - outputProtocol = outputProtocolFactory.GetProtocol(outputTransport); + inputTransport = InputTransportFactory.GetTransport(client); + outputTransport = OutputTransportFactory.GetTransport(client); + inputProtocol = InputProtocolFactory.GetProtocol(inputTransport); + outputProtocol = OutputProtocolFactory.GetProtocol(outputTransport); //Recover event handler (if any) and fire createContext server event when a client connects - if (serverEventHandler != null) - connectionContext = serverEventHandler.createContext(inputProtocol, outputProtocol); + if (ServerEventHandler != null) + connectionContext = ServerEventHandler.CreateContextAsync(inputProtocol, outputProtocol, cancellationToken).Result; //Process client requests until client disconnects while (!stop) { - if (!inputTransport.Peek()) + if (! inputTransport.PeekAsync(cancellationToken).Result) break; //Fire processContext server event //N.B. This is the pattern implemented in C++ and the event fires provisionally. //That is to say it may be many minutes between the event firing and the client request //actually arriving or the client may hang up without ever makeing a request. - if (serverEventHandler != null) - serverEventHandler.processContext(connectionContext, inputTransport); + if (ServerEventHandler != null) + ServerEventHandler.ProcessContextAsync(connectionContext, inputTransport, cancellationToken).Wait(); //Process client request (blocks until transport is readable) - if (!processor.Process(inputProtocol, outputProtocol)) + if (!processor.ProcessAsync(inputProtocol, outputProtocol, cancellationToken).Result) break; } } @@ -257,31 +265,25 @@ private void Execute(object threadContext) catch (Exception x) { //Unexpected - logDelegate("Error: " + x); + LogError("Error: " + x); } //Fire deleteContext server event after client disconnects - if (serverEventHandler != null) - serverEventHandler.deleteContext(connectionContext, inputProtocol, outputProtocol); + if (ServerEventHandler != null) + ServerEventHandler.DeleteContextAsync(connectionContext, inputProtocol, outputProtocol, cancellationToken).Wait(); } finally { //Close transports - if (inputTransport != null) - inputTransport.Close(); - if (outputTransport != null) - outputTransport.Close(); + inputTransport?.Close(); + outputTransport?.Close(); // disposable stuff should be disposed - if (inputProtocol != null) - inputProtocol.Dispose(); - if (outputProtocol != null) - outputProtocol.Dispose(); - if (inputTransport != null) - inputTransport.Dispose(); - if (outputTransport != null) - outputTransport.Dispose(); + inputProtocol?.Dispose(); + outputProtocol?.Dispose(); + inputTransport?.Dispose(); + outputTransport?.Dispose(); } } } @@ -289,7 +291,7 @@ private void Execute(object threadContext) public override void Stop() { stop = true; - serverTransport.Close(); + ServerTransport?.Close(); } } } diff --git a/lib/netcore/Thrift/TApplicationException.cs b/lib/netstd/Thrift/TApplicationException.cs similarity index 90% rename from lib/netcore/Thrift/TApplicationException.cs rename to lib/netstd/Thrift/TApplicationException.cs index 9ec145a8529..514db9a086b 100644 --- a/lib/netcore/Thrift/TApplicationException.cs +++ b/lib/netstd/Thrift/TApplicationException.cs @@ -1,4 +1,4 @@ -// Licensed to the Apache Software Foundation(ASF) under one +// Licensed to the Apache Software Foundation(ASF) under one // or more contributor license agreements.See the NOTICE file // distributed with this work for additional information // regarding copyright ownership.The ASF licenses this file @@ -17,9 +17,9 @@ using System.Threading; using System.Threading.Tasks; -using Thrift.Protocols; -using Thrift.Protocols.Entities; -using Thrift.Protocols.Utilities; +using Thrift.Protocol; +using Thrift.Protocol.Entities; +using Thrift.Protocol.Utilities; namespace Thrift { @@ -44,7 +44,7 @@ public enum ExceptionType private const int MessageTypeFieldId = 1; private const int ExTypeFieldId = 2; - protected ExceptionType Type; + public ExceptionType Type { get; private set; } public TApplicationException() { @@ -56,12 +56,12 @@ public TApplicationException(ExceptionType type) } public TApplicationException(ExceptionType type, string message) - : base(message) + : base(message, null) // TApplicationException is serializable, but we never serialize InnerException { Type = type; } - public static async Task ReadAsync(TProtocol inputProtocol, CancellationToken cancellationToken) + public static async ValueTask ReadAsync(TProtocol inputProtocol, CancellationToken cancellationToken) { string message = null; var type = ExceptionType.Unknown; @@ -112,10 +112,7 @@ public static async Task ReadAsync(TProtocol inputProtoco public async Task WriteAsync(TProtocol outputProtocol, CancellationToken cancellationToken) { - if (cancellationToken.IsCancellationRequested) - { - await Task.FromCanceled(cancellationToken); - } + cancellationToken.ThrowIfCancellationRequested(); const string messageTypeFieldName = "message"; const string exTypeFieldName = "exType"; @@ -147,4 +144,4 @@ public async Task WriteAsync(TProtocol outputProtocol, CancellationToken cancell await outputProtocol.WriteStructEndAsync(cancellationToken); } } -} \ No newline at end of file +} diff --git a/lib/netcore/Thrift/TBaseClient.cs b/lib/netstd/Thrift/TBaseClient.cs similarity index 97% rename from lib/netcore/Thrift/TBaseClient.cs rename to lib/netstd/Thrift/TBaseClient.cs index e0192515347..aefb54dd5a1 100644 --- a/lib/netcore/Thrift/TBaseClient.cs +++ b/lib/netstd/Thrift/TBaseClient.cs @@ -18,7 +18,7 @@ using System; using System.Threading; using System.Threading.Tasks; -using Thrift.Protocols; +using Thrift.Protocol; namespace Thrift { @@ -63,7 +63,7 @@ public virtual async Task OpenTransportAsync(CancellationToken cancellationToken await _inputProtocol.Transport.OpenAsync(cancellationToken); } - if (!_inputProtocol.Transport.IsOpen) + if (!_outputProtocol.Transport.IsOpen) { await _outputProtocol.Transport.OpenAsync(cancellationToken); } diff --git a/lib/netstd/Thrift/TConfiguration.cs b/lib/netstd/Thrift/TConfiguration.cs new file mode 100644 index 00000000000..c8dde10434f --- /dev/null +++ b/lib/netstd/Thrift/TConfiguration.cs @@ -0,0 +1,19 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace Thrift +{ + public class TConfiguration + { + public const int DEFAULT_MAX_MESSAGE_SIZE = 100 * 1024 * 1024; + public const int DEFAULT_MAX_FRAME_SIZE = 16384000; // this value is used consistently across all Thrift libraries + public const int DEFAULT_RECURSION_DEPTH = 64; + + public int MaxMessageSize { get; set; } = DEFAULT_MAX_MESSAGE_SIZE; + public int MaxFrameSize { get; set; } = DEFAULT_MAX_FRAME_SIZE; + public int RecursionLimit { get; set; } = DEFAULT_RECURSION_DEPTH; + + // TODO(JensG): add connection and i/o timeouts + } +} diff --git a/lib/netcore/Thrift/TException.cs b/lib/netstd/Thrift/TException.cs similarity index 91% rename from lib/netcore/Thrift/TException.cs rename to lib/netstd/Thrift/TException.cs index 6aa588d7f34..43e70549bea 100644 --- a/lib/netcore/Thrift/TException.cs +++ b/lib/netstd/Thrift/TException.cs @@ -26,8 +26,8 @@ public TException() { } - public TException(string message) - : base(message) + public TException(string message, Exception inner) + : base(message, inner) { } } diff --git a/lib/netstd/Thrift/Thrift.csproj b/lib/netstd/Thrift/Thrift.csproj new file mode 100644 index 00000000000..3278d98f268 --- /dev/null +++ b/lib/netstd/Thrift/Thrift.csproj @@ -0,0 +1,64 @@ + + + + + netstandard2.1;netstandard2.0 + Thrift + Thrift + true + true + false + false + false + false + false + false + false + false + + + + true + true + thrift.snk + false + 0.14.0.0 + + + + + + + + + + + + + + + + + + + $(IntermediateOutputPath)$(TargetFrameworkMoniker).AssemblyAttributes$(DefaultLanguageSourceExtension) + + + diff --git a/lib/netstd/Thrift/Transport/Client/THttpTransport.cs b/lib/netstd/Thrift/Transport/Client/THttpTransport.cs new file mode 100644 index 00000000000..dcd028ccad1 --- /dev/null +++ b/lib/netstd/Thrift/Transport/Client/THttpTransport.cs @@ -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. + +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Net.Http; +using System.Net.Http.Headers; +using System.Security.Cryptography.X509Certificates; +using System.Threading; +using System.Threading.Tasks; + +namespace Thrift.Transport.Client +{ + // ReSharper disable once InconsistentNaming + public class THttpTransport : TEndpointTransport + { + private readonly X509Certificate[] _certificates; + private readonly Uri _uri; + + private int _connectTimeout = 30000; // Timeouts in milliseconds + private HttpClient _httpClient; + private Stream _inputStream; + private MemoryStream _outputStream = new MemoryStream(); + private bool _isDisposed; + + public THttpTransport(Uri uri, TConfiguration config, IDictionary customRequestHeaders = null, string userAgent = null) + : this(uri, config, Enumerable.Empty(), customRequestHeaders, userAgent) + { + } + + public THttpTransport(Uri uri, TConfiguration config, IEnumerable certificates, + IDictionary customRequestHeaders, string userAgent = null) + : base(config) + { + _uri = uri; + _certificates = (certificates ?? Enumerable.Empty()).ToArray(); + + if (!string.IsNullOrEmpty(userAgent)) + UserAgent = userAgent; + + // due to current bug with performance of Dispose in netcore https://github.com/dotnet/corefx/issues/8809 + // this can be switched to default way (create client->use->dispose per flush) later + _httpClient = CreateClient(customRequestHeaders); + ConfigureClient(_httpClient); + } + + /// + /// Constructor that takes a HttpClient instance to support using IHttpClientFactory. + /// + /// As the HttpMessageHandler of the client must be configured at the time of creation, it + /// is assumed that the consumer has already added any certificates and configured decompression methods. The + /// consumer can use the CreateHttpClientHandler method to get a handler with these set. + /// Client configured with the desired message handler, user agent, and URI if not + /// specified in the uri parameter. A default user agent will be used if not set. + /// Thrift configuration object + /// Optional URI to use for requests, if not specified the base address of httpClient + /// is used. + public THttpTransport(HttpClient httpClient, TConfiguration config, Uri uri = null) + : base(config) + { + _httpClient = httpClient; + + _uri = uri ?? httpClient.BaseAddress; + httpClient.BaseAddress = _uri; + + var userAgent = _httpClient.DefaultRequestHeaders.UserAgent.ToString(); + if (!string.IsNullOrEmpty(userAgent)) + UserAgent = userAgent; + + ConfigureClient(_httpClient); + } + + // According to RFC 2616 section 3.8, the "User-Agent" header may not carry a version number + public readonly string UserAgent = "Thrift netstd THttpClient"; + + public override bool IsOpen => true; + + public HttpRequestHeaders RequestHeaders => _httpClient.DefaultRequestHeaders; + + public MediaTypeHeaderValue ContentType { get; set; } + + public override Task OpenAsync(CancellationToken cancellationToken) + { + cancellationToken.ThrowIfCancellationRequested(); + return Task.CompletedTask; + } + + public override void Close() + { + if (_inputStream != null) + { + _inputStream.Dispose(); + _inputStream = null; + } + + if (_outputStream != null) + { + _outputStream.Dispose(); + _outputStream = null; + } + + if (_httpClient != null) + { + _httpClient.Dispose(); + _httpClient = null; + } + } + + public override async ValueTask ReadAsync(byte[] buffer, int offset, int length, CancellationToken cancellationToken) + { + cancellationToken.ThrowIfCancellationRequested(); + + if (_inputStream == null) + throw new TTransportException(TTransportException.ExceptionType.NotOpen, "No request has been sent"); + + CheckReadBytesAvailable(length); + + try + { +#if NETSTANDARD2_1 + var ret = await _inputStream.ReadAsync(new Memory(buffer, offset, length), cancellationToken); +#else + var ret = await _inputStream.ReadAsync(buffer, offset, length, cancellationToken); +#endif + if (ret == -1) + { + throw new TTransportException(TTransportException.ExceptionType.EndOfFile, "No more data available"); + } + + CountConsumedMessageBytes(ret); + return ret; + } + catch (IOException iox) + { + throw new TTransportException(TTransportException.ExceptionType.Unknown, iox.ToString()); + } + } + + public override async Task WriteAsync(byte[] buffer, int offset, int length, CancellationToken cancellationToken) + { + cancellationToken.ThrowIfCancellationRequested(); + + await _outputStream.WriteAsync(buffer, offset, length, cancellationToken); + } + + /// + /// Get a client handler configured with recommended properties to use with the HttpClient constructor + /// and an IHttpClientFactory. + /// + /// An optional array of client certificates to associate with the handler. + /// + /// A client handler with deflate and gZip compression-decompression algorithms and any client + /// certificates passed in via certificates. + /// + public virtual HttpClientHandler CreateHttpClientHandler(X509Certificate[] certificates = null) + { + var handler = new HttpClientHandler(); + if (certificates != null) + handler.ClientCertificates.AddRange(certificates); + handler.AutomaticDecompression = System.Net.DecompressionMethods.Deflate | System.Net.DecompressionMethods.GZip; + return handler; + } + + private HttpClient CreateClient(IDictionary customRequestHeaders) + { + var handler = CreateHttpClientHandler(_certificates); + var httpClient = new HttpClient(handler); + + + if (customRequestHeaders != null) + { + foreach (var item in customRequestHeaders) + { + httpClient.DefaultRequestHeaders.Add(item.Key, item.Value); + } + } + + return httpClient; + } + + private void ConfigureClient(HttpClient httpClient) + { + if (_connectTimeout > 0) + { + httpClient.Timeout = TimeSpan.FromMilliseconds(_connectTimeout); + } + + httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/x-thrift")); + + // Clear any user agent values to avoid drift with the field value + httpClient.DefaultRequestHeaders.UserAgent.Clear(); + httpClient.DefaultRequestHeaders.UserAgent.TryParseAdd(UserAgent); + + httpClient.DefaultRequestHeaders.AcceptEncoding.Add(new StringWithQualityHeaderValue("deflate")); + httpClient.DefaultRequestHeaders.AcceptEncoding.Add(new StringWithQualityHeaderValue("gzip")); + } + + public override async Task FlushAsync(CancellationToken cancellationToken) + { + try + { + _outputStream.Seek(0, SeekOrigin.Begin); + + using (var contentStream = new StreamContent(_outputStream)) + { + contentStream.Headers.ContentType = ContentType ?? new MediaTypeHeaderValue(@"application/x-thrift"); + + var response = (await _httpClient.PostAsync(_uri, contentStream, cancellationToken)).EnsureSuccessStatusCode(); + + _inputStream?.Dispose(); + _inputStream = await response.Content.ReadAsStreamAsync(); + if (_inputStream.CanSeek) + { + _inputStream.Seek(0, SeekOrigin.Begin); + } + } + } + catch (IOException iox) + { + throw new TTransportException(TTransportException.ExceptionType.Unknown, iox.ToString()); + } + catch (HttpRequestException wx) + { + throw new TTransportException(TTransportException.ExceptionType.Unknown, + "Couldn't connect to server: " + wx); + } + catch (Exception ex) + { + throw new TTransportException(TTransportException.ExceptionType.Unknown, ex.Message); + } + finally + { + _outputStream = new MemoryStream(); + ResetConsumedMessageSize(); + } + } + + + // IDisposable + protected override void Dispose(bool disposing) + { + if (!_isDisposed) + { + if (disposing) + { + _inputStream?.Dispose(); + _outputStream?.Dispose(); + _httpClient?.Dispose(); + } + } + _isDisposed = true; + } + } +} diff --git a/lib/netstd/Thrift/Transport/Client/TMemoryBufferTransport.cs b/lib/netstd/Thrift/Transport/Client/TMemoryBufferTransport.cs new file mode 100644 index 00000000000..797de4e576a --- /dev/null +++ b/lib/netstd/Thrift/Transport/Client/TMemoryBufferTransport.cs @@ -0,0 +1,177 @@ +// Licensed to the Apache Software Foundation(ASF) under one +// or more contributor license agreements.See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership.The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +using System; +using System.Diagnostics; +using System.IO; +using System.Threading; +using System.Threading.Tasks; + +namespace Thrift.Transport.Client +{ + // ReSharper disable once InconsistentNaming + public class TMemoryBufferTransport : TEndpointTransport + { + private bool IsDisposed; + private byte[] Bytes; + private int _bytesUsed; + + public TMemoryBufferTransport(TConfiguration config, int initialCapacity = 2048) + : base(config) + { + Bytes = new byte[initialCapacity]; + } + + public TMemoryBufferTransport(byte[] buf, TConfiguration config) + :base(config) + { + Bytes = (byte[])buf.Clone(); + _bytesUsed = Bytes.Length; + UpdateKnownMessageSize(_bytesUsed); + } + + public int Position { get; set; } + + public int Capacity + { + get + { + Debug.Assert(_bytesUsed <= Bytes.Length); + return Bytes.Length; + } + set + { + Array.Resize(ref Bytes, value); + _bytesUsed = value; + } + } + + public int Length + { + get { + Debug.Assert(_bytesUsed <= Bytes.Length); + return _bytesUsed; + } + set { + if ((Bytes.Length < value) || (Bytes.Length > (10 * value))) + Array.Resize(ref Bytes, Math.Max(2048, (int)(value * 1.25))); + _bytesUsed = value; + } + } + + public void SetLength(int value) + { + Length = value; + Position = Math.Min(Position, value); + } + + public override bool IsOpen => true; + + public override Task OpenAsync(CancellationToken cancellationToken) + { + cancellationToken.ThrowIfCancellationRequested(); + return Task.CompletedTask; + } + + public override void Close() + { + /** do nothing **/ + } + + public void Seek(int delta, SeekOrigin origin) + { + int newPos; + switch (origin) + { + case SeekOrigin.Begin: + newPos = delta; + break; + case SeekOrigin.Current: + newPos = Position + delta; + break; + case SeekOrigin.End: + newPos = _bytesUsed + delta; + break; + default: + throw new ArgumentException(nameof(origin)); + } + + if ((0 > newPos) || (newPos > _bytesUsed)) + throw new ArgumentException(nameof(origin)); + Position = newPos; + + ResetConsumedMessageSize(); + CountConsumedMessageBytes(Position); + } + + public override ValueTask ReadAsync(byte[] buffer, int offset, int length, CancellationToken cancellationToken) + { + var count = Math.Min(Length - Position, length); + Buffer.BlockCopy(Bytes, Position, buffer, offset, count); + Position += count; + CountConsumedMessageBytes(count); + return new ValueTask(count); + } + + public override Task WriteAsync(byte[] buffer, CancellationToken cancellationToken) + { + return WriteAsync(buffer, 0, buffer.Length, cancellationToken); + } + + public override Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) + { + var free = Length - Position; + Length = Length + count - free; + Buffer.BlockCopy(buffer, offset, Bytes, Position, count); + Position += count; + return Task.CompletedTask; + } + + public override Task FlushAsync(CancellationToken cancellationToken) + { + cancellationToken.ThrowIfCancellationRequested(); + ResetConsumedMessageSize(); + return Task.CompletedTask; + } + + public byte[] GetBuffer() + { + var retval = new byte[Length]; + Buffer.BlockCopy(Bytes, 0, retval, 0, Length); + return retval; + } + + internal bool TryGetBuffer(out ArraySegment bufSegment) + { + bufSegment = new ArraySegment(Bytes, 0, _bytesUsed); + return true; + } + + // IDisposable + protected override void Dispose(bool disposing) + { + if (!IsDisposed) + { + if (disposing) + { + // nothing to do + } + } + IsDisposed = true; + } + } +} diff --git a/lib/netstd/Thrift/Transport/Client/TNamedPipeTransport.cs b/lib/netstd/Thrift/Transport/Client/TNamedPipeTransport.cs new file mode 100644 index 00000000000..815983ea82c --- /dev/null +++ b/lib/netstd/Thrift/Transport/Client/TNamedPipeTransport.cs @@ -0,0 +1,121 @@ +// Licensed to the Apache Software Foundation(ASF) under one +// or more contributor license agreements.See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership.The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +using System; +using System.IO.Pipes; +using System.Threading; +using System.Threading.Tasks; + +namespace Thrift.Transport.Client +{ + // ReSharper disable once InconsistentNaming + public class TNamedPipeTransport : TEndpointTransport + { + private NamedPipeClientStream PipeStream; + private readonly int ConnectTimeout; + + public TNamedPipeTransport(string pipe, TConfiguration config, int timeout = Timeout.Infinite) + : this(".", pipe, config, timeout) + { + } + + public TNamedPipeTransport(string server, string pipe, TConfiguration config, int timeout = Timeout.Infinite) + : base(config) + { + var serverName = string.IsNullOrWhiteSpace(server) ? server : "."; + ConnectTimeout = (timeout > 0) ? timeout : Timeout.Infinite; + + PipeStream = new NamedPipeClientStream(serverName, pipe, PipeDirection.InOut, PipeOptions.None); + } + + public override bool IsOpen => PipeStream != null && PipeStream.IsConnected; + + public override async Task OpenAsync(CancellationToken cancellationToken) + { + if (IsOpen) + { + throw new TTransportException(TTransportException.ExceptionType.AlreadyOpen); + } + + await PipeStream.ConnectAsync( ConnectTimeout, cancellationToken); + ResetConsumedMessageSize(); + } + + public override void Close() + { + if (PipeStream != null) + { + PipeStream.Dispose(); + PipeStream = null; + } + } + + public override async ValueTask ReadAsync(byte[] buffer, int offset, int length, CancellationToken cancellationToken) + { + if (PipeStream == null) + { + throw new TTransportException(TTransportException.ExceptionType.NotOpen); + } + + CheckReadBytesAvailable(length); +#if NETSTANDARD2_1 + var numRead = await PipeStream.ReadAsync(new Memory(buffer, offset, length), cancellationToken); +#else + var numRead = await PipeStream.ReadAsync(buffer, offset, length, cancellationToken); +#endif + CountConsumedMessageBytes(numRead); + return numRead; + } + + public override async Task WriteAsync(byte[] buffer, int offset, int length, CancellationToken cancellationToken) + { + if (PipeStream == null) + { + throw new TTransportException(TTransportException.ExceptionType.NotOpen); + } + + // if necessary, send the data in chunks + // there's a system limit around 0x10000 bytes that we hit otherwise + // MSDN: "Pipe write operations across a network are limited to 65,535 bytes per write. For more information regarding pipes, see the Remarks section." + var nBytes = Math.Min(15 * 4096, length); // 16 would exceed the limit + while (nBytes > 0) + { + await PipeStream.WriteAsync(buffer, offset, nBytes, cancellationToken); + offset += nBytes; + length -= nBytes; + nBytes = Math.Min(nBytes, length); + } + } + + public override Task FlushAsync(CancellationToken cancellationToken) + { + cancellationToken.ThrowIfCancellationRequested(); + + ResetConsumedMessageSize(); + return Task.CompletedTask; + } + + + protected override void Dispose(bool disposing) + { + if(disposing) + { + PipeStream?.Dispose(); + } + } + } +} diff --git a/lib/netcore/Thrift/Transports/Client/TSocketClientTransport.cs b/lib/netstd/Thrift/Transport/Client/TSocketTransport.cs similarity index 66% rename from lib/netcore/Thrift/Transports/Client/TSocketClientTransport.cs rename to lib/netstd/Thrift/Transport/Client/TSocketTransport.cs index e769d14211e..5e3245b8c40 100644 --- a/lib/netcore/Thrift/Transports/Client/TSocketClientTransport.cs +++ b/lib/netstd/Thrift/Transport/Client/TSocketTransport.cs @@ -18,33 +18,27 @@ using System; using System.Net; using System.Net.Sockets; +using System.Text; using System.Threading; using System.Threading.Tasks; -namespace Thrift.Transports.Client +namespace Thrift.Transport.Client { // ReSharper disable once InconsistentNaming - public class TSocketClientTransport : TStreamClientTransport + public class TSocketTransport : TStreamTransport { private bool _isDisposed; - public TSocketClientTransport(TcpClient client) - { - TcpClient = client ?? throw new ArgumentNullException(nameof(client)); - - if (IsOpen) - { - InputStream = client.GetStream(); - OutputStream = client.GetStream(); - } - } - public TSocketClientTransport(IPAddress host, int port) - : this(host, port, 0) + public TSocketTransport(TcpClient client, TConfiguration config) + : base(config) { + TcpClient = client ?? throw new ArgumentNullException(nameof(client)); + SetInputOutputStream(); } - public TSocketClientTransport(IPAddress host, int port, int timeout) + public TSocketTransport(IPAddress host, int port, TConfiguration config, int timeout = 0) + : base(config) { Host = host; Port = port; @@ -52,6 +46,39 @@ public TSocketClientTransport(IPAddress host, int port, int timeout) TcpClient = new TcpClient(); TcpClient.ReceiveTimeout = TcpClient.SendTimeout = timeout; TcpClient.Client.NoDelay = true; + SetInputOutputStream(); + } + + public TSocketTransport(string host, int port, TConfiguration config, int timeout = 0) + : base(config) + { + try + { + var entry = Dns.GetHostEntry(host); + if (entry.AddressList.Length == 0) + throw new TTransportException(TTransportException.ExceptionType.Unknown, "unable to resolve host name"); + + Host = entry.AddressList[0]; + Port = port; + + TcpClient = new TcpClient(host, port); + TcpClient.ReceiveTimeout = TcpClient.SendTimeout = timeout; + TcpClient.Client.NoDelay = true; + SetInputOutputStream(); + } + catch (SocketException e) + { + throw new TTransportException(TTransportException.ExceptionType.Unknown, e.Message, e); + } + } + + private void SetInputOutputStream() + { + if (IsOpen) + { + InputStream = TcpClient.GetStream(); + OutputStream = TcpClient.GetStream(); + } } public TcpClient TcpClient { get; private set; } @@ -73,21 +100,13 @@ public override bool IsOpen { get { - if (TcpClient == null) - { - return false; - } - - return TcpClient.Connected; + return (TcpClient != null) && TcpClient.Connected; } } public override async Task OpenAsync(CancellationToken cancellationToken) { - if (cancellationToken.IsCancellationRequested) - { - await Task.FromCanceled(cancellationToken); - } + cancellationToken.ThrowIfCancellationRequested(); if (IsOpen) { @@ -105,9 +124,7 @@ public override async Task OpenAsync(CancellationToken cancellationToken) } await TcpClient.ConnectAsync(Host, Port); - - InputStream = TcpClient.GetStream(); - OutputStream = TcpClient.GetStream(); + SetInputOutputStream(); } public override void Close() @@ -136,4 +153,4 @@ protected override void Dispose(bool disposing) _isDisposed = true; } } -} \ No newline at end of file +} diff --git a/lib/netcore/Thrift/Transports/Client/TStreamClientTransport.cs b/lib/netstd/Thrift/Transport/Client/TStreamTransport.cs similarity index 71% rename from lib/netcore/Thrift/Transports/Client/TStreamClientTransport.cs rename to lib/netstd/Thrift/Transport/Client/TStreamTransport.cs index f7164f0454f..b397460d7db 100644 --- a/lib/netcore/Thrift/Transports/Client/TStreamClientTransport.cs +++ b/lib/netstd/Thrift/Transport/Client/TStreamTransport.cs @@ -14,23 +14,25 @@ // KIND, either express or implied. See the License for the // specific language governing permissions and limitations // under the License. - +using System; using System.IO; using System.Threading; using System.Threading.Tasks; -namespace Thrift.Transports.Client +namespace Thrift.Transport.Client { // ReSharper disable once InconsistentNaming - public class TStreamClientTransport : TClientTransport + public class TStreamTransport : TEndpointTransport { private bool _isDisposed; - protected TStreamClientTransport() + protected TStreamTransport(TConfiguration config) + :base(config) { } - public TStreamClientTransport(Stream inputStream, Stream outputStream) + public TStreamTransport(Stream inputStream, Stream outputStream, TConfiguration config) + : base(config) { InputStream = inputStream; OutputStream = outputStream; @@ -38,16 +40,21 @@ public TStreamClientTransport(Stream inputStream, Stream outputStream) protected Stream OutputStream { get; set; } - protected Stream InputStream { get; set; } + private Stream _InputStream = null; + protected Stream InputStream { + get => _InputStream; + set { + _InputStream = value; + ResetConsumedMessageSize(); + } + } public override bool IsOpen => true; - public override async Task OpenAsync(CancellationToken cancellationToken) + public override Task OpenAsync(CancellationToken cancellationToken) { - if (cancellationToken.IsCancellationRequested) - { - await Task.FromCanceled(cancellationToken); - } + cancellationToken.ThrowIfCancellationRequested(); + return Task.CompletedTask; } public override void Close() @@ -65,8 +72,7 @@ public override void Close() } } - public override async Task ReadAsync(byte[] buffer, int offset, int length, - CancellationToken cancellationToken) + public override async ValueTask ReadAsync(byte[] buffer, int offset, int length, CancellationToken cancellationToken) { if (InputStream == null) { @@ -74,7 +80,11 @@ public override async Task ReadAsync(byte[] buffer, int offset, int length, "Cannot read from null inputstream"); } +#if NETSTANDARD2_1 + return await InputStream.ReadAsync(new Memory(buffer, offset, length), cancellationToken); +#else return await InputStream.ReadAsync(buffer, offset, length, cancellationToken); +#endif } public override async Task WriteAsync(byte[] buffer, int offset, int length, CancellationToken cancellationToken) @@ -82,7 +92,7 @@ public override async Task WriteAsync(byte[] buffer, int offset, int length, Can if (OutputStream == null) { throw new TTransportException(TTransportException.ExceptionType.NotOpen, - "Cannot read from null inputstream"); + "Cannot write to null outputstream"); } await OutputStream.WriteAsync(buffer, offset, length, cancellationToken); @@ -91,8 +101,10 @@ public override async Task WriteAsync(byte[] buffer, int offset, int length, Can public override async Task FlushAsync(CancellationToken cancellationToken) { await OutputStream.FlushAsync(cancellationToken); + ResetConsumedMessageSize(); } + // IDisposable protected override void Dispose(bool disposing) { @@ -107,4 +119,4 @@ protected override void Dispose(bool disposing) _isDisposed = true; } } -} \ No newline at end of file +} diff --git a/lib/netcore/Thrift/Transports/Client/TTlsSocketClientTransport.cs b/lib/netstd/Thrift/Transport/Client/TTlsSocketTransport.cs similarity index 77% rename from lib/netcore/Thrift/Transports/Client/TTlsSocketClientTransport.cs rename to lib/netstd/Thrift/Transport/Client/TTlsSocketTransport.cs index c8be4ede188..e3da6f4c264 100644 --- a/lib/netcore/Thrift/Transports/Client/TTlsSocketClientTransport.cs +++ b/lib/netstd/Thrift/Transport/Client/TTlsSocketTransport.cs @@ -1,4 +1,4 @@ -// Licensed to the Apache Software Foundation(ASF) under one +// Licensed to the Apache Software Foundation(ASF) under one // or more contributor license agreements.See the NOTICE file // distributed with this work for additional information // regarding copyright ownership.The ASF licenses this file @@ -24,12 +24,12 @@ using System.Threading; using System.Threading.Tasks; -namespace Thrift.Transports.Client +namespace Thrift.Transport.Client { //TODO: check for correct work // ReSharper disable once InconsistentNaming - public class TTlsSocketClientTransport : TStreamClientTransport + public class TTlsSocketTransport : TStreamTransport { private readonly X509Certificate2 _certificate; private readonly RemoteCertificateValidationCallback _certValidator; @@ -38,14 +38,17 @@ public class TTlsSocketClientTransport : TStreamClientTransport private readonly LocalCertificateSelectionCallback _localCertificateSelectionCallback; private readonly int _port; private readonly SslProtocols _sslProtocols; + private readonly string _targetHost; private TcpClient _client; private SslStream _secureStream; private int _timeout; - public TTlsSocketClientTransport(TcpClient client, X509Certificate2 certificate, bool isServer = false, + public TTlsSocketTransport(TcpClient client, TConfiguration config, + X509Certificate2 certificate, bool isServer = false, RemoteCertificateValidationCallback certValidator = null, LocalCertificateSelectionCallback localCertificateSelectionCallback = null, SslProtocols sslProtocols = SslProtocols.Tls12) + : base(config) { _client = client; _certificate = certificate; @@ -56,7 +59,7 @@ public TTlsSocketClientTransport(TcpClient client, X509Certificate2 certificate, if (isServer && certificate == null) { - throw new ArgumentException("TTlsSocketClientTransport needs certificate to be used for server", + throw new ArgumentException("TTlsSocketTransport needs certificate to be used for server", nameof(certificate)); } @@ -67,11 +70,12 @@ public TTlsSocketClientTransport(TcpClient client, X509Certificate2 certificate, } } - public TTlsSocketClientTransport(IPAddress host, int port, string certificatePath, + public TTlsSocketTransport(IPAddress host, int port, TConfiguration config, + string certificatePath, RemoteCertificateValidationCallback certValidator = null, LocalCertificateSelectionCallback localCertificateSelectionCallback = null, SslProtocols sslProtocols = SslProtocols.Tls12) - : this(host, port, 0, + : this(host, port, config, 0, new X509Certificate2(certificatePath), certValidator, localCertificateSelectionCallback, @@ -79,12 +83,12 @@ public TTlsSocketClientTransport(IPAddress host, int port, string certificatePat { } - public TTlsSocketClientTransport(IPAddress host, int port, + public TTlsSocketTransport(IPAddress host, int port, TConfiguration config, X509Certificate2 certificate = null, RemoteCertificateValidationCallback certValidator = null, LocalCertificateSelectionCallback localCertificateSelectionCallback = null, SslProtocols sslProtocols = SslProtocols.Tls12) - : this(host, port, 0, + : this(host, port, config, 0, certificate, certValidator, localCertificateSelectionCallback, @@ -92,11 +96,12 @@ public TTlsSocketClientTransport(IPAddress host, int port, { } - public TTlsSocketClientTransport(IPAddress host, int port, int timeout, + public TTlsSocketTransport(IPAddress host, int port, TConfiguration config, int timeout, X509Certificate2 certificate, RemoteCertificateValidationCallback certValidator = null, LocalCertificateSelectionCallback localCertificateSelectionCallback = null, SslProtocols sslProtocols = SslProtocols.Tls12) + : base(config) { _host = host; _port = port; @@ -109,6 +114,37 @@ public TTlsSocketClientTransport(IPAddress host, int port, int timeout, InitSocket(); } + public TTlsSocketTransport(string host, int port, TConfiguration config, int timeout, + X509Certificate2 certificate, + RemoteCertificateValidationCallback certValidator = null, + LocalCertificateSelectionCallback localCertificateSelectionCallback = null, + SslProtocols sslProtocols = SslProtocols.Tls12) + : base(config) + { + try + { + _targetHost = host; + + var entry = Dns.GetHostEntry(host); + if (entry.AddressList.Length == 0) + throw new TTransportException(TTransportException.ExceptionType.Unknown, "unable to resolve host name"); + + _host = entry.AddressList[0]; + _port = port; + _timeout = timeout; + _certificate = certificate; + _certValidator = certValidator; + _localCertificateSelectionCallback = localCertificateSelectionCallback; + _sslProtocols = sslProtocols; + + InitSocket(); + } + catch (SocketException e) + { + throw new TTransportException(TTransportException.ExceptionType.Unknown, e.Message, e); + } + } + public int Timeout { set { _client.ReceiveTimeout = _client.SendTimeout = _timeout = value; } @@ -204,7 +240,7 @@ public async Task SetupTlsAsync() ? new X509CertificateCollection {_certificate} : new X509CertificateCollection(); - var targetHost = _host.ToString(); + var targetHost = _targetHost ?? _host.ToString(); await _secureStream.AuthenticateAsClientAsync(targetHost, certs, _sslProtocols, true); } } @@ -234,4 +270,4 @@ public override void Close() } } } -} \ No newline at end of file +} diff --git a/lib/netstd/Thrift/Transport/Layered/TBufferedTransport.cs b/lib/netstd/Thrift/Transport/Layered/TBufferedTransport.cs new file mode 100644 index 00000000000..dee52ddb18c --- /dev/null +++ b/lib/netstd/Thrift/Transport/Layered/TBufferedTransport.cs @@ -0,0 +1,209 @@ +// Licensed to the Apache Software Foundation(ASF) under one +// or more contributor license agreements.See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership.The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +using System; +using System.Diagnostics; +using System.IO; +using System.Threading; +using System.Threading.Tasks; + +namespace Thrift.Transport +{ + // ReSharper disable once InconsistentNaming + public class TBufferedTransport : TLayeredTransport + { + private readonly int DesiredBufferSize; + private readonly Client.TMemoryBufferTransport ReadBuffer; + private readonly Client.TMemoryBufferTransport WriteBuffer; + private bool IsDisposed; + + public class Factory : TTransportFactory + { + public override TTransport GetTransport(TTransport trans) + { + return new TBufferedTransport(trans); + } + } + + //TODO: should support only specified input transport? + public TBufferedTransport(TTransport transport, int bufSize = 1024) + : base(transport) + { + if (bufSize <= 0) + { + throw new ArgumentOutOfRangeException(nameof(bufSize), "Buffer size must be a positive number."); + } + + DesiredBufferSize = bufSize; + + WriteBuffer = new Client.TMemoryBufferTransport(InnerTransport.Configuration, bufSize); + ReadBuffer = new Client.TMemoryBufferTransport(InnerTransport.Configuration, bufSize); + + Debug.Assert(DesiredBufferSize == ReadBuffer.Capacity); + Debug.Assert(DesiredBufferSize == WriteBuffer.Capacity); + } + + public TTransport UnderlyingTransport + { + get + { + CheckNotDisposed(); + + return InnerTransport; + } + } + + public override bool IsOpen => !IsDisposed && InnerTransport.IsOpen; + + public override async Task OpenAsync(CancellationToken cancellationToken) + { + CheckNotDisposed(); + + await InnerTransport.OpenAsync(cancellationToken); + } + + public override void Close() + { + CheckNotDisposed(); + + InnerTransport.Close(); + } + + public override async ValueTask ReadAsync(byte[] buffer, int offset, int length, CancellationToken cancellationToken) + { + CheckNotDisposed(); + ValidateBufferArgs(buffer, offset, length); + + if (!IsOpen) + { + throw new TTransportException(TTransportException.ExceptionType.NotOpen); + } + + + // do we have something buffered? + var count = ReadBuffer.Length - ReadBuffer.Position; + if (count > 0) + { + return await ReadBuffer.ReadAsync(buffer, offset, length, cancellationToken); + } + + // does the request even fit into the buffer? + // Note we test for >= instead of > to avoid nonsense buffering + if (length >= ReadBuffer.Capacity) + { + return await InnerTransport.ReadAsync(buffer, offset, length, cancellationToken); + } + + // buffer a new chunk of bytes from the underlying transport + ReadBuffer.Length = ReadBuffer.Capacity; + ArraySegment bufSegment; + ReadBuffer.TryGetBuffer(out bufSegment); + ReadBuffer.Length = await InnerTransport.ReadAsync(bufSegment.Array, 0, bufSegment.Count, cancellationToken); + ReadBuffer.Position = 0; + + // deliver the bytes + return await ReadBuffer.ReadAsync(buffer, offset, length, cancellationToken); + } + + + public override async Task WriteAsync(byte[] buffer, int offset, int length, CancellationToken cancellationToken) + { + CheckNotDisposed(); + ValidateBufferArgs(buffer, offset, length); + + if (!IsOpen) + { + throw new TTransportException(TTransportException.ExceptionType.NotOpen); + } + + // enough space left in buffer? + var free = WriteBuffer.Capacity - WriteBuffer.Length; + if (length > free) + { + ArraySegment bufSegment; + WriteBuffer.TryGetBuffer(out bufSegment); + await InnerTransport.WriteAsync(bufSegment.Array, 0, bufSegment.Count, cancellationToken); + WriteBuffer.SetLength(0); + } + + // do the data even fit into the buffer? + // Note we test for < instead of <= to avoid nonsense buffering + if (length < WriteBuffer.Capacity) + { + await WriteBuffer.WriteAsync(buffer, offset, length, cancellationToken); + return; + } + + // write thru + await InnerTransport.WriteAsync(buffer, offset, length, cancellationToken); + } + + public override async Task FlushAsync(CancellationToken cancellationToken) + { + CheckNotDisposed(); + + if (!IsOpen) + { + throw new TTransportException(TTransportException.ExceptionType.NotOpen); + } + + if (WriteBuffer.Length > 0) + { + ArraySegment bufSegment; + WriteBuffer.TryGetBuffer(out bufSegment); + await InnerTransport.WriteAsync(bufSegment.Array, 0, bufSegment.Count, cancellationToken); + WriteBuffer.SetLength(0); + } + + await InnerTransport.FlushAsync(cancellationToken); + } + + public override void CheckReadBytesAvailable(long numBytes) + { + var buffered = ReadBuffer.Length - ReadBuffer.Position; + if (buffered < numBytes) + { + numBytes -= buffered; + InnerTransport.CheckReadBytesAvailable(numBytes); + } + } + + + private void CheckNotDisposed() + { + if (IsDisposed) + { + throw new ObjectDisposedException(nameof(InnerTransport)); + } + } + + // IDisposable + protected override void Dispose(bool disposing) + { + if (!IsDisposed) + { + if (disposing) + { + ReadBuffer?.Dispose(); + WriteBuffer?.Dispose(); + InnerTransport?.Dispose(); + } + } + IsDisposed = true; + } + } +} diff --git a/lib/netstd/Thrift/Transport/Layered/TFramedTransport.cs b/lib/netstd/Thrift/Transport/Layered/TFramedTransport.cs new file mode 100644 index 00000000000..be1513fcf9c --- /dev/null +++ b/lib/netstd/Thrift/Transport/Layered/TFramedTransport.cs @@ -0,0 +1,191 @@ +// Licensed to the Apache Software Foundation(ASF) under one +// or more contributor license agreements.See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership.The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +using System; +using System.Buffers.Binary; +using System.IO; +using System.Threading; +using System.Threading.Tasks; + +namespace Thrift.Transport +{ + // ReSharper disable once InconsistentNaming + public class TFramedTransport : TLayeredTransport + { + private const int HeaderSize = 4; + private readonly byte[] HeaderBuf = new byte[HeaderSize]; + private readonly Client.TMemoryBufferTransport ReadBuffer; + private readonly Client.TMemoryBufferTransport WriteBuffer; + + private bool IsDisposed; + + public class Factory : TTransportFactory + { + public override TTransport GetTransport(TTransport trans) + { + return new TFramedTransport(trans); + } + } + + public TFramedTransport(TTransport transport) + : base(transport) + { + ReadBuffer = new Client.TMemoryBufferTransport(Configuration); + WriteBuffer = new Client.TMemoryBufferTransport(Configuration); + InitWriteBuffer(); + } + + public override bool IsOpen => !IsDisposed && InnerTransport.IsOpen; + + public override async Task OpenAsync(CancellationToken cancellationToken) + { + CheckNotDisposed(); + + await InnerTransport.OpenAsync(cancellationToken); + } + + public override void Close() + { + CheckNotDisposed(); + + InnerTransport.Close(); + } + + public override async ValueTask ReadAsync(byte[] buffer, int offset, int length, CancellationToken cancellationToken) + { + CheckNotDisposed(); + ValidateBufferArgs(buffer, offset, length); + + if (!IsOpen) + { + throw new TTransportException(TTransportException.ExceptionType.NotOpen); + } + + // Read another frame of data if we run out of bytes + if (ReadBuffer.Position >= ReadBuffer.Length) + { + await ReadFrameAsync(cancellationToken); + } + + return await ReadBuffer.ReadAsync(buffer, offset, length, cancellationToken); + } + + private async ValueTask ReadFrameAsync(CancellationToken cancellationToken) + { + await InnerTransport.ReadAllAsync(HeaderBuf, 0, HeaderSize, cancellationToken); + int size = BinaryPrimitives.ReadInt32BigEndian(HeaderBuf); + + if ((0 > size) || (size > Configuration.MaxFrameSize)) // size must be in the range 0 to allowed max + throw new TTransportException(TTransportException.ExceptionType.Unknown, $"Maximum frame size exceeded ({size} bytes)"); + UpdateKnownMessageSize(size + HeaderSize); + + ReadBuffer.SetLength(size); + ReadBuffer.Seek(0, SeekOrigin.Begin); + + ArraySegment bufSegment; + ReadBuffer.TryGetBuffer(out bufSegment); + await InnerTransport.ReadAllAsync(bufSegment.Array, 0, size, cancellationToken); + } + + public override async Task WriteAsync(byte[] buffer, int offset, int length, CancellationToken cancellationToken) + { + CheckNotDisposed(); + ValidateBufferArgs(buffer, offset, length); + + if (!IsOpen) + { + throw new TTransportException(TTransportException.ExceptionType.NotOpen); + } + + if (WriteBuffer.Length > (int.MaxValue - length)) + { + await FlushAsync(cancellationToken); + } + + await WriteBuffer.WriteAsync(buffer, offset, length, cancellationToken); + } + + public override async Task FlushAsync(CancellationToken cancellationToken) + { + CheckNotDisposed(); + + if (!IsOpen) + { + throw new TTransportException(TTransportException.ExceptionType.NotOpen); + } + + ArraySegment bufSegment; + WriteBuffer.TryGetBuffer(out bufSegment); + + int dataLen = bufSegment.Count - HeaderSize; + if (dataLen < 0) + { + throw new InvalidOperationException(); // logic error actually + } + + // Inject message header into the reserved buffer space + BinaryPrimitives.WriteInt32BigEndian(bufSegment.Array, dataLen); + + // Send the entire message at once + await InnerTransport.WriteAsync(bufSegment.Array, 0, bufSegment.Count, cancellationToken); + + InitWriteBuffer(); + + await InnerTransport.FlushAsync(cancellationToken); + } + + private void InitWriteBuffer() + { + // Reserve space for message header to be put right before sending it out + WriteBuffer.SetLength(HeaderSize); + WriteBuffer.Seek(0, SeekOrigin.End); + } + + public override void CheckReadBytesAvailable(long numBytes) + { + var buffered = ReadBuffer.Length - ReadBuffer.Position; + if (buffered < numBytes) + { + numBytes -= buffered; + InnerTransport.CheckReadBytesAvailable(numBytes); + } + } + + private void CheckNotDisposed() + { + if (IsDisposed) + { + throw new ObjectDisposedException(this.GetType().Name); + } + } + + // IDisposable + protected override void Dispose(bool disposing) + { + if (!IsDisposed) + { + if (disposing) + { + ReadBuffer?.Dispose(); + WriteBuffer?.Dispose(); + InnerTransport?.Dispose(); + } + } + IsDisposed = true; + } + } +} diff --git a/lib/netstd/Thrift/Transport/Layered/TLayeredTransport.cs b/lib/netstd/Thrift/Transport/Layered/TLayeredTransport.cs new file mode 100644 index 00000000000..2137ae458ff --- /dev/null +++ b/lib/netstd/Thrift/Transport/Layered/TLayeredTransport.cs @@ -0,0 +1,28 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace Thrift.Transport +{ + public abstract class TLayeredTransport : TTransport + { + public readonly TTransport InnerTransport; + + public override TConfiguration Configuration { get => InnerTransport.Configuration; } + + public TLayeredTransport(TTransport transport) + { + InnerTransport = transport ?? throw new ArgumentNullException(nameof(transport)); + } + + public override void UpdateKnownMessageSize(long size) + { + InnerTransport.UpdateKnownMessageSize(size); + } + + public override void CheckReadBytesAvailable(long numBytes) + { + InnerTransport.CheckReadBytesAvailable(numBytes); + } + } +} diff --git a/lib/netstd/Thrift/Transport/Server/NullLogger.cs b/lib/netstd/Thrift/Transport/Server/NullLogger.cs new file mode 100644 index 00000000000..1f1f542d59f --- /dev/null +++ b/lib/netstd/Thrift/Transport/Server/NullLogger.cs @@ -0,0 +1,56 @@ +// Licensed to the Apache Software Foundation(ASF) under one +// or more contributor license agreements.See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership.The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +using Microsoft.Extensions.Logging; +using System; + + +namespace Thrift.Transport.Server +{ + // sometimes we just don't want to log anything + internal class NullLogger : IDisposable, ILogger, ILogger + { + internal class NullScope : IDisposable + { + public void Dispose() + { + // nothing to do + } + } + + public IDisposable BeginScope(TState state) + { + return new NullScope(); + } + + public void Dispose() + { + // nothing to do + } + + public bool IsEnabled(LogLevel logLevel) + { + return false; // no + } + + public void Log(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func formatter) + { + // do nothing + } + } +} + diff --git a/lib/netstd/Thrift/Transport/Server/THttpServerTransport.cs b/lib/netstd/Thrift/Transport/Server/THttpServerTransport.cs new file mode 100644 index 00000000000..7271f504e3d --- /dev/null +++ b/lib/netstd/Thrift/Transport/Server/THttpServerTransport.cs @@ -0,0 +1,127 @@ +// Licensed to the Apache Software Foundation(ASF) under one +// or more contributor license agreements.See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership.The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +using System; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Http; +using Microsoft.Extensions.Logging; +using Thrift.Processor; +using Thrift.Protocol; +using Thrift.Transport.Client; + +namespace Thrift.Transport.Server +{ + // ReSharper disable once InconsistentNaming + public class THttpServerTransport + { + protected const string ContentType = "application/x-thrift"; + private readonly ILogger _logger; + private readonly RequestDelegate _next; + protected Encoding Encoding = Encoding.UTF8; + + protected TProtocolFactory InputProtocolFactory; + protected TProtocolFactory OutputProtocolFactory; + + protected TTransportFactory InputTransportFactory; + protected TTransportFactory OutputTransportFactory; + + protected ITAsyncProcessor Processor; + protected TConfiguration Configuration; + + public THttpServerTransport( + ITAsyncProcessor processor, + TConfiguration config, + RequestDelegate next = null, + ILoggerFactory loggerFactory = null) + : this(processor, config, new TBinaryProtocol.Factory(), null, next, loggerFactory) + { + } + + public THttpServerTransport( + ITAsyncProcessor processor, + TConfiguration config, + TProtocolFactory protocolFactory, + TTransportFactory transFactory = null, + RequestDelegate next = null, + ILoggerFactory loggerFactory = null) + : this(processor, config, protocolFactory, protocolFactory, transFactory, transFactory, next, loggerFactory) + { + } + + public THttpServerTransport( + ITAsyncProcessor processor, + TConfiguration config, + TProtocolFactory inputProtocolFactory, + TProtocolFactory outputProtocolFactory, + TTransportFactory inputTransFactory = null, + TTransportFactory outputTransFactory = null, + RequestDelegate next = null, + ILoggerFactory loggerFactory = null) + { + // loggerFactory == null is not illegal anymore + + Processor = processor ?? throw new ArgumentNullException(nameof(processor)); + Configuration = config; // may be null + + InputProtocolFactory = inputProtocolFactory ?? throw new ArgumentNullException(nameof(inputProtocolFactory)); + OutputProtocolFactory = outputProtocolFactory ?? throw new ArgumentNullException(nameof(outputProtocolFactory)); + + InputTransportFactory = inputTransFactory; + OutputTransportFactory = outputTransFactory; + + _next = next; + _logger = (loggerFactory != null) ? loggerFactory.CreateLogger() : new NullLogger(); + } + + public async Task Invoke(HttpContext context) + { + context.Response.ContentType = ContentType; + await ProcessRequestAsync(context, context.RequestAborted); //TODO: check for correct logic + } + + public async Task ProcessRequestAsync(HttpContext context, CancellationToken cancellationToken) + { + var transport = new TStreamTransport(context.Request.Body, context.Response.Body, Configuration); + + try + { + var intrans = (InputTransportFactory != null) ? InputTransportFactory.GetTransport(transport) : transport; + var outtrans = (OutputTransportFactory != null) ? OutputTransportFactory.GetTransport(transport) : transport; + + var input = InputProtocolFactory.GetProtocol(intrans); + var output = OutputProtocolFactory.GetProtocol(outtrans); + + while (await Processor.ProcessAsync(input, output, cancellationToken)) + { + if (!context.Response.HasStarted) // oneway method called + await context.Response.Body.FlushAsync(cancellationToken); + } + } + catch (TTransportException) + { + if (!context.Response.HasStarted) // if something goes bust, let the client know + context.Response.StatusCode = 500; + } + finally + { + transport.Close(); + } + } + } +} diff --git a/lib/netstd/Thrift/Transport/Server/TNamedPipeServerTransport.cs b/lib/netstd/Thrift/Transport/Server/TNamedPipeServerTransport.cs new file mode 100644 index 00000000000..33811100198 --- /dev/null +++ b/lib/netstd/Thrift/Transport/Server/TNamedPipeServerTransport.cs @@ -0,0 +1,328 @@ +// Licensed to the Apache Software Foundation(ASF) under one +// or more contributor license agreements.See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership.The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +using Microsoft.Win32.SafeHandles; +using System; +using System.IO.Pipes; +using System.Runtime.InteropServices; +using System.Threading; +using System.Threading.Tasks; +using System.ComponentModel; +using System.Security.AccessControl; +using System.Security.Principal; + +namespace Thrift.Transport.Server +{ + // ReSharper disable once InconsistentNaming + public class TNamedPipeServerTransport : TServerTransport + { + /// + /// This is the address of the Pipe on the localhost. + /// + private readonly string _pipeAddress; + private bool _asyncMode = true; + private volatile bool _isPending = true; + private NamedPipeServerStream _stream = null; + + public TNamedPipeServerTransport(string pipeAddress, TConfiguration config) + : base(config) + { + _pipeAddress = pipeAddress; + } + + public override bool IsOpen() { + return true; + } + + public override void Listen() + { + // nothing to do here + } + + public override void Close() + { + if (_stream != null) + { + try + { + if (_stream.IsConnected) + _stream.Disconnect(); + _stream.Dispose(); + } + finally + { + _stream = null; + _isPending = false; + } + } + } + + public override bool IsClientPending() + { + return _isPending; + } + + private void EnsurePipeInstance() + { + if (_stream == null) + { + const PipeDirection direction = PipeDirection.InOut; + const int maxconn = NamedPipeServerStream.MaxAllowedServerInstances; + const PipeTransmissionMode mode = PipeTransmissionMode.Byte; + const int inbuf = 4096; + const int outbuf = 4096; + var options = _asyncMode ? PipeOptions.Asynchronous : PipeOptions.None; + + + // TODO: "CreatePipeNative" ist only a workaround, and there are have basically two possible outcomes: + // - once NamedPipeServerStream() gets a CTOR that supports pipesec, remove CreatePipeNative() + // - if 31190 gets resolved before, use _stream.SetAccessControl(pipesec) instead of CreatePipeNative() + // EITHER WAY, + // - if CreatePipeNative() finally gets removed, also remove "allow unsafe code" from the project settings + + try + { + var handle = CreatePipeNative(_pipeAddress, inbuf, outbuf); + if ((handle != null) && (!handle.IsInvalid)) + { + _stream = new NamedPipeServerStream(PipeDirection.InOut, _asyncMode, false, handle); + handle = null; // we don't own it any longer + } + else + { + handle?.Dispose(); + _stream = new NamedPipeServerStream(_pipeAddress, direction, maxconn, mode, options, inbuf, outbuf/*, pipesec*/); + } + } + catch (NotImplementedException) // Mono still does not support async, fallback to sync + { + if (_asyncMode) + { + options &= (~PipeOptions.Asynchronous); + _stream = new NamedPipeServerStream(_pipeAddress, direction, maxconn, mode, options, inbuf, outbuf); + _asyncMode = false; + } + else + { + throw; + } + } + } + } + + + #region CreatePipeNative workaround + + + [StructLayout(LayoutKind.Sequential)] + internal class SECURITY_ATTRIBUTES + { + internal int nLength = 0; + internal IntPtr lpSecurityDescriptor = IntPtr.Zero; + internal int bInheritHandle = 0; + } + + + private const string Kernel32 = "kernel32.dll"; + + [DllImport(Kernel32, SetLastError = true)] + internal static extern IntPtr CreateNamedPipe( + string lpName, uint dwOpenMode, uint dwPipeMode, + uint nMaxInstances, uint nOutBufferSize, uint nInBufferSize, uint nDefaultTimeOut, + SECURITY_ATTRIBUTES pipeSecurityDescriptor + ); + + + + // Workaround: create the pipe via API call + // we have to do it this way, since NamedPipeServerStream() for netstd still lacks a few CTORs + // and _stream.SetAccessControl(pipesec); only keeps throwing ACCESS_DENIED errors at us + // References: + // - https://github.com/dotnet/corefx/issues/30170 (closed, continued in 31190) + // - https://github.com/dotnet/corefx/issues/31190 System.IO.Pipes.AccessControl package does not work + // - https://github.com/dotnet/corefx/issues/24040 NamedPipeServerStream: Provide support for WRITE_DAC + // - https://github.com/dotnet/corefx/issues/34400 Have a mechanism for lower privileged user to connect to a privileged user's pipe + private SafePipeHandle CreatePipeNative(string name, int inbuf, int outbuf) + { + if (Environment.OSVersion.Platform != PlatformID.Win32NT) + return null; // Windows only + + var pinningHandle = new GCHandle(); + try + { + // owner gets full access, everyone else read/write + var pipesec = new PipeSecurity(); + using (var currentIdentity = WindowsIdentity.GetCurrent()) + { + var sidOwner = currentIdentity.Owner; + var sidWorld = new SecurityIdentifier(WellKnownSidType.WorldSid, null); + + pipesec.SetOwner(sidOwner); + pipesec.AddAccessRule(new PipeAccessRule(sidOwner, PipeAccessRights.FullControl, AccessControlType.Allow)); + pipesec.AddAccessRule(new PipeAccessRule(sidWorld, PipeAccessRights.ReadWrite, AccessControlType.Allow)); + } + + // create a security descriptor and assign it to the security attribs + var secAttrs = new SECURITY_ATTRIBUTES(); + byte[] sdBytes = pipesec.GetSecurityDescriptorBinaryForm(); + pinningHandle = GCHandle.Alloc(sdBytes, GCHandleType.Pinned); + unsafe { + fixed (byte* pSD = sdBytes) { + secAttrs.lpSecurityDescriptor = (IntPtr)pSD; + } + } + + // a bunch of constants we will need shortly + const int PIPE_ACCESS_DUPLEX = 0x00000003; + const int FILE_FLAG_OVERLAPPED = 0x40000000; + const int WRITE_DAC = 0x00040000; + const int PIPE_TYPE_BYTE = 0x00000000; + const int PIPE_READMODE_BYTE = 0x00000000; + const int PIPE_UNLIMITED_INSTANCES = 255; + + // create the pipe via API call + var rawHandle = CreateNamedPipe( + @"\\.\pipe\" + name, + PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED | WRITE_DAC, + PIPE_TYPE_BYTE | PIPE_READMODE_BYTE, + PIPE_UNLIMITED_INSTANCES, (uint)inbuf, (uint)outbuf, + 5 * 1000, + secAttrs + ); + + // make a SafePipeHandle() from it + var handle = new SafePipeHandle(rawHandle, true); + if (handle.IsInvalid) + throw new Win32Exception(Marshal.GetLastWin32Error()); + + // return it (to be packaged) + return handle; + } + finally + { + if (pinningHandle.IsAllocated) + pinningHandle.Free(); + } + } + + #endregion + + protected override async ValueTask AcceptImplementationAsync(CancellationToken cancellationToken) + { + try + { + EnsurePipeInstance(); + + await _stream.WaitForConnectionAsync(cancellationToken); + + var trans = new ServerTransport(_stream, Configuration); + _stream = null; // pass ownership to ServerTransport + + //_isPending = false; + + return trans; + } + catch (TTransportException) + { + Close(); + throw; + } + catch (Exception e) + { + Close(); + throw new TTransportException(TTransportException.ExceptionType.NotOpen, e.Message); + } + } + + private class ServerTransport : TEndpointTransport + { + private readonly NamedPipeServerStream PipeStream; + + public ServerTransport(NamedPipeServerStream stream, TConfiguration config) + : base(config) + { + PipeStream = stream; + } + + public override bool IsOpen => PipeStream != null && PipeStream.IsConnected; + + public override Task OpenAsync(CancellationToken cancellationToken) + { + cancellationToken.ThrowIfCancellationRequested(); + return Task.CompletedTask; + } + + public override void Close() + { + PipeStream?.Dispose(); + } + + public override async ValueTask ReadAsync(byte[] buffer, int offset, int length, CancellationToken cancellationToken) + { + if (PipeStream == null) + { + throw new TTransportException(TTransportException.ExceptionType.NotOpen); + } + + CheckReadBytesAvailable(length); +#if NETSTANDARD2_1 + var numBytes = await PipeStream.ReadAsync(new Memory(buffer, offset, length), cancellationToken); +#else + var numBytes = await PipeStream.ReadAsync(buffer, offset, length, cancellationToken); +#endif + CountConsumedMessageBytes(numBytes); + return numBytes; + } + + public override async Task WriteAsync(byte[] buffer, int offset, int length, CancellationToken cancellationToken) + { + if (PipeStream == null) + { + throw new TTransportException(TTransportException.ExceptionType.NotOpen); + } + + // if necessary, send the data in chunks + // there's a system limit around 0x10000 bytes that we hit otherwise + // MSDN: "Pipe write operations across a network are limited to 65,535 bytes per write. For more information regarding pipes, see the Remarks section." + var nBytes = Math.Min(15 * 4096, length); // 16 would exceed the limit + while (nBytes > 0) + { + await PipeStream.WriteAsync(buffer, offset, nBytes, cancellationToken); + offset += nBytes; + length -= nBytes; + nBytes = Math.Min(nBytes, length); + } + } + + public override Task FlushAsync(CancellationToken cancellationToken) + { + cancellationToken.ThrowIfCancellationRequested(); + + ResetConsumedMessageSize(); + return Task.CompletedTask; + } + + protected override void Dispose(bool disposing) + { + if (disposing) + { + PipeStream?.Dispose(); + } + } + } + } +} diff --git a/lib/netcore/Thrift/Transports/Server/TServerFramedTransport.cs b/lib/netstd/Thrift/Transport/Server/TServerSocketTransport.cs similarity index 67% rename from lib/netcore/Thrift/Transports/Server/TServerFramedTransport.cs rename to lib/netstd/Thrift/Transport/Server/TServerSocketTransport.cs index 0b86e9ebba1..d7421c944bc 100644 --- a/lib/netcore/Thrift/Transports/Server/TServerFramedTransport.cs +++ b/lib/netstd/Thrift/Transport/Server/TServerSocketTransport.cs @@ -1,13 +1,13 @@ -// Licensed to the Apache Software Foundation(ASF) under one +// Licensed to the Apache Software Foundation(ASF) under one // or more contributor license agreements.See the NOTICE file // distributed with this work for additional information // regarding copyright ownership.The ASF licenses this file // to you under the Apache License, Version 2.0 (the // "License"); you may not use this file except in compliance // with the License. You may obtain a copy of the License at -// +// // http://www.apache.org/licenses/LICENSE-2.0 -// +// // Unless required by applicable law or agreed to in writing, // software distributed under the License is distributed on an // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY @@ -20,41 +20,31 @@ using System.Net.Sockets; using System.Threading; using System.Threading.Tasks; -using Thrift.Transports.Client; +using Thrift.Transport.Client; -namespace Thrift.Transports.Server +namespace Thrift.Transport.Server { + // ReSharper disable once InconsistentNaming - public class TServerFramedTransport : TServerTransport + public class TServerSocketTransport : TServerTransport { private readonly int _clientTimeout; - private readonly int _port; private TcpListener _server; - public TServerFramedTransport(TcpListener listener) - : this(listener, 0) - { - } - - public TServerFramedTransport(TcpListener listener, int clientTimeout) + public TServerSocketTransport(TcpListener listener, TConfiguration config, int clientTimeout = 0) + : base(config) { _server = listener; _clientTimeout = clientTimeout; } - public TServerFramedTransport(int port) - : this(port, 0) - { - } - - public TServerFramedTransport(int port, int clientTimeout) + public TServerSocketTransport(int port, TConfiguration config, int clientTimeout = 0) + : this(null, config, clientTimeout) { - _port = port; - _clientTimeout = clientTimeout; try { // Make server socket - _server = new TcpListener(IPAddress.Any, _port); + _server = new TcpListener(IPAddress.Any, port); _server.Server.NoDelay = true; } catch (Exception) @@ -64,6 +54,32 @@ public TServerFramedTransport(int port, int clientTimeout) } } + public override bool IsOpen() + { + return (_server != null) + && (_server.Server != null) + && _server.Server.IsBound; + } + + public int GetPort() + { + if ((_server != null) && (_server.Server != null) && (_server.Server.LocalEndPoint != null)) + { + if (_server.Server.LocalEndPoint is IPEndPoint server) + { + return server.Port; + } + else + { + throw new TTransportException("ServerSocket is not a network socket"); + } + } + else + { + throw new TTransportException("ServerSocket is not open"); + } + } + public override void Listen() { // Make sure not to block on accept @@ -85,12 +101,9 @@ public override bool IsClientPending() return _server.Pending(); } - protected override async Task AcceptImplementationAsync(CancellationToken cancellationToken) + protected override async ValueTask AcceptImplementationAsync(CancellationToken cancellationToken) { - if (cancellationToken.IsCancellationRequested) - { - return await Task.FromCanceled(cancellationToken); - } + cancellationToken.ThrowIfCancellationRequested(); if (_server == null) { @@ -99,15 +112,15 @@ protected override async Task AcceptImplementationAsync(Cancel try { - TFramedClientTransport tSocketTransport = null; + TTransport tSocketTransport = null; var tcpClient = await _server.AcceptTcpClientAsync(); try { - tSocketTransport = new TFramedClientTransport(new TSocketClientTransport(tcpClient) + tSocketTransport = new TSocketTransport(tcpClient, Configuration) { Timeout = _clientTimeout - }); + }; return tSocketTransport; } @@ -119,7 +132,7 @@ protected override async Task AcceptImplementationAsync(Cancel } else // Otherwise, clean it up ourselves. { - ((IDisposable) tcpClient).Dispose(); + ((IDisposable)tcpClient).Dispose(); } throw; @@ -147,4 +160,4 @@ public override void Close() } } } -} \ No newline at end of file +} diff --git a/lib/netcore/Thrift/Transports/TServerTransport.cs b/lib/netstd/Thrift/Transport/Server/TServerTransport.cs similarity index 71% rename from lib/netcore/Thrift/Transports/TServerTransport.cs rename to lib/netstd/Thrift/Transport/Server/TServerTransport.cs index 0d45a55f9b5..eee50fb5a10 100644 --- a/lib/netcore/Thrift/Transports/TServerTransport.cs +++ b/lib/netstd/Thrift/Transport/Server/TServerTransport.cs @@ -5,9 +5,9 @@ // to you under the Apache License, Version 2.0 (the // "License"); you may not use this file except in compliance // with the License. You may obtain a copy of the License at -// +// // http://www.apache.org/licenses/LICENSE-2.0 -// +// // Unless required by applicable law or agreed to in writing, // software distributed under the License is distributed on an // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY @@ -18,28 +18,37 @@ using System.Threading; using System.Threading.Tasks; -namespace Thrift.Transports +namespace Thrift.Transport { // ReSharper disable once InconsistentNaming public abstract class TServerTransport { + public readonly TConfiguration Configuration; + + public TServerTransport(TConfiguration config) + { + Configuration = config ?? new TConfiguration(); + } + + public abstract bool IsOpen(); + public abstract void Listen(); public abstract void Close(); public abstract bool IsClientPending(); - protected virtual async Task AcceptImplementationAsync() + protected virtual async ValueTask AcceptImplementationAsync() { return await AcceptImplementationAsync(CancellationToken.None); } - protected abstract Task AcceptImplementationAsync(CancellationToken cancellationToken); + protected abstract ValueTask AcceptImplementationAsync(CancellationToken cancellationToken); - public async Task AcceptAsync() + public async ValueTask AcceptAsync() { return await AcceptAsync(CancellationToken.None); } - public async Task AcceptAsync(CancellationToken cancellationToken) + public async ValueTask AcceptAsync(CancellationToken cancellationToken) { var transport = await AcceptImplementationAsync(cancellationToken); @@ -51,4 +60,4 @@ public async Task AcceptAsync(CancellationToken cancellationTo return transport; } } -} \ No newline at end of file +} diff --git a/lib/netcore/Thrift/Transports/Server/TTlsServerSocketTransport.cs b/lib/netstd/Thrift/Transport/Server/TTlsServerSocketTransport.cs similarity index 74% rename from lib/netcore/Thrift/Transports/Server/TTlsServerSocketTransport.cs rename to lib/netstd/Thrift/Transport/Server/TTlsServerSocketTransport.cs index 759feeddd8d..77abcaeb54f 100644 --- a/lib/netcore/Thrift/Transports/Server/TTlsServerSocketTransport.cs +++ b/lib/netstd/Thrift/Transport/Server/TTlsServerSocketTransport.cs @@ -5,9 +5,9 @@ // to you under the Apache License, Version 2.0 (the // "License"); you may not use this file except in compliance // with the License. You may obtain a copy of the License at -// +// // http://www.apache.org/licenses/LICENSE-2.0 -// +// // Unless required by applicable law or agreed to in writing, // software distributed under the License is distributed on an // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY @@ -23,9 +23,9 @@ using System.Security.Cryptography.X509Certificates; using System.Threading; using System.Threading.Tasks; -using Thrift.Transports.Client; +using Thrift.Transport.Client; -namespace Thrift.Transports.Server +namespace Thrift.Transport.Server { // ReSharper disable once InconsistentNaming public class TTlsServerSocketTransport : TServerTransport @@ -33,38 +33,18 @@ public class TTlsServerSocketTransport : TServerTransport private readonly RemoteCertificateValidationCallback _clientCertValidator; private readonly int _clientTimeout = 0; private readonly LocalCertificateSelectionCallback _localCertificateSelectionCallback; - private readonly int _port; private readonly X509Certificate2 _serverCertificate; private readonly SslProtocols _sslProtocols; - private readonly bool _useBufferedSockets; - private readonly bool _useFramedTransport; private TcpListener _server; - public TTlsServerSocketTransport(int port, X509Certificate2 certificate) - : this(port, false, certificate) - { - } - - public TTlsServerSocketTransport( - int port, - bool useBufferedSockets, - X509Certificate2 certificate, - RemoteCertificateValidationCallback clientCertValidator = null, - LocalCertificateSelectionCallback localCertificateSelectionCallback = null, - SslProtocols sslProtocols = SslProtocols.Tls12) - : this(port, useBufferedSockets, false, certificate, - clientCertValidator, localCertificateSelectionCallback, sslProtocols) - { - } - public TTlsServerSocketTransport( - int port, - bool useBufferedSockets, - bool useFramedTransport, + TcpListener listener, + TConfiguration config, X509Certificate2 certificate, RemoteCertificateValidationCallback clientCertValidator = null, LocalCertificateSelectionCallback localCertificateSelectionCallback = null, SslProtocols sslProtocols = SslProtocols.Tls12) + : base(config) { if (!certificate.HasPrivateKey) { @@ -72,18 +52,26 @@ public TTlsServerSocketTransport( "Your server-certificate needs to have a private key"); } - _port = port; _serverCertificate = certificate; - _useBufferedSockets = useBufferedSockets; - _useFramedTransport = useFramedTransport; _clientCertValidator = clientCertValidator; _localCertificateSelectionCallback = localCertificateSelectionCallback; _sslProtocols = sslProtocols; + _server = listener; + } + public TTlsServerSocketTransport( + int port, + TConfiguration config, + X509Certificate2 certificate, + RemoteCertificateValidationCallback clientCertValidator = null, + LocalCertificateSelectionCallback localCertificateSelectionCallback = null, + SslProtocols sslProtocols = SslProtocols.Tls12) + : this(null, config, certificate, clientCertValidator, localCertificateSelectionCallback, sslProtocols) + { try { // Create server socket - _server = new TcpListener(IPAddress.Any, _port); + _server = new TcpListener(IPAddress.Any, port); _server.Server.NoDelay = true; } catch (Exception) @@ -93,6 +81,32 @@ public TTlsServerSocketTransport( } } + public override bool IsOpen() + { + return (_server != null) + && (_server.Server != null) + && _server.Server.IsBound; + } + + public int GetPort() + { + if ((_server != null) && (_server.Server != null) && (_server.Server.LocalEndPoint != null)) + { + if (_server.Server.LocalEndPoint is IPEndPoint server) + { + return server.Port; + } + else + { + throw new TTransportException("ServerSocket is not a network socket"); + } + } + else + { + throw new TTransportException("ServerSocket is not open"); + } + } + public override void Listen() { // Make sure accept is not blocking @@ -114,12 +128,9 @@ public override bool IsClientPending() return _server.Pending(); } - protected override async Task AcceptImplementationAsync(CancellationToken cancellationToken) + protected override async ValueTask AcceptImplementationAsync(CancellationToken cancellationToken) { - if (cancellationToken.IsCancellationRequested) - { - return await Task.FromCanceled(cancellationToken); - } + cancellationToken.ThrowIfCancellationRequested(); if (_server == null) { @@ -132,24 +143,14 @@ protected override async Task AcceptImplementationAsync(Cancel client.SendTimeout = client.ReceiveTimeout = _clientTimeout; //wrap the client in an SSL Socket passing in the SSL cert - var tTlsSocket = new TTlsSocketClientTransport(client, _serverCertificate, true, _clientCertValidator, + var tTlsSocket = new TTlsSocketTransport( + client, Configuration, + _serverCertificate, true, _clientCertValidator, _localCertificateSelectionCallback, _sslProtocols); await tTlsSocket.SetupTlsAsync(); - TClientTransport trans = tTlsSocket; - - if (_useBufferedSockets) - { - trans = new TBufferedClientTransport(trans); - } - - if (_useFramedTransport) - { - trans = new TFramedClientTransport(trans); - } - - return trans; + return tTlsSocket; } catch (Exception ex) { @@ -174,4 +175,4 @@ public override void Close() } } } -} \ No newline at end of file +} diff --git a/lib/netstd/Thrift/Transport/TEndpointTransport.cs b/lib/netstd/Thrift/Transport/TEndpointTransport.cs new file mode 100644 index 00000000000..fa2ac6bddc0 --- /dev/null +++ b/lib/netstd/Thrift/Transport/TEndpointTransport.cs @@ -0,0 +1,87 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Text; + +namespace Thrift.Transport +{ + + abstract public class TEndpointTransport : TTransport + { + protected long MaxMessageSize { get => Configuration.MaxMessageSize; } + protected long KnownMessageSize { get; private set; } + protected long RemainingMessageSize { get; private set; } + + private readonly TConfiguration _configuration; + public override TConfiguration Configuration { get => _configuration; } + + public TEndpointTransport( TConfiguration config) + { + _configuration = config ?? new TConfiguration(); + Debug.Assert(Configuration != null); + + ResetConsumedMessageSize(); + } + + /// + /// Resets RemainingMessageSize to the configured maximum + /// + protected void ResetConsumedMessageSize(long newSize = -1) + { + // full reset + if (newSize < 0) + { + KnownMessageSize = MaxMessageSize; + RemainingMessageSize = MaxMessageSize; + return; + } + + // update only: message size can shrink, but not grow + Debug.Assert(KnownMessageSize <= MaxMessageSize); + if (newSize > KnownMessageSize) + throw new TTransportException(TTransportException.ExceptionType.EndOfFile, "MaxMessageSize reached"); + + KnownMessageSize = newSize; + RemainingMessageSize = newSize; + } + + /// + /// Updates RemainingMessageSize to reflect then known real message size (e.g. framed transport). + /// Will throw if we already consumed too many bytes or if the new size is larger than allowed. + /// + /// + public override void UpdateKnownMessageSize(long size) + { + var consumed = KnownMessageSize - RemainingMessageSize; + ResetConsumedMessageSize(size); + CountConsumedMessageBytes(consumed); + } + + /// + /// Throws if there are not enough bytes in the input stream to satisfy a read of numBytes bytes of data + /// + /// + public override void CheckReadBytesAvailable(long numBytes) + { + if (RemainingMessageSize < numBytes) + throw new TTransportException(TTransportException.ExceptionType.EndOfFile, "MaxMessageSize reached"); + } + + /// + /// Consumes numBytes from the RemainingMessageSize. + /// + /// + protected void CountConsumedMessageBytes(long numBytes) + { + if (RemainingMessageSize >= numBytes) + { + RemainingMessageSize -= numBytes; + } + else + { + RemainingMessageSize = 0; + throw new TTransportException(TTransportException.ExceptionType.EndOfFile, "MaxMessageSize reached"); + } + } + } +} diff --git a/lib/netcore/Thrift/Transports/TClientTransport.cs b/lib/netstd/Thrift/Transport/TTransport.cs similarity index 64% rename from lib/netcore/Thrift/Transports/TClientTransport.cs rename to lib/netstd/Thrift/Transport/TTransport.cs index 0dd96cb36fc..3f4245aa2ce 100644 --- a/lib/netcore/Thrift/Transports/TClientTransport.cs +++ b/lib/netstd/Thrift/Transport/TTransport.cs @@ -1,4 +1,4 @@ -// Licensed to the Apache Software Foundation(ASF) under one +// Licensed to the Apache Software Foundation(ASF) under one // or more contributor license agreements.See the NOTICE file // distributed with this work for additional information // regarding copyright ownership.The ASF licenses this file @@ -16,28 +16,32 @@ // under the License. using System; +using System.Diagnostics; using System.IO; using System.Threading; using System.Threading.Tasks; -namespace Thrift.Transports +namespace Thrift.Transport { //TODO: think about client info // ReSharper disable once InconsistentNaming - public abstract class TClientTransport : IDisposable + public abstract class TTransport : IDisposable { //TODO: think how to avoid peek byte private readonly byte[] _peekBuffer = new byte[1]; private bool _hasPeekByte; - public abstract bool IsOpen { get; } + public abstract bool IsOpen { get; } + public abstract TConfiguration Configuration { get; } + public abstract void UpdateKnownMessageSize(long size); + public abstract void CheckReadBytesAvailable(long numBytes); public void Dispose() { Dispose(true); GC.SuppressFinalize(this); } - public async Task PeekAsync(CancellationToken cancellationToken) + public async ValueTask PeekAsync(CancellationToken cancellationToken) { //If we already have a byte read but not consumed, do nothing. if (_hasPeekByte) @@ -69,12 +73,8 @@ public async Task PeekAsync(CancellationToken cancellationToken) return true; } - public virtual async Task OpenAsync() - { - await OpenAsync(CancellationToken.None); - } - public abstract Task OpenAsync(CancellationToken cancellationToken); + public abstract Task OpenAsync(CancellationToken cancellationToken = default); public abstract void Close(); @@ -85,74 +85,68 @@ protected static void ValidateBufferArgs(byte[] buffer, int offset, int length) throw new ArgumentNullException(nameof(buffer)); } +#if DEBUG // let it fail with OutOfRange in RELEASE mode if (offset < 0) { - throw new ArgumentOutOfRangeException(nameof(offset), "Buffer offset is smaller than zero."); + throw new ArgumentOutOfRangeException(nameof(offset), "Buffer offset must be >= 0"); } if (length < 0) { - throw new ArgumentOutOfRangeException(nameof(length), "Buffer length is smaller than zero."); + throw new ArgumentOutOfRangeException(nameof(length), "Buffer length must be >= 0"); } if (offset + length > buffer.Length) { - throw new ArgumentOutOfRangeException(nameof(buffer), "Not enough data."); + throw new ArgumentOutOfRangeException(nameof(buffer), "Not enough data"); } +#endif } - public virtual async Task ReadAsync(byte[] buffer, int offset, int length) - { - return await ReadAsync(buffer, offset, length, CancellationToken.None); - } - public abstract Task ReadAsync(byte[] buffer, int offset, int length, CancellationToken cancellationToken); + public abstract ValueTask ReadAsync(byte[] buffer, int offset, int length, CancellationToken cancellationToken); - public virtual async Task ReadAllAsync(byte[] buffer, int offset, int length) + public virtual async ValueTask ReadAllAsync(byte[] buffer, int offset, int length, CancellationToken cancellationToken) { - return await ReadAllAsync(buffer, offset, length, CancellationToken.None); - } + cancellationToken.ThrowIfCancellationRequested(); - public virtual async Task ReadAllAsync(byte[] buffer, int offset, int length, - CancellationToken cancellationToken) - { ValidateBufferArgs(buffer, offset, length); + if (length <= 0) + return 0; - if (cancellationToken.IsCancellationRequested) - { - return await Task.FromCanceled(cancellationToken); - } - - var retrieved = 0; - - //If we previously peeked a byte, we need to use that first. + // If we previously peeked a byte, we need to use that first. + var totalBytes = 0; if (_hasPeekByte) { - buffer[offset + retrieved++] = _peekBuffer[0]; + buffer[offset++] = _peekBuffer[0]; _hasPeekByte = false; + if (1 == length) + { + return 1; // we're done + } + ++totalBytes; } - while (retrieved < length) + var remaining = length - totalBytes; + Debug.Assert(remaining > 0); // any other possible cases should have been handled already + while (true) { - if (cancellationToken.IsCancellationRequested) + var numBytes = await ReadAsync(buffer, offset, remaining, cancellationToken); + totalBytes += numBytes; + if (totalBytes >= length) { - return await Task.FromCanceled(cancellationToken); + return totalBytes; // we're done } - var returnedCount = await ReadAsync(buffer, offset + retrieved, length - retrieved, cancellationToken); - if (returnedCount <= 0) + if (numBytes <= 0) { throw new TTransportException(TTransportException.ExceptionType.EndOfFile, "Cannot read, Remote side has closed"); } - retrieved += returnedCount; - } - return retrieved; - } - public virtual async Task WriteAsync(byte[] buffer) - { - await WriteAsync(buffer, CancellationToken.None); + remaining -= numBytes; + offset += numBytes; + } } public virtual async Task WriteAsync(byte[] buffer, CancellationToken cancellationToken) @@ -167,13 +161,9 @@ public virtual async Task WriteAsync(byte[] buffer, int offset, int length) public abstract Task WriteAsync(byte[] buffer, int offset, int length, CancellationToken cancellationToken); - public virtual async Task FlushAsync() - { - await FlushAsync(CancellationToken.None); - } public abstract Task FlushAsync(CancellationToken cancellationToken); protected abstract void Dispose(bool disposing); } -} \ No newline at end of file +} diff --git a/lib/netcore/Thrift/Transports/TTransportException.cs b/lib/netstd/Thrift/Transport/TTransportException.cs similarity index 77% rename from lib/netcore/Thrift/Transports/TTransportException.cs rename to lib/netstd/Thrift/Transport/TTransportException.cs index b7c42e33a87..760a178e6ce 100644 --- a/lib/netcore/Thrift/Transports/TTransportException.cs +++ b/lib/netstd/Thrift/Transport/TTransportException.cs @@ -15,7 +15,9 @@ // specific language governing permissions and limitations // under the License. -namespace Thrift.Transports +using System; + +namespace Thrift.Transport { // ReSharper disable once InconsistentNaming public class TTransportException : TException @@ -30,26 +32,26 @@ public enum ExceptionType Interrupted } - protected ExceptionType ExType; + public ExceptionType ExType { get; private set; } public TTransportException() { } - public TTransportException(ExceptionType exType) - : this() + public TTransportException(ExceptionType exType, Exception inner = null) + : base(string.Empty, inner) { ExType = exType; } - public TTransportException(ExceptionType exType, string message) - : base(message) + public TTransportException(ExceptionType exType, string message, Exception inner = null) + : base(message, inner) { ExType = exType; } - public TTransportException(string message) - : base(message) + public TTransportException(string message, Exception inner = null) + : base(message, inner) { } diff --git a/lib/netcore/Thrift/Transports/TTransportFactory.cs b/lib/netstd/Thrift/Transport/TTransportFactory.cs similarity index 92% rename from lib/netcore/Thrift/Transports/TTransportFactory.cs rename to lib/netstd/Thrift/Transport/TTransportFactory.cs index 26c3cc4714d..16e27ac8252 100644 --- a/lib/netcore/Thrift/Transports/TTransportFactory.cs +++ b/lib/netstd/Thrift/Transport/TTransportFactory.cs @@ -15,7 +15,7 @@ // specific language governing permissions and limitations // under the License. -namespace Thrift.Transports +namespace Thrift.Transport { /// /// From Mark Slee & Aditya Agarwal of Facebook: @@ -27,9 +27,9 @@ namespace Thrift.Transports // ReSharper disable once InconsistentNaming public class TTransportFactory { - public virtual TClientTransport GetTransport(TClientTransport trans) + public virtual TTransport GetTransport(TTransport trans) { return trans; } } -} \ No newline at end of file +} diff --git a/lib/netstd/Thrift/thrift.snk b/lib/netstd/Thrift/thrift.snk new file mode 100644 index 00000000000..97bc5812b81 Binary files /dev/null and b/lib/netstd/Thrift/thrift.snk differ diff --git a/lib/netcore/build.cmd b/lib/netstd/build.cmd similarity index 100% rename from lib/netcore/build.cmd rename to lib/netstd/build.cmd diff --git a/lib/netcore/build.sh b/lib/netstd/build.sh similarity index 100% rename from lib/netcore/build.sh rename to lib/netstd/build.sh diff --git a/lib/netcore/runtests.cmd b/lib/netstd/runtests.cmd similarity index 100% rename from lib/netcore/runtests.cmd rename to lib/netstd/runtests.cmd diff --git a/lib/netcore/runtests.sh b/lib/netstd/runtests.sh similarity index 100% rename from lib/netcore/runtests.sh rename to lib/netstd/runtests.sh diff --git a/lib/nodejs/CMakeLists.txt b/lib/nodejs/CMakeLists.txt new file mode 100644 index 00000000000..5059044b29a --- /dev/null +++ b/lib/nodejs/CMakeLists.txt @@ -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. +# + +if(NOT NODEJS_INSTALL_DIR) + if(IS_ABSOLUTE "${LIB_INSTALL_DIR}") + set(NODEJS_INSTALL_DIR "${LIB_INSTALL_DIR}/nodejs") + else() + set(NODEJS_INSTALL_DIR "${CMAKE_INSTALL_PREFIX}/${LIB_INSTALL_DIR}/nodejs") + endif() +endif() + +# Currently no doc +#if(IS_ABSOLUTE "${DOC_INSTALL_DIR}") +# set(NODEJS_DOC_INSTALL_DIR "${DOC_INSTALL_DIR}/nodejs") +#else() +# set(NODEJS_DOC_INSTALL_DIR "${CMAKE_INSTALL_PREFIX}/${DOC_INSTALL_DIR}/nodejs") +#endif() + +add_custom_target(ThriftNodeJS ALL + COMMENT "Installing NodeJS dependencies with npm" + COMMAND npm install + WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/../../" +) + +install(DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/lib/" + DESTINATION "${NODEJS_INSTALL_DIR}") +#install(DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/doc/" +# DESTINATION "${NODEJS_DOC_INSTALL_DIR}") diff --git a/lib/nodejs/Makefile.am b/lib/nodejs/Makefile.am index 68ea3ea48b1..67f6ff6a90a 100755 --- a/lib/nodejs/Makefile.am +++ b/lib/nodejs/Makefile.am @@ -32,12 +32,15 @@ check: deps cd $(top_srcdir) && $(NPM) test && $(NPM) run lint-tests && cd lib/nodejs clean-local: - $(RM) -r test/gen-nodejs + $(RM) -r test/gen-* $(RM) -r $(top_srcdir)/node_modules + $(RM) -r test/episodic-code-generation-test/gen* + $(RM) -r test/episodic-code-generation-test/node_modules EXTRA_DIST = \ examples \ lib \ test \ coding_standards.md \ + CMakeLists.txt \ README.md diff --git a/lib/nodejs/README.md b/lib/nodejs/README.md index 50acfbdbedd..c087440a180 100644 --- a/lib/nodejs/README.md +++ b/lib/nodejs/README.md @@ -68,3 +68,75 @@ Since JavaScript represents all numbers as doubles, int64 values cannot be accur ## Client and server examples Several example clients and servers are included in the thrift/lib/nodejs/examples folder and the cross language tutorial thrift/tutorial/nodejs folder. + +## Use on browsers + +You can use code generated with js:node on browsers with Webpack. Here is an example. + +thrift --gen js:node,ts,es6,with_ns + +```javascript +import * as thrift from 'thrift'; +import { MyServiceClient } from '../gen-nodejs/MyService'; + +let host = window.location.hostname; +let port = 443; +let opts = { + transport: thrift.TBufferedTransport, + protocol: thrift.TJSONProtocol, + headers: { + 'Content-Type': 'application/vnd.apache.thrift.json', + }, + https: true, + path: '/url/path', + useCORS: true, +}; + +let connection = thrift.createXHRConnection(host, port, opts); +let thriftClient = thrift.createXHRClient(MyServiceClient, connection); + +connection.on('error', (err) => { + console.error(err); +}); + +thriftClient.myService(param) + .then((result) => { + console.log(result); + }) + .catch((err) => { + .... + }); +``` + +Bundlers, like webpack, will use thrift/browser.js by default because of the +`"browser": "./lib/nodejs/lib/thrift/browser.js"` field in package.json. + +### Browser example with WebSocket, BufferedTransport and BinaryProtocol +```javascript +import thrift from 'thrift'; +import { MyServiceClient } from '../gen-nodejs/MyService'; + +const host = window.location.hostname; +const port = 9090; +const opts = { + transport: thrift.TBufferedTransport, + protocol: thrift.TBinaryProtocol +} +const connection = thrift.createWSConnection(host, port, opts); +connection.open(); +const thriftClient = thrift.createWSClient(MyServiceClient, connection); + +connection.on('error', (err) => { + console.error(err); +}); + +thriftClient.myService(param) + .then((result) => { + console.log(result); + }) + .catch((err) => { + .... + }); +``` + + diff --git a/lib/nodejs/examples/httpServer.py b/lib/nodejs/examples/httpServer.py index b712fcd7f95..76e9f4aa3c8 100644 --- a/lib/nodejs/examples/httpServer.py +++ b/lib/nodejs/examples/httpServer.py @@ -5,10 +5,12 @@ from thrift.protocol import TJSONProtocol from thrift.server import THttpServer + class HelloSvcHandler: - def hello_func(self): - print("Hello Called") - return "hello from Python" + def hello_func(self): + print("Hello Called") + return "hello from Python" + processor = HelloSvc.Processor(HelloSvcHandler()) protoFactory = TJSONProtocol.TJSONProtocolFactory() diff --git a/lib/nodejs/lib/thrift/binary_protocol.js b/lib/nodejs/lib/thrift/binary_protocol.js index b57c8c57635..af8836cf52c 100644 --- a/lib/nodejs/lib/thrift/binary_protocol.js +++ b/lib/nodejs/lib/thrift/binary_protocol.js @@ -33,6 +33,10 @@ var VERSION_MASK = -65536, // 0xffff0000 VERSION_1 = -2147418112, // 0x80010000 TYPE_MASK = 0x000000ff; +TBinaryProtocol.VERSION_MASK = VERSION_MASK; +TBinaryProtocol.VERSION_1 = VERSION_1; +TBinaryProtocol.TYPE_MASK = TYPE_MASK + function TBinaryProtocol(trans, strictRead, strictWrite) { this.trans = trans; this.strictRead = (strictRead !== undefined ? strictRead : false); @@ -302,8 +306,6 @@ TBinaryProtocol.prototype.getTransport = function() { TBinaryProtocol.prototype.skip = function(type) { switch (type) { - case Type.STOP: - return; case Type.BOOL: this.readBool(); break; diff --git a/lib/nodejs/lib/thrift/browser.js b/lib/nodejs/lib/thrift/browser.js index 67ce8535beb..0b06f0f1331 100644 --- a/lib/nodejs/lib/thrift/browser.js +++ b/lib/nodejs/lib/thrift/browser.js @@ -18,19 +18,32 @@ */ exports.Thrift = require('./thrift'); +var wsConnection = require('./ws_connection'); +exports.WSConnection = wsConnection.WSConnection; +exports.createWSConnection = wsConnection.createWSConnection; +exports.createWSClient = wsConnection.createWSClient; + var xhrConnection = require('./xhr_connection'); exports.XHRConnection = xhrConnection.XHRConnection; exports.createXHRConnection = xhrConnection.createXHRConnection; exports.createXHRClient = xhrConnection.createXHRClient; -exports.Multiplexer = require('./multiplexed_protocol').Multiplexer; -exports.TWebSocketTransport = require('./ws_transport'); +exports.Int64 = require('node-int64'); +exports.Q = require('q'); + +var mpxProtocol = require('./multiplexed_protocol'); +exports.Multiplexer = mpxProtocol.Multiplexer; + +/* + * Export transport and protocol so they can be used outside of a + * cassandra/server context + */ exports.TBufferedTransport = require('./buffered_transport'); exports.TFramedTransport = require('./framed_transport'); +exports.TWebSocketTransport = require('./ws_transport'); -exports.Protocol = exports.TJSONProtocol = require('./json_protocol'); +exports.Protocol = require('./json_protocol'); +exports.TJSONProtocol = require('./json_protocol'); exports.TBinaryProtocol = require('./binary_protocol'); exports.TCompactProtocol = require('./compact_protocol'); - -exports.Int64 = require('node-int64'); diff --git a/lib/nodejs/lib/thrift/buffered_transport.js b/lib/nodejs/lib/thrift/buffered_transport.js index a9e006e6ccb..113e21616e3 100644 --- a/lib/nodejs/lib/thrift/buffered_transport.js +++ b/lib/nodejs/lib/thrift/buffered_transport.js @@ -19,6 +19,7 @@ var binary = require('./binary'); var InputBufferUnderrunError = require('./input_buffer_underrun_error'); +var THeaderTransport = require('./header_transport'); module.exports = TBufferedTransport; @@ -33,6 +34,8 @@ function TBufferedTransport(buffer, callback) { this.onFlush = callback; }; +TBufferedTransport.prototype = new THeaderTransport(); + TBufferedTransport.prototype.reset = function() { this.inBuf = new Buffer(this.defaultReadBufferSize); this.readCursor = 0; diff --git a/lib/nodejs/lib/thrift/compact_protocol.js b/lib/nodejs/lib/thrift/compact_protocol.js index 5c531e5d60e..302a88d4d84 100644 --- a/lib/nodejs/lib/thrift/compact_protocol.js +++ b/lib/nodejs/lib/thrift/compact_protocol.js @@ -854,8 +854,6 @@ TCompactProtocol.prototype.zigzagToI64 = function(n) { TCompactProtocol.prototype.skip = function(type) { switch (type) { - case Type.STOP: - return; case Type.BOOL: this.readBool(); break; diff --git a/lib/nodejs/lib/thrift/connection.js b/lib/nodejs/lib/thrift/connection.js index b54545436d3..25e34ed4240 100644 --- a/lib/nodejs/lib/thrift/connection.js +++ b/lib/nodejs/lib/thrift/connection.js @@ -74,10 +74,7 @@ var Connection = exports.Connection = function(stream, options) { this.framePos = 0; this.frame = null; self.initialize_retry_vars(); - - self.offline_queue.forEach(function(data) { - self.connection.write(data); - }); + self.flush_offline_queue(); self.emit("connect"); }); @@ -177,6 +174,18 @@ Connection.prototype.initialize_retry_vars = function () { this.attempts = 0; }; +Connection.prototype.flush_offline_queue = function () { + var self = this; + var offline_queue = this.offline_queue; + + // Reset offline queue + this.offline_queue = []; + // Attempt to write queued items + offline_queue.forEach(function(data) { + self.write(data); + }); +}; + Connection.prototype.write = function(data) { if (!this.connected) { this.offline_queue.push(data); @@ -221,6 +230,11 @@ Connection.prototype.connection_gone = function () { }); this.retry_timer = setTimeout(function () { + if (self.connection.destroyed) { + self.retry_timer = null; + return; + } + log.debug("Retrying connection..."); self.retry_totaltime += self.retry_delay; @@ -242,7 +256,11 @@ Connection.prototype.connection_gone = function () { }; exports.createConnection = function(host, port, options) { - var stream = net.createConnection(port, host); + var stream = net.createConnection( { + port: port, + host: host, + timeout: options.connect_timeout || options.timeout || 0 + }); var connection = new Connection(stream, options); connection.host = host; connection.port = port; @@ -306,10 +324,7 @@ var StdIOConnection = exports.StdIOConnection = function(command, options) { this.frame = null; this.connected = true; - self.offline_queue.forEach(function(data) { - self.connection.write(data); - }); - + self.flush_offline_queue(); this.connection.addListener("error", function(err) { self.emit("error", err); @@ -354,6 +369,18 @@ StdIOConnection.prototype.end = function() { this.connection.end(); }; +StdIOConnection.prototype.flush_offline_queue = function () { + var self = this; + var offline_queue = this.offline_queue; + + // Reset offline queue + this.offline_queue = []; + // Attempt to write queued items + offline_queue.forEach(function(data) { + self.write(data); + }); +}; + StdIOConnection.prototype.write = function(data) { if (!this.connected) { this.offline_queue.push(data); diff --git a/lib/nodejs/lib/thrift/framed_transport.js b/lib/nodejs/lib/thrift/framed_transport.js index 6947925acd4..f7daa3f1c08 100644 --- a/lib/nodejs/lib/thrift/framed_transport.js +++ b/lib/nodejs/lib/thrift/framed_transport.js @@ -19,6 +19,7 @@ var binary = require('./binary'); var InputBufferUnderrunError = require('./input_buffer_underrun_error'); +var THeaderTransport = require('./header_transport'); module.exports = TFramedTransport; @@ -30,6 +31,8 @@ function TFramedTransport(buffer, callback) { this.onFlush = callback; }; +TFramedTransport.prototype = new THeaderTransport(); + TFramedTransport.receiver = function(callback, seqid) { var residual = null; diff --git a/lib/nodejs/lib/thrift/header_protocol.js b/lib/nodejs/lib/thrift/header_protocol.js new file mode 100644 index 00000000000..8971751fb75 --- /dev/null +++ b/lib/nodejs/lib/thrift/header_protocol.js @@ -0,0 +1,258 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +var util = require('util'); +var TBinaryProtocol = require('./binary_protocol'); +var TCompactProtocol = require('./compact_protocol'); +var THeaderTransport = require('./header_transport'); + +var ProtocolMap = {}; +ProtocolMap[THeaderTransport.SubprotocolId.BINARY] = TBinaryProtocol; +ProtocolMap[THeaderTransport.SubprotocolId.COMPACT] = TCompactProtocol; + +module.exports = THeaderProtocol; + +function THeaderProtocolError(message) { + Error.call(this); + if (Error.captureStackTrace !== undefined) { + Error.captureStackTrace(this, this.constructor); + } + this.name = this.constructor.name; + this.message = message; +} + +util.inherits(THeaderProtocolError, Error); + +/** + * A framed protocol with headers. + * + * THeaderProtocol frames other Thrift protocols and adds support for + * optional out-of-band headers. The currently supported subprotocols are + * TBinaryProtocol and TCompactProtocol. It can currently only be used with + * transports that inherit THeaderTransport. + * + * THeaderProtocol does not currently support THTTPServer, TNonblockingServer, + * or TProcessPoolServer. + * + * See doc/specs/HeaderFormat.md for details of the wire format. + */ +function THeaderProtocol(trans) { + if (!(trans instanceof THeaderTransport)) { + throw new THeaderProtocolError( + 'Only transports that inherit THeaderTransport can be' + + ' used with THeaderProtocol' + ); + } + this.trans = trans; + this.setProtocol(); +}; + +THeaderProtocol.prototype.flush = function() { + // Headers must be written prior to flushing because because + // you need to calculate the length of the payload for the length + // field of the header + this.trans.writeHeaders(); + return this.trans.flush(); +}; + +THeaderProtocol.prototype.writeMessageBegin = function(name, type, seqid) { + return this.protocol.writeMessageBegin(name, type, seqid); +}; + +THeaderProtocol.prototype.writeMessageEnd = function() { + return this.protocol.writeMessageEnd(); +}; + +THeaderProtocol.prototype.writeStructBegin = function(name) { + return this.protocol.writeStructBegin(name); +}; + +THeaderProtocol.prototype.writeStructEnd = function() { + return this.protocol.writeStructEnd(); +}; + +THeaderProtocol.prototype.writeFieldBegin = function(name, type, id) { + return this.protocol.writeFieldBegin(name, type, id); +} + +THeaderProtocol.prototype.writeFieldEnd = function() { + return this.protocol.writeFieldEnd(); +}; + +THeaderProtocol.prototype.writeFieldStop = function() { + return this.protocol.writeFieldStop(); +}; + +THeaderProtocol.prototype.writeMapBegin = function(ktype, vtype, size) { + return this.protocol.writeMapBegin(ktype, vtype, size); +}; + +THeaderProtocol.prototype.writeMapEnd = function() { + return this.protocol.writeMapEnd(); +}; + +THeaderProtocol.prototype.writeListBegin = function(etype, size) { + return this.protocol.writeListBegin(etype, size); +}; + +THeaderProtocol.prototype.writeListEnd = function() { + return this.protocol.writeListEnd(); +}; + +THeaderProtocol.prototype.writeSetBegin = function(etype, size) { + return this.protocol.writeSetBegin(etype, size); +}; + +THeaderProtocol.prototype.writeSetEnd = function() { + return this.protocol.writeSetEnd(); +}; + +THeaderProtocol.prototype.writeBool = function(b) { + return this.protocol.writeBool(b); +}; + +THeaderProtocol.prototype.writeByte = function(b) { + return this.protocol.writeByte(b); +}; + +THeaderProtocol.prototype.writeI16 = function(i16) { + return this.protocol.writeI16(i16); +}; + +THeaderProtocol.prototype.writeI32 = function(i32) { + return this.protocol.writeI32(i32); +}; + +THeaderProtocol.prototype.writeI64 = function(i64) { + return this.protocol.writeI64(i64); +}; + +THeaderProtocol.prototype.writeDouble = function(dub) { + return this.protocol.writeDouble(dub); +}; + +THeaderProtocol.prototype.writeStringOrBinary = function(name, encoding, arg) { + return this.protocol.writeStringOrBinary(name, encoding, arg); +}; + +THeaderProtocol.prototype.writeString = function(arg) { + return this.protocol.writeString(arg); +}; + +THeaderProtocol.prototype.writeBinary = function(arg) { + return this.protocol.writeBinary(arg); +}; + +THeaderProtocol.prototype.readMessageBegin = function() { + this.trans.readHeaders(); + this.setProtocol(); + return this.protocol.readMessageBegin(); +}; + +THeaderProtocol.prototype.readMessageEnd = function() { + return this.protocol.readMessageEnd(); +}; + +THeaderProtocol.prototype.readStructBegin = function() { + return this.protocol.readStructBegin(); +}; + +THeaderProtocol.prototype.readStructEnd = function() { + return this.protocol.readStructEnd(); +}; + +THeaderProtocol.prototype.readFieldBegin = function() { + return this.protocol.readFieldBegin(); +}; + +THeaderProtocol.prototype.readFieldEnd = function() { + return this.protocol.readFieldEnd(); +}; + +THeaderProtocol.prototype.readMapBegin = function() { + return this.protocol.readMapBegin(); +}; + +THeaderProtocol.prototype.readMapEnd = function() { + return this.protocol.readMapEnd(); +}; + +THeaderProtocol.prototype.readListBegin = function() { + return this.protocol.readListBegin(); +}; + +THeaderProtocol.prototype.readListEnd = function() { + return this.protocol.readListEnd(); +}; + +THeaderProtocol.prototype.readSetBegin = function() { + return this.protocol.readSetBegin(); +}; + +THeaderProtocol.prototype.readSetEnd = function() { + return this.protocol.readSetEnd(); +}; + +THeaderProtocol.prototype.readBool = function() { + return this.protocol.readBool(); +}; + +THeaderProtocol.prototype.readByte = function() { + return this.protocol.readByte(); +}; + +THeaderProtocol.prototype.readI16 = function() { + return this.protocol.readI16(); +}; + +THeaderProtocol.prototype.readI32 = function() { + return this.protocol.readI32(); +}; + +THeaderProtocol.prototype.readI64 = function() { + return this.protocol.readI64(); +}; + +THeaderProtocol.prototype.readDouble = function() { + return this.protocol.readDouble(); +}; + +THeaderProtocol.prototype.readBinary = function() { + return this.protocol.readBinary(); +}; + +THeaderProtocol.prototype.readString = function() { + return this.protocol.readString(); +}; + +THeaderProtocol.prototype.getTransport = function() { + return this.trans; +}; + +THeaderProtocol.prototype.skip = function(type) { + return this.protocol.skip(type); +}; + +THeaderProtocol.prototype.setProtocol = function(subProtocolId) { + var subProtocolId = this.trans.getProtocolId(); + if (!ProtocolMap[subProtocolId]) { + throw new THeaderProtocolError('Headers not supported for protocol ' + subProtocolId); + } + + this.protocol = new ProtocolMap[subProtocolId](this.trans); +}; diff --git a/lib/nodejs/lib/thrift/header_transport.js b/lib/nodejs/lib/thrift/header_transport.js new file mode 100644 index 00000000000..ec8624b02a7 --- /dev/null +++ b/lib/nodejs/lib/thrift/header_transport.js @@ -0,0 +1,341 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +var util = require('util'); +var TCompactProtocol = require('./compact_protocol'); +var TBinaryProtocol = require('./binary_protocol'); +var InputBufferUnderrunError = require('./input_buffer_underrun_error'); + +function THeaderTransportError(message) { + Error.call(this); + if (Error.captureStackTrace !== undefined) { + Error.captureStackTrace(this, this.constructor); + } + this.name = this.constructor.name; + this.message = message; +} + +util.inherits(THeaderTransportError, Error); + +module.exports = THeaderTransport; + +// from HeaderFormat.md +var COMPACT_PROTOCOL_OFFSET = 0; +var COMPACT_PROTOCOL_VERSION_OFFSET = 1; +var FRAME_SIZE_OFFSET = 0; +var HEADER_MAGIC_OFFSET = 32 / 8; +var FLAGS_OFFSET = 48 / 8; +var SEQID_OFFSET = 64 / 8; +var HEADER_SIZE_OFFSET = 96 / 8; +var HEADER_START_OFFSET = 112 / 8; + +var HEADER_MAGIC = 0x0FFF; + +var TINFO_HEADER_KEY_VALUE_TYPE = 0x01; +var MAX_FRAME_SIZE = 0x3FFFFFFF; + + // A helper class for reading/writing varints. Uses + // TCompactProtocol under the hood +function VarintHelper(readBuffer) { + var TBufferedTransport = require('./buffered_transport'); + this.outputBuffer = null; + var _this = this; + this.transport = new TBufferedTransport(null, function(output) { + _this.outputBuffer = output; + }); + + this.transport.inBuf = readBuffer || Buffer.alloc(0); + this.transport.writeCursor = this.transport.inBuf.length; + this.protocol = new TCompactProtocol(this.transport); +}; + +VarintHelper.prototype.readVarint32 = function() { + return this.protocol.readVarint32(); +}; + +VarintHelper.prototype.writeVarint32 = function(i) { + this.protocol.writeVarint32(i); +}; + +VarintHelper.prototype.readString = function() { + return this.protocol.readString(); +}; + +VarintHelper.prototype.writeString = function(str) { + this.protocol.writeString(str); +} + +VarintHelper.prototype.getOutCount = function() { + return this.transport.outCount; +}; + +VarintHelper.prototype.write = function(str) { + this.transport.write(str); +}; + +VarintHelper.prototype.toBuffer = function() { + this.transport.flush(); + return this.outputBuffer; +}; + +// from lib/cpp/src/thrift/protocol/TProtocolTypes.h +THeaderTransport.SubprotocolId = { + BINARY: 0, + JSON: 1, + COMPACT: 2, +}; + +/** + An abstract transport used as a prototype for other transports + to enable reading/writing theaders. This should NOT be used as a standalone transport + The methods in this transport are called by THeaderProtocol, which will call readHeaders/writeHeaders + in the read/writeMessageBegin methods and parse/write headers to/from a request + prior to reading/writing. + + The reason this is not a standalone transport type is because different transport types + have their own individual static receiver methods that are called prior to instantiation. + There doesn't seem to be a way for THeaderTransport to know which receiver method to use + without reworking the server API. + + For reading headers from a request, the parsed headers can be retrieved via + getReadHeader. Similarly, you can set headers to be written on the client via + setWriteHeader. + */ +function THeaderTransport() { + this.maxFrameSize = MAX_FRAME_SIZE; + this.protocolId = THeaderTransport.SubprotocolId.BINARY; + this.rheaders = {}; + this.wheaders = {}; + this.inBuf = Buffer.alloc(0); + this.outCount = 0; + this.flags = null; + this.seqid = 0; + this.shouldWriteHeaders = true; +}; + +var validateHeaders = function(key, value) { + if (typeof key !== 'string' || typeof value !== 'string') { + throw new THeaderTransportError('Header key and values must be strings'); + } +}; + +var validateProtocolId = function(protocolId) { + var protocols = Object.keys(THeaderTransport.SubprotocolId); + for (var i = 0; i < protocols.length; i++) { + if (protocolId === THeaderTransport.SubprotocolId[protocols[i]]) return true; + } + + throw new Error(protocolId + ' is not a valid protocol id'); +}; + +THeaderTransport.prototype.setSeqId = function(seqid) { + this.seqid = seqid; +}; + +THeaderTransport.prototype.getSeqId = function(seqid) { + return this.seqid; +}; + +THeaderTransport.prototype.setFlags = function(flags) { + this.flags = flags; +}; + +THeaderTransport.prototype.getReadHeaders = function() { + return this.rheaders; +}; + +THeaderTransport.prototype.setReadHeader = function(key, value) { + validateHeaders(key, value); + this.rheaders[key] = value; +}; + +THeaderTransport.prototype.clearReadHeaders = function() { + this.rheaders = {}; +}; + +THeaderTransport.prototype.getWriteHeaders = function() { + return this.wheaders; +}; + +THeaderTransport.prototype.setWriteHeader = function(key, value) { + validateHeaders(key, value); + this.wheaders[key] = value; +}; + +THeaderTransport.prototype.clearWriteHeaders = function() { + this.wheaders = {}; +}; + +THeaderTransport.prototype.setMaxFrameSize = function(frameSize) { + this.maxFrameSize = frameSize; +}; + +THeaderTransport.prototype.setProtocolId = function(protocolId) { + validateProtocolId(protocolId); + this.protocolId = protocolId; +}; + +THeaderTransport.prototype.getProtocolId = function() { + return this.protocolId; +}; + +var isUnframedBinary = function(readBuffer) { + var version = readBuffer.readInt32BE(); + return (version & TBinaryProtocol.VERSION_MASK) === TBinaryProtocol.VERSION_1; +} + +var isUnframedCompact = function(readBuffer) { + var protocolId = readBuffer.readInt8(COMPACT_PROTOCOL_OFFSET); + var version = readBuffer.readInt8(COMPACT_PROTOCOL_VERSION_OFFSET); + return protocolId === TCompactProtocol.PROTOCOL_ID && + (version & TCompactProtocol.VERSION_MASK) === TCompactProtocol.VERSION_N; +} + +THeaderTransport.prototype.readHeaders = function() { + var readBuffer = this.inBuf; + + var isUnframed = false; + if (isUnframedBinary(readBuffer)) { + this.setProtocolId(THeaderTransport.SubprotocolId.BINARY); + isUnframed = true; + } + + if (isUnframedCompact(readBuffer)) { + this.setProtocolId(THeaderTransport.SubprotocolId.COMPACT); + isUnframed = true; + } + + if (isUnframed) { + this.shouldWriteHeaders = false; + return; + } + + var frameSize = readBuffer.readInt32BE(FRAME_SIZE_OFFSET); + if (frameSize > this.maxFrameSize) { + throw new THeaderTransportError('Frame exceeds maximum frame size'); + } + + var headerMagic = readBuffer.readInt16BE(HEADER_MAGIC_OFFSET); + this.shouldWriteHeaders = headerMagic === HEADER_MAGIC; + if (!this.shouldWriteHeaders) { + return; + } + + this.setFlags(readBuffer.readInt16BE(FLAGS_OFFSET)); + this.setSeqId(readBuffer.readInt32BE(SEQID_OFFSET)); + var headerSize = readBuffer.readInt16BE(HEADER_SIZE_OFFSET) * 4; + var endOfHeaders = HEADER_START_OFFSET + headerSize; + if (endOfHeaders > readBuffer.length) { + throw new THeaderTransportError('Header size is greater than frame size'); + } + + var headerBuffer = Buffer.alloc(headerSize); + readBuffer.copy(headerBuffer, 0, HEADER_START_OFFSET, endOfHeaders); + + var varintHelper = new VarintHelper(headerBuffer); + this.setProtocolId(varintHelper.readVarint32()); + var transformCount = varintHelper.readVarint32(); + if (transformCount > 0) { + throw new THeaderTransportError('Transforms are not yet supported'); + } + + while (true) { + try { + var headerType = varintHelper.readVarint32(); + if (headerType !== TINFO_HEADER_KEY_VALUE_TYPE) { + break; + } + + var numberOfHeaders = varintHelper.readVarint32(); + for (var i = 0; i < numberOfHeaders; i++) { + var key = varintHelper.readString(); + var value = varintHelper.readString(); + this.setReadHeader(key, value); + } + } catch (e) { + if (e instanceof InputBufferUnderrunError) { + break; + } + throw e; + } + } + + // moves the read cursor past the headers + this.read(endOfHeaders); + return this.getReadHeaders(); +}; + +THeaderTransport.prototype.writeHeaders = function() { + // only write headers on the server if the client contained headers + if (!this.shouldWriteHeaders) { + return; + } + var headers = this.getWriteHeaders(); + + var varintWriter = new VarintHelper(); + varintWriter.writeVarint32(this.protocolId); + varintWriter.writeVarint32(0); // transforms not supported + + // writing info header key values + var headerKeys = Object.keys(headers); + if (headerKeys.length > 0) { + varintWriter.writeVarint32(TINFO_HEADER_KEY_VALUE_TYPE); + varintWriter.writeVarint32(headerKeys.length); + for (var i = 0; i < headerKeys.length; i++) { + var key = headerKeys[i]; + var value = headers[key]; + + varintWriter.writeString(key); + varintWriter.writeString(value); + } + } + var headerSizeWithoutPadding = varintWriter.getOutCount(); + var paddingNeeded = (4 - (headerSizeWithoutPadding % 4)) % 4; + + var headerSize = Buffer.alloc(2); + headerSize.writeInt16BE(Math.floor((headerSizeWithoutPadding + paddingNeeded) / 4)); + + var paddingBuffer = Buffer.alloc(paddingNeeded); + paddingBuffer.fill(0x00); + varintWriter.write(paddingBuffer); + var headerContentBuffer = varintWriter.toBuffer(); + var frameSize = Buffer.alloc(4); + frameSize.writeInt32BE(10 + this.outCount + headerContentBuffer.length); + var headerMagic = Buffer.alloc(2); + headerMagic.writeInt16BE(HEADER_MAGIC); + + // flags are not yet supported, so write a zero + var flags = Buffer.alloc(2); + flags.writeInt16BE(0); + + var seqid = Buffer.alloc(4); + seqid.writeInt32BE(this.getSeqId()); + + var headerBuffer = Buffer.concat([ + frameSize, + headerMagic, + flags, + seqid, + headerSize, + headerContentBuffer, + ]); + + this.outBuffers.unshift(headerBuffer); + this.outCount += headerBuffer.length; +}; diff --git a/lib/nodejs/lib/thrift/http_connection.js b/lib/nodejs/lib/thrift/http_connection.js index 3c2ab0f535d..17e0d0c2995 100644 --- a/lib/nodejs/lib/thrift/http_connection.js +++ b/lib/nodejs/lib/thrift/http_connection.js @@ -217,7 +217,7 @@ HttpConnection.prototype.write = function(data) { var opts = self.nodeOptions; opts.headers["Content-length"] = data.length; if (!opts.headers["Content-Type"]) - opts.headers["Content-Type"] = "application/x-thrift"; + opts.headers["Content-Type"] = "application/x-thrift"; var req = (self.https) ? https.request(opts, self.responseCallback) : http.request(opts, self.responseCallback); @@ -253,7 +253,10 @@ exports.createHttpClient = createClient function THTTPException(response) { thrift.TApplicationException.call(this); - Error.captureStackTrace(this, this.constructor); + if (Error.captureStackTrace !== undefined) { + Error.captureStackTrace(this, this.constructor); + } + this.name = this.constructor.name; this.statusCode = response.statusCode; this.response = response; diff --git a/lib/nodejs/lib/thrift/index.js b/lib/nodejs/lib/thrift/index.js index b09953d71ec..00f21743d43 100644 --- a/lib/nodejs/lib/thrift/index.js +++ b/lib/nodejs/lib/thrift/index.js @@ -58,17 +58,19 @@ exports.createWebServer = web_server.createWebServer; exports.Int64 = require('node-int64'); exports.Q = require('q'); -var mprocessor = require('./multiplexed_processor'); -var mprotocol = require('./multiplexed_protocol'); -exports.Multiplexer = mprotocol.Multiplexer; -exports.MultiplexedProcessor = mprocessor.MultiplexedProcessor; +var mpxProcessor = require('./multiplexed_processor'); +var mpxProtocol = require('./multiplexed_protocol'); +exports.MultiplexedProcessor = mpxProcessor.MultiplexedProcessor; +exports.Multiplexer = mpxProtocol.Multiplexer; /* * Export transport and protocol so they can be used outside of a * cassandra/server context */ -exports.TFramedTransport = require('./framed_transport'); exports.TBufferedTransport = require('./buffered_transport'); -exports.TBinaryProtocol = require('./binary_protocol'); +exports.TFramedTransport = require('./framed_transport'); + exports.TJSONProtocol = require('./json_protocol'); +exports.TBinaryProtocol = require('./binary_protocol'); exports.TCompactProtocol = require('./compact_protocol'); +exports.THeaderProtocol = require('./header_protocol'); diff --git a/lib/nodejs/lib/thrift/input_buffer_underrun_error.js b/lib/nodejs/lib/thrift/input_buffer_underrun_error.js index 72555e51642..e424540a09c 100644 --- a/lib/nodejs/lib/thrift/input_buffer_underrun_error.js +++ b/lib/nodejs/lib/thrift/input_buffer_underrun_error.js @@ -22,7 +22,9 @@ module.exports = InputBufferUnderrunError; function InputBufferUnderrunError(message) { Error.call(this); - Error.captureStackTrace(this, this.constructor); + if (Error.captureStackTrace !== undefined) { + Error.captureStackTrace(this, this.constructor); + } this.name = this.constructor.name; this.message = message; }; diff --git a/lib/nodejs/lib/thrift/json_protocol.js b/lib/nodejs/lib/thrift/json_protocol.js index 727a3b2ffb1..7e2b7c90891 100644 --- a/lib/nodejs/lib/thrift/json_protocol.js +++ b/lib/nodejs/lib/thrift/json_protocol.js @@ -738,8 +738,6 @@ TJSONProtocol.prototype.getTransport = function() { */ TJSONProtocol.prototype.skip = function(type) { switch (type) { - case Type.STOP: - return; case Type.BOOL: this.readBool(); break; diff --git a/lib/nodejs/lib/thrift/server.js b/lib/nodejs/lib/thrift/server.js index e124acceca8..16b74eaafc4 100644 --- a/lib/nodejs/lib/thrift/server.js +++ b/lib/nodejs/lib/thrift/server.js @@ -23,6 +23,7 @@ var tls = require('tls'); var TBufferedTransport = require('./buffered_transport'); var TBinaryProtocol = require('./binary_protocol'); +var THeaderProtocol = require('./header_protocol'); var InputBufferUnderrunError = require('./input_buffer_underrun_error'); /** @@ -43,14 +44,23 @@ exports.createMultiplexServer = function(processor, options) { }); stream.on('data', transport.receiver(function(transportWithData) { var input = new protocol(transportWithData); - var output = new protocol(new transport(undefined, function(buf) { + var outputCb = function(buf) { try { stream.write(buf); } catch (err) { self.emit('error', err); stream.end(); } - })); + }; + + var output = new protocol(new transport(undefined, outputCb)); + // Read and write need to be performed on the same transport + // for THeaderProtocol because we should only respond with + // headers if the request contains headers + if (protocol === THeaderProtocol) { + output = input; + output.trans.onFlush = outputCb; + } try { do { diff --git a/lib/nodejs/lib/thrift/thrift.js b/lib/nodejs/lib/thrift/thrift.js index f2b2896723a..f728eacbe48 100644 --- a/lib/nodejs/lib/thrift/thrift.js +++ b/lib/nodejs/lib/thrift/thrift.js @@ -49,7 +49,10 @@ exports.TException = TException; function TException(message) { Error.call(this); - Error.captureStackTrace(this, this.constructor); + if (Error.captureStackTrace !== undefined) { + Error.captureStackTrace(this, this.constructor); + } + this.name = this.constructor.name; this.message = message; }; @@ -73,7 +76,10 @@ exports.TApplicationException = TApplicationException; function TApplicationException(type, message) { TException.call(this); - Error.captureStackTrace(this, this.constructor); + if (Error.captureStackTrace !== undefined) { + Error.captureStackTrace(this, this.constructor); + } + this.type = type || TApplicationExceptionType.UNKNOWN; this.name = this.constructor.name; this.message = message; @@ -149,7 +155,10 @@ exports.TProtocolException = TProtocolException; function TProtocolException(type, message) { Error.call(this); - Error.captureStackTrace(this, this.constructor); + if (Error.captureStackTrace !== undefined) { + Error.captureStackTrace(this, this.constructor); + } + this.name = this.constructor.name; this.type = type; this.message = message; diff --git a/lib/nodejs/lib/thrift/ws_connection.js b/lib/nodejs/lib/thrift/ws_connection.js index 052cbd4e994..8ee8f6eca18 100644 --- a/lib/nodejs/lib/thrift/ws_connection.js +++ b/lib/nodejs/lib/thrift/ws_connection.js @@ -17,18 +17,16 @@ * under the License. */ var util = require('util'); -var WebSocket = require('ws'); +var WebSocket = require('isomorphic-ws'); var EventEmitter = require("events").EventEmitter; var thrift = require('./thrift'); -var ttransport = require('./transport'); -var tprotocol = require('./protocol'); var TBufferedTransport = require('./buffered_transport'); var TJSONProtocol = require('./json_protocol'); var InputBufferUnderrunError = require('./input_buffer_underrun_error'); var createClient = require('./create_client'); - +var jsEnv = require('browser-or-node'); exports.WSConnection = WSConnection; /** @@ -69,7 +67,7 @@ exports.WSConnection = WSConnection; * @throws {error} Exceptions other than ttransport.InputBufferUnderrunError are rethrown * @event {error} The "error" event is fired when a Node.js error event occurs during * request or response processing, in which case the node error is passed on. An "error" - * event may also be fired when the connectison can not map a response back to the + * event may also be fired when the connection can not map a response back to the * appropriate client (an internal error), generating a TApplicationException. * @classdesc WSConnection objects provide Thrift end point transport * semantics implemented using Websockets. @@ -80,7 +78,6 @@ function WSConnection(host, port, options) { EventEmitter.call(this); //Set configuration - var self = this; this.options = options || {}; this.host = host; this.port = port; @@ -113,14 +110,13 @@ WSConnection.prototype.__reset = function() { }; WSConnection.prototype.__onOpen = function() { - var self = this; this.emit("open"); if (this.send_pending.length > 0) { //If the user made calls before the connection was fully //open, send them now this.send_pending.forEach(function(data) { - self.socket.send(data); - }); + this.socket.send(data); + }, this); this.send_pending = []; } }; @@ -184,7 +180,7 @@ WSConnection.prototype.__decodeCallback = function(transport_with_data) { }; WSConnection.prototype.__onData = function(data) { - if (Object.prototype.toString.call(data) == "[object ArrayBuffer]") { + if (Object.prototype.toString.call(data) === "[object ArrayBuffer]") { data = new Uint8Array(data); } var buf = new Buffer(data); @@ -207,7 +203,7 @@ WSConnection.prototype.__onError = function(evt) { * @returns {boolean} */ WSConnection.prototype.isOpen = function() { - return this.socket && this.socket.readyState == this.socket.OPEN; + return this.socket && this.socket.readyState === this.socket.OPEN; }; /** @@ -215,11 +211,15 @@ WSConnection.prototype.isOpen = function() { */ WSConnection.prototype.open = function() { //If OPEN/CONNECTING/CLOSING ignore additional opens - if (this.socket && this.socket.readyState != this.socket.CLOSED) { + if (this.socket && this.socket.readyState !== this.socket.CLOSED) { return; } //If there is no socket or the socket is closed: - this.socket = new WebSocket(this.uri(), "", this.wsOptions); + if (jsEnv.isBrowser) { + this.socket = new WebSocket(this.uri()); + } else { + this.socket = new WebSocket(this.uri(), "", this.wsOptions); + } this.socket.binaryType = 'arraybuffer'; this.socket.onopen = this.__onOpen.bind(this); this.socket.onmessage = this.__onMessage.bind(this); @@ -245,8 +245,8 @@ WSConnection.prototype.uri = function() { var host = this.host; // avoid port if default for schema - if (this.port && (('wss' == schema && this.port != 443) || - ('ws' == schema && this.port != 80))) { + if (this.port && (('wss' === schema && this.port !== 443) || + ('ws' === schema && this.port !== 80))) { port = ':' + this.port; } diff --git a/lib/nodejs/lib/thrift/ws_transport.js b/lib/nodejs/lib/thrift/ws_transport.js index 3513b84be8d..4cf62b9f9bf 100644 --- a/lib/nodejs/lib/thrift/ws_transport.js +++ b/lib/nodejs/lib/thrift/ws_transport.js @@ -83,8 +83,8 @@ TWebSocketTransport.prototype.__onOpen = function() { //If the user made calls before the connection was fully //open, send them now this.send_pending.forEach(function(elem) { - this.socket.send(elem.buf); - this.callbacks.push((function() { + self.socket.send(elem.buf); + self.callbacks.push((function() { var clientCallback = elem.cb; return function(msg) { self.setRecvBuffer(msg); diff --git a/lib/nodejs/test/client.js b/lib/nodejs/test/client.js index 49e3a5ec935..31ea06e2f0d 100644 --- a/lib/nodejs/test/client.js +++ b/lib/nodejs/test/client.js @@ -69,6 +69,11 @@ if (program.transport === "http") { type = "http"; } +if (program.transport === "websocket") { + program.transport = "buffered"; + type = "websocket"; +} + const options = { transport: helpers.transports[program.transport], protocol: helpers.protocols[program.protocol] diff --git a/lib/nodejs/test/episodic-code-generation-test/client.js b/lib/nodejs/test/episodic-code-generation-test/client.js new file mode 100644 index 00000000000..55dc70269ca --- /dev/null +++ b/lib/nodejs/test/episodic-code-generation-test/client.js @@ -0,0 +1,77 @@ +#!/usr/bin/env node + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +const assert = require("assert"); +const test = require("tape"); +const thrift = require("thrift"); +const program = require("commander"); + +program + .option("--host ", "Set the thrift server host to connect", "localhost") + .option("--port ", "Set the thrift server port number to connect", 9090) + .parse(process.argv); + +const Service = require("./gen-2/second-episode/gen-nodejs/Service"); +const Types = require("types-package/first-episode/Types_types"); + +const host = program.host; +const port = program.port; + +const options = { + transport: thrift.TBufferedTransport, + protocol: thrift.TJSONProtocol +}; + +const connection = thrift.createConnection(host, port, options); +const testDriver = function(client, callback) { + test("NodeJS episodic compilation client-server test", function(assert) { + const type1Object = new Types.Type1(); + type1Object.number = 42; + type1Object.message = "The answer"; + client.testEpisode(type1Object, function(err, response) { + assert.error(err, "no callback error"); + assert.equal(response.number, type1Object.number + 1); + assert.equal( + response.message, + type1Object.message + " [Hello from the server]" + ); + assert.end(); + callback("Server successfully tested"); + }); + }); +}; + +connection.on("error", function(err) { + assert(false, err); +}); + +const client = thrift.createClient(Service, connection); + +runTests(); + +function runTests() { + testDriver(client, function(status) { + console.log(status); + connection.destroy(); + }); +} + +exports.expressoTest = function() {}; diff --git a/lib/nodejs/test/episodic-code-generation-test/episodic_compilation.package.json b/lib/nodejs/test/episodic-code-generation-test/episodic_compilation.package.json new file mode 100644 index 00000000000..7a78b4beb85 --- /dev/null +++ b/lib/nodejs/test/episodic-code-generation-test/episodic_compilation.package.json @@ -0,0 +1,3 @@ +{ + "name": "types-package" +} diff --git a/lib/nodejs/test/episodic-code-generation-test/server.js b/lib/nodejs/test/episodic-code-generation-test/server.js new file mode 100644 index 00000000000..2917b681c84 --- /dev/null +++ b/lib/nodejs/test/episodic-code-generation-test/server.js @@ -0,0 +1,50 @@ +#!/usr/bin/env node + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * 'License'); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * 'AS IS' BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +const thrift = require("../../lib/thrift"); +const program = require("commander"); + +program + .option("--port ", "Set the thrift server port", 9090) + .parse(process.argv); + +const Service = require("./gen-2/second-episode/gen-nodejs/Service"); +const Types = require("types-package/first-episode/Types_types"); + +const port = program.port; + +const options = { + transport: thrift.TBufferedTransport, + protocol: thrift.TJSONProtocol +}; + +const ServiceHandler = { + testEpisode: function(receivedType1Object) { + const type1Object = new Types.Type1(); + type1Object.number = receivedType1Object.number + 1; + type1Object.message = + receivedType1Object.message + " [Hello from the server]"; + return type1Object; + } +}; + +const server = thrift.createServer(Service, ServiceHandler, options); +server.listen(port); diff --git a/lib/nodejs/test/header.test.js b/lib/nodejs/test/header.test.js new file mode 100644 index 00000000000..efd7f81d5e5 --- /dev/null +++ b/lib/nodejs/test/header.test.js @@ -0,0 +1,78 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +const TFramedTransport = require("../lib/thrift/framed_transport"); +const THeaderTransport = require("../lib/thrift/header_transport"); +const THeaderProtocol = require("../lib/thrift/header_protocol"); +const thrift = require("../lib/thrift"); +const fs = require("fs"); +const test = require("tape"); +const path = require("path"); + +const headerPayload = fs.readFileSync( + path.join(__dirname, "test_header_payload") +); + +const cases = { + "Should read headers from payload": function(assert) { + const transport = new TFramedTransport(); + transport.inBuf = Buffer.from(headerPayload); + + const headers = transport.readHeaders(); + assert.equals(headers.Parent, "shoobar"); + assert.equals(headers.Trace, "abcde"); + assert.end(); + }, + "Should read headers when reading message begin": function(assert) { + const transport = new TFramedTransport(); + transport.inBuf = Buffer.from(headerPayload); + const protocol = new THeaderProtocol(transport); + const result = protocol.readMessageBegin(); + + const headers = transport.getReadHeaders(); + assert.equals(headers.Parent, "shoobar"); + assert.equals(headers.Trace, "abcde"); + assert.equals(result.fname, "add"); + assert.equals(result.mtype, thrift.Thrift.MessageType.CALL); + assert.end(); + }, + "Should be able to write headers": function(assert) { + const writeTransport = new TFramedTransport(); + writeTransport.setProtocolId(THeaderTransport.SubprotocolId.BINARY); + writeTransport.setWriteHeader("Hihihihi", "hohohoho"); + writeTransport.setWriteHeader("boobooboo", "fooshoopoo"); + writeTransport.setWriteHeader("a", "z"); + writeTransport.writeHeaders(); + const writeBuffer = writeTransport.outBuffers[0]; + + const readTransport = new TFramedTransport(); + readTransport.inBuf = writeBuffer; + readTransport.readHeaders(); + + const headers = readTransport.getReadHeaders(); + assert.equals(headers.Hihihihi, "hohohoho"); + assert.equals(headers.boobooboo, "fooshoopoo"); + assert.equals(headers.a, "z"); + assert.end(); + } +}; + +Object.keys(cases).forEach(function(caseName) { + test(caseName, cases[caseName]); +}); diff --git a/lib/nodejs/test/helpers.js b/lib/nodejs/test/helpers.js index 72d128d56af..f3c27b3d1f1 100644 --- a/lib/nodejs/test/helpers.js +++ b/lib/nodejs/test/helpers.js @@ -28,7 +28,8 @@ module.exports.transports = { module.exports.protocols = { json: thrift.TJSONProtocol, binary: thrift.TBinaryProtocol, - compact: thrift.TCompactProtocol + compact: thrift.TCompactProtocol, + header: thrift.THeaderProtocol }; module.exports.ecmaMode = process.argv.includes("--es6") ? "es6" : "es5"; diff --git a/lib/nodejs/test/int64.test.js b/lib/nodejs/test/int64.test.js new file mode 100644 index 00000000000..27ad28c003f --- /dev/null +++ b/lib/nodejs/test/int64.test.js @@ -0,0 +1,92 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +const Int64 = require("node-int64"); +const JSONInt64 = require("json-int64"); +const i64types = require("./gen-nodejs-es6/Int64Test_types.js"); +const test = require("tape"); + +const cases = { + "should correctly generate Int64 constants": function(assert) { + const EXPECTED_SMALL_INT64_AS_NUMBER = 42; + const EXPECTED_SMALL_INT64 = new Int64(42); + const EXPECTED_MAX_JS_SAFE_INT64 = new Int64(Number.MAX_SAFE_INTEGER); + const EXPECTED_MIN_JS_SAFE_INT64 = new Int64(Number.MIN_SAFE_INTEGER); + const EXPECTED_MAX_JS_SAFE_PLUS_ONE_INT64 = new Int64("0020000000000000"); // hex-encoded + const EXPECTED_MIN_JS_SAFE_MINUS_ONE_INT64 = new Int64("ffe0000000000000"); // hex-encoded 2's complement + const EXPECTED_MAX_SIGNED_INT64 = new Int64("7fffffffffffffff"); // hex-encoded + const EXPECTED_MIN_SIGNED_INT64 = new Int64("8000000000000000"); // hex-encoded 2's complement + const EXPECTED_INT64_LIST = [ + EXPECTED_SMALL_INT64, + EXPECTED_MAX_JS_SAFE_INT64, + EXPECTED_MIN_JS_SAFE_INT64, + EXPECTED_MAX_JS_SAFE_PLUS_ONE_INT64, + EXPECTED_MIN_JS_SAFE_MINUS_ONE_INT64, + EXPECTED_MAX_SIGNED_INT64, + EXPECTED_MIN_SIGNED_INT64 + ]; + + assert.ok(EXPECTED_SMALL_INT64.equals(i64types.SMALL_INT64)); + assert.ok(EXPECTED_MAX_JS_SAFE_INT64.equals(i64types.MAX_JS_SAFE_INT64)); + assert.ok(EXPECTED_MIN_JS_SAFE_INT64.equals(i64types.MIN_JS_SAFE_INT64)); + assert.ok( + EXPECTED_MAX_JS_SAFE_PLUS_ONE_INT64.equals( + i64types.MAX_JS_SAFE_PLUS_ONE_INT64 + ) + ); + assert.ok( + EXPECTED_MIN_JS_SAFE_MINUS_ONE_INT64.equals( + i64types.MIN_JS_SAFE_MINUS_ONE_INT64 + ) + ); + assert.ok(EXPECTED_MAX_SIGNED_INT64.equals(i64types.MAX_SIGNED_INT64)); + assert.ok(EXPECTED_MIN_SIGNED_INT64.equals(i64types.MIN_SIGNED_INT64)); + assert.equal( + EXPECTED_SMALL_INT64_AS_NUMBER, + i64types.SMALL_INT64.toNumber() + ); + assert.equal( + Number.MAX_SAFE_INTEGER, + i64types.MAX_JS_SAFE_INT64.toNumber() + ); + assert.equal( + Number.MIN_SAFE_INTEGER, + i64types.MIN_JS_SAFE_INT64.toNumber() + ); + + for (let i = 0; i < EXPECTED_INT64_LIST.length; ++i) { + assert.ok(EXPECTED_INT64_LIST[i].equals(i64types.INT64_LIST[i])); + } + + for (let i = 0; i < EXPECTED_INT64_LIST.length; ++i) { + const int64Object = EXPECTED_INT64_LIST[i]; + assert.ok( + i64types.INT64_2_INT64_MAP[ + JSONInt64.toDecimalString(int64Object) + ].equals(int64Object) + ); + } + + assert.end(); + } +}; + +Object.keys(cases).forEach(function(caseName) { + test(caseName, cases[caseName]); +}); diff --git a/lib/nodejs/test/server.js b/lib/nodejs/test/server.js index 7402094bc51..677839aea2b 100644 --- a/lib/nodejs/test/server.js +++ b/lib/nodejs/test/server.js @@ -61,6 +61,9 @@ let type = program.type; if (program.transport === "http") { program.transport = "buffered"; type = "http"; +} else if (program.transport === "websocket") { + program.transport = "buffered"; + type = "websocket"; } let options = { diff --git a/lib/nodejs/test/testAll.sh b/lib/nodejs/test/testAll.sh index 24f1f2ea13b..3ae88b369f6 100755 --- a/lib/nodejs/test/testAll.sh +++ b/lib/nodejs/test/testAll.sh @@ -23,6 +23,12 @@ fi DIR="$( cd "$( dirname "$0" )" && pwd )" +EPISODIC_DIR=${DIR}/episodic-code-generation-test + +THRIFT_FILES_DIR=${DIR}/../../../test + +THRIFT_COMPILER=${DIR}/../../../compiler/cpp/thrift + ISTANBUL="$DIR/../../../node_modules/istanbul/lib/cli.js" REPORT_PREFIX="${DIR}/../coverage/report" @@ -54,23 +60,68 @@ testServer() return $RET } +testEpisodicCompilation() +{ + RET=0 + if [ -n "${COVER}" ]; then + ${ISTANBUL} cover ${EPISODIC_DIR}/server.js --dir ${REPORT_PREFIX}${COUNT} --handle-sigint & + COUNT=$((COUNT+1)) + else + node ${EPISODIC_DIR}/server.js & + fi + SERVERPID=$! + sleep 0.1 + if [ -n "${COVER}" ]; then + ${ISTANBUL} cover ${EPISODIC_DIR}/client.js --dir ${REPORT_PREFIX}${COUNT} || RET=1 + COUNT=$((COUNT+1)) + else + node ${EPISODIC_DIR}/client.js || RET=1 + fi + kill -2 $SERVERPID || RET=1 + wait $SERVERPID + return $RET +} + TESTOK=0 -#generating thrift code +# generating Thrift code -${DIR}/../../../compiler/cpp/thrift -o ${DIR} --gen js:node ${DIR}/../../../test/ThriftTest.thrift -${DIR}/../../../compiler/cpp/thrift -o ${DIR} --gen js:node ${DIR}/../../../test/JsDeepConstructorTest.thrift +${THRIFT_COMPILER} -o ${DIR} --gen js:node ${THRIFT_FILES_DIR}/ThriftTest.thrift +${THRIFT_COMPILER} -o ${DIR} --gen js:node ${THRIFT_FILES_DIR}/JsDeepConstructorTest.thrift +${THRIFT_COMPILER} -o ${DIR} --gen js:node ${THRIFT_FILES_DIR}/Int64Test.thrift mkdir ${DIR}/gen-nodejs-es6 -${DIR}/../../../compiler/cpp/thrift -out ${DIR}/gen-nodejs-es6 --gen js:node,es6 ${DIR}/../../../test/ThriftTest.thrift -${DIR}/../../../compiler/cpp/thrift -out ${DIR}/gen-nodejs-es6 --gen js:node,es6 ${DIR}/../../../test/JsDeepConstructorTest.thrift +${THRIFT_COMPILER} -out ${DIR}/gen-nodejs-es6 --gen js:node,es6 ${THRIFT_FILES_DIR}/ThriftTest.thrift +${THRIFT_COMPILER} -out ${DIR}/gen-nodejs-es6 --gen js:node,es6 ${THRIFT_FILES_DIR}/JsDeepConstructorTest.thrift +${THRIFT_COMPILER} -out ${DIR}/gen-nodejs-es6 --gen js:node,es6 ${THRIFT_FILES_DIR}/Int64Test.thrift + +# generate episodic compilation test code +TYPES_PACKAGE=${EPISODIC_DIR}/node_modules/types-package + +# generate the first episode +mkdir --parents ${EPISODIC_DIR}/gen-1/first-episode +${THRIFT_COMPILER} -o ${EPISODIC_DIR}/gen-1/first-episode --gen js:node,thrift_package_output_directory=first-episode ${THRIFT_FILES_DIR}/Types.thrift + +# create a "package" from the first episode and "install" it, the episode file must be at the module root +mkdir --parents ${TYPES_PACKAGE}/first-episode +cp --force ${EPISODIC_DIR}/episodic_compilation.package.json ${TYPES_PACKAGE}/package.json +cp --force ${EPISODIC_DIR}/gen-1/first-episode/gen-nodejs/Types_types.js ${TYPES_PACKAGE}/first-episode/ +cp --force ${EPISODIC_DIR}/gen-1/first-episode/gen-nodejs/thrift.js.episode ${TYPES_PACKAGE} + +# generate the second episode +mkdir --parents ${EPISODIC_DIR}/gen-2/second-episode +${THRIFT_COMPILER} -o ${EPISODIC_DIR}/gen-2/second-episode --gen js:node,imports=${TYPES_PACKAGE} ${THRIFT_FILES_DIR}/Service.thrift +if [ -f ${EPISODIC_DIR}/gen-2/second-episode/Types_types.js ]; then + TESTOK=1 +fi -#unit tests +# unit tests node ${DIR}/binary.test.js || TESTOK=1 +node ${DIR}/int64.test.js || TESTOK=1 node ${DIR}/deep-constructor.test.js || TESTOK=1 -#integration tests +# integration tests for type in tcp multiplex websocket http do @@ -88,6 +139,8 @@ do done done +# episodic compilation test +testEpisodicCompilation if [ -n "${COVER}" ]; then ${ISTANBUL} report --dir "${DIR}/../coverage" --include "${DIR}/../coverage/report*/coverage.json" lcov cobertura html diff --git a/lib/nodejs/test/test_header_payload b/lib/nodejs/test/test_header_payload new file mode 100644 index 00000000000..22d5ef7a271 Binary files /dev/null and b/lib/nodejs/test/test_header_payload differ diff --git a/lib/nodets/.gitignore b/lib/nodets/.gitignore deleted file mode 100644 index c7aba89242d..00000000000 --- a/lib/nodets/.gitignore +++ /dev/null @@ -1 +0,0 @@ -test-compiled/ diff --git a/lib/nodets/Makefile.am b/lib/nodets/Makefile.am index ea640cf0e74..939dff2a5e3 100755 --- a/lib/nodets/Makefile.am +++ b/lib/nodets/Makefile.am @@ -20,6 +20,7 @@ stubs: $(top_srcdir)/test/ThriftTest.thrift mkdir -p test-compiled $(THRIFT) --gen js:node,ts -o test/ $(top_srcdir)/test/ThriftTest.thrift && $(THRIFT) --gen js:node,ts -o test-compiled $(top_srcdir)/test/ThriftTest.thrift + $(THRIFT) --gen js:node,ts -o test/ $(top_srcdir)/test/Int64Test.thrift && $(THRIFT) --gen js:node,ts -o test-compiled $(top_srcdir)/test/Int64Test.thrift ts-compile: stubs mkdir -p test-compiled diff --git a/lib/nodets/test/int64.test.ts b/lib/nodets/test/int64.test.ts new file mode 100644 index 00000000000..d209234b50c --- /dev/null +++ b/lib/nodets/test/int64.test.ts @@ -0,0 +1,88 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import Int64 = require("node-int64"); +import JSONInt64 = require('json-int64'); +import i64types = require("./gen-nodejs/Int64Test_types"); +import test = require("tape"); + +const cases = { + "should correctly generate Int64 constants": function(assert) { + const EXPECTED_SMALL_INT64_AS_NUMBER: number = 42; + const EXPECTED_SMALL_INT64: Int64 = new Int64(42); + const EXPECTED_MAX_JS_SAFE_INT64: Int64 = new Int64(Number.MAX_SAFE_INTEGER); + const EXPECTED_MIN_JS_SAFE_INT64: Int64 = new Int64(Number.MIN_SAFE_INTEGER); + const EXPECTED_MAX_JS_SAFE_PLUS_ONE_INT64: Int64 = new Int64("0020000000000000"); // hex-encoded + const EXPECTED_MIN_JS_SAFE_MINUS_ONE_INT64: Int64 = new Int64("ffe0000000000000"); // hex-encoded 2's complement + const EXPECTED_MAX_SIGNED_INT64: Int64 = new Int64("7fffffffffffffff"); // hex-encoded + const EXPECTED_MIN_SIGNED_INT64: Int64 = new Int64("8000000000000000"); // hex-encoded 2's complement + const EXPECTED_INT64_LIST: Int64[] = [ + EXPECTED_SMALL_INT64, + EXPECTED_MAX_JS_SAFE_INT64, + EXPECTED_MIN_JS_SAFE_INT64, + EXPECTED_MAX_JS_SAFE_PLUS_ONE_INT64, + EXPECTED_MIN_JS_SAFE_MINUS_ONE_INT64, + EXPECTED_MAX_SIGNED_INT64, + EXPECTED_MIN_SIGNED_INT64 + ]; + + assert.ok(EXPECTED_SMALL_INT64.equals(i64types.SMALL_INT64)); + assert.ok(EXPECTED_MAX_JS_SAFE_INT64.equals(i64types.MAX_JS_SAFE_INT64)); + assert.ok(EXPECTED_MIN_JS_SAFE_INT64.equals(i64types.MIN_JS_SAFE_INT64)); + assert.ok( + EXPECTED_MAX_JS_SAFE_PLUS_ONE_INT64.equals( + i64types.MAX_JS_SAFE_PLUS_ONE_INT64 + ) + ); + assert.ok( + EXPECTED_MIN_JS_SAFE_MINUS_ONE_INT64.equals( + i64types.MIN_JS_SAFE_MINUS_ONE_INT64 + ) + ); + assert.ok(EXPECTED_MAX_SIGNED_INT64.equals(i64types.MAX_SIGNED_INT64)); + assert.ok(EXPECTED_MIN_SIGNED_INT64.equals(i64types.MIN_SIGNED_INT64)); + assert.equal( + EXPECTED_SMALL_INT64_AS_NUMBER, + i64types.SMALL_INT64.toNumber() + ); + assert.equal( + Number.MAX_SAFE_INTEGER, + i64types.MAX_JS_SAFE_INT64.toNumber() + ); + assert.equal( + Number.MIN_SAFE_INTEGER, + i64types.MIN_JS_SAFE_INT64.toNumber() + ); + + for (let i = 0; i < EXPECTED_INT64_LIST.length; ++i) { + assert.ok(EXPECTED_INT64_LIST[i].equals(i64types.INT64_LIST[i])); + } + + for (let i = 0; i < EXPECTED_INT64_LIST.length; ++i){ + let int64Object = EXPECTED_INT64_LIST[i]; + assert.ok(i64types.INT64_2_INT64_MAP[JSONInt64.toDecimalString(int64Object)].equals(int64Object)); + } + + assert.end(); + } +}; + +Object.keys(cases).forEach(function(caseName) { + test(caseName, cases[caseName]); +}); diff --git a/lib/nodets/test/test-cases.ts b/lib/nodets/test/test-cases.ts index ca740ec8262..44f254e9200 100644 --- a/lib/nodets/test/test-cases.ts +++ b/lib/nodets/test/test-cases.ts @@ -1,6 +1,7 @@ 'use strict'; import ttypes = require('./gen-nodejs/ThriftTest_types'); +import Int64 = require('node-int64'); //all Languages in UTF-8 /*jshint -W100 */ @@ -84,7 +85,7 @@ export var out = new ttypes.Xtruct({ string_thing: 'Zero', byte_thing: 1, i32_thing: -3, - i64_thing: 1000000 + i64_thing: new Int64(1000000) }); export var out2 = new ttypes.Xtruct2(); @@ -93,17 +94,17 @@ out2.struct_thing = out; out2.i32_thing = 5; export var crazy = new ttypes.Insanity({ - "userMap":{ "5":5, "8":8 }, + "userMap":{ "5":new Int64(5), "8":new Int64(8) }, "xtructs":[new ttypes.Xtruct({ "string_thing":"Goodbye4", "byte_thing":4, "i32_thing":4, - "i64_thing":4 + "i64_thing":new Int64(4) }), new ttypes.Xtruct({ "string_thing":"Hello2", "byte_thing":2, "i32_thing":2, - "i64_thing":2 + "i64_thing":new Int64(2) })] }); diff --git a/lib/nodets/test/testAll.sh b/lib/nodets/test/testAll.sh index a7c00bfd931..3be12c3622a 100755 --- a/lib/nodets/test/testAll.sh +++ b/lib/nodets/test/testAll.sh @@ -11,7 +11,9 @@ compile() { #generating thrift code ${DIR}/../../../compiler/cpp/thrift -o ${DIR} --gen js:node,ts ${DIR}/../../../test/ThriftTest.thrift + ${DIR}/../../../compiler/cpp/thrift -o ${DIR} --gen js:node,ts ${DIR}/../../../test/Int64Test.thrift ${DIR}/../../../compiler/cpp/thrift -o ${COMPILEDDIR} --gen js:node,ts ${DIR}/../../../test/ThriftTest.thrift + ${DIR}/../../../compiler/cpp/thrift -o ${COMPILEDDIR} --gen js:node,ts ${DIR}/../../../test/Int64Test.thrift tsc --outDir $COMPILEDDIR --project $DIR/tsconfig.json } @@ -30,6 +32,8 @@ testServer() return $RET } +node ${COMPILEDDIR}/int64.test.js || TESTOK=1 + #integration tests testServer || TESTOK=1 diff --git a/lib/nodets/test/test_handler.ts b/lib/nodets/test/test_handler.ts index 1bc855a7099..996c32a5a32 100644 --- a/lib/nodets/test/test_handler.ts +++ b/lib/nodets/test/test_handler.ts @@ -24,6 +24,7 @@ import ttypes = require("./gen-nodejs/ThriftTest_types"); import thrift = require("thrift"); import Thrift = thrift.Thrift; import Q = require("q"); +import Int64 = require("node-int64"); export class SyncThriftTestHandler { @@ -62,7 +63,7 @@ export class SyncThriftTestHandler { return Q.resolve(insane); } - testMulti(arg0: any, arg1: number, arg2: number, arg3: { [k: number]: string; }, arg4: ttypes.Numberz, arg5: number) { + testMulti(arg0: any, arg1: number, arg2: Int64, arg3: { [k: number]: string; }, arg4: ttypes.Numberz, arg5: number) { var hello = new ttypes.Xtruct(); hello.string_thing = 'Hello2'; hello.byte_thing = arg0; @@ -196,7 +197,7 @@ export class AsyncThriftTestHandler { } return Q.resolve(); } - testMulti(arg0: any, arg1: number, arg2: number, arg3: { [k: number]: string; }, arg4: ttypes.Numberz, arg5: number, result: Function): Q.IPromise { + testMulti(arg0: any, arg1: number, arg2: Int64, arg3: { [k: number]: string; }, arg4: ttypes.Numberz, arg5: number, result: Function): Q.IPromise { var hello = this.syncHandler.testMulti(arg0, arg1, arg2, arg3, arg4, arg5); hello.then(hello => result(null, hello)); return Q.resolve(); diff --git a/lib/ocaml/.gitignore b/lib/ocaml/.gitignore deleted file mode 100644 index 0d9a6af4677..00000000000 --- a/lib/ocaml/.gitignore +++ /dev/null @@ -1,11 +0,0 @@ -_build/ -_tags -configure -setup.data -setup.ml -myocamlbuild.ml -*/META -*/*.mllib -*/*.mldylib -Makefile -OCamlMakefile diff --git a/lib/ocaml/_oasis b/lib/ocaml/_oasis index 315183f4df8..2b6c477b061 100644 --- a/lib/ocaml/_oasis +++ b/lib/ocaml/_oasis @@ -1,5 +1,5 @@ Name: libthrift-ocaml -Version: 0.12.0 +Version: 0.14.0 OASISFormat: 0.3 Synopsis: OCaml bindings for the Apache Thrift RPC system Authors: Apache Thrift Developers diff --git a/lib/ocaml/src/Thrift.ml b/lib/ocaml/src/Thrift.ml index f0d7a429659..063459ba0c1 100644 --- a/lib/ocaml/src/Thrift.ml +++ b/lib/ocaml/src/Thrift.ml @@ -206,8 +206,6 @@ struct (* skippage *) method skip typ = match typ with - | T_STOP -> () - | T_VOID -> () | T_BOOL -> ignore self#readBool | T_BYTE | T_I08 -> ignore self#readByte @@ -248,6 +246,7 @@ struct self#readListEnd) | T_UTF8 -> () | T_UTF16 -> () + | _ -> raise (Protocol.E (Protocol.INVALID_DATA, "Invalid data")) end class virtual factory = diff --git a/lib/perl/MANIFEST.SKIP b/lib/perl/MANIFEST.SKIP index 7963b42ad80..9b044509e50 100644 --- a/lib/perl/MANIFEST.SKIP +++ b/lib/perl/MANIFEST.SKIP @@ -8,6 +8,7 @@ Makefile Makefile.am Makefile.in pm_to_blib -test/Makefile.am -test/Makefile.in +t/Makefile +t/Makefile.am +t/Makefile.in tools/FixupDist.pl diff --git a/lib/perl/Makefile.PL b/lib/perl/Makefile.PL index bdeaad2b7bc..b72a23e3b1a 100644 --- a/lib/perl/Makefile.PL +++ b/lib/perl/Makefile.PL @@ -33,4 +33,20 @@ WriteMakefile( ABSTRACT => 'Apache Thrift is a software framework for scalable c 'Bit::Vector' => 0, 'Class::Accessor' => 0 }, +# SIGN => 1, + TEST_REQUIRES => { + 'Test::Exception' => 0, + }, VERSION_FROM => 'lib/Thrift.pm' ); + +# THRIFT-4691 +package MY; # so that "SUPER" works right +sub test { + # Adds gen-perl and gen-perl2 to the test execution as include paths + # Could not find anything in MakeMaker that would do this... + my @result; + for (@result = shift->SUPER::test(@_)) { + s/\$\(TEST_FILES\)/-Igen-perl -Igen-perl2 \$(TEST_FILES)/ig; + } + @result; +} diff --git a/lib/perl/Makefile.am b/lib/perl/Makefile.am index fa0f16b8af0..84009bd160a 100644 --- a/lib/perl/Makefile.am +++ b/lib/perl/Makefile.am @@ -17,7 +17,7 @@ # under the License. # -SUBDIRS = test +SUBDIRS = t Makefile-perl.mk : Makefile.PL $(PERL) Makefile.PL MAKEFILE=Makefile-perl.mk INSTALLDIRS=$(INSTALLDIRS) INSTALL_BASE=$(PERL_PREFIX) @@ -40,6 +40,7 @@ EXTRA_DIST = \ coding_standards.md \ build-cpan-dist.sh \ Makefile.PL \ + MANIFEST.SKIP \ test.pl \ lib/Thrift.pm \ lib/Thrift.pm \ @@ -63,6 +64,7 @@ EXTRA_DIST = \ lib/Thrift/UnixSocket.pm \ lib/Thrift/Type.pm \ lib/Thrift/Transport.pm \ + tools/FixupDist.pl \ README.md THRIFT = @top_builddir@/compiler/cpp/thrift @@ -95,7 +97,7 @@ BUILT_SOURCES = $(PERL_GEN) check-local: $(PERL_GEN) $(PERL) -Iblib/lib -I@abs_srcdir@ -I@builddir@/gen-perl2 -I@builddir@/gen-perl \ - @abs_srcdir@/test.pl @abs_srcdir@/test/*.t + @abs_srcdir@/test.pl @abs_srcdir@/t/*.t $(THRIFTTEST_GEN): $(THRIFT_IF) $(THRIFT) $(THRIFT) --gen perl $< diff --git a/lib/perl/README.md b/lib/perl/README.md index bd1e5b2e496..6958510374d 100644 --- a/lib/perl/README.md +++ b/lib/perl/README.md @@ -61,6 +61,12 @@ to use Thrift. * Bit::Vector * Class::Accessor +## Test + +This is only required when running tests: + + * Test::Exception + ### HttpClient Transport These are only required if using Thrift::HttpClient: diff --git a/lib/perl/build-cpan-dist.sh b/lib/perl/build-cpan-dist.sh index ae22e7e623c..c92fd76b58b 100755 --- a/lib/perl/build-cpan-dist.sh +++ b/lib/perl/build-cpan-dist.sh @@ -12,9 +12,10 @@ rm -rf Thrift-* # setup cpan without a prompt echo | cpan -cpan install HTTP::Date +cpan install HTTP::Date Log::Log4perl cpan install CPAN cpan install CPAN::Meta ExtUtils::MakeMaker JSON::PP +# cpan install Module::Signature perl Makefile.PL rm MYMETA.yml @@ -47,7 +48,9 @@ if [[ "$DISTDIR" != "$NEWDIR" ]]; then fi cd $DISTDIR cp -p ../Makefile.PL . +cp -pr ../gen-perl . +cp -pr ../gen-perl2 . perl ../tools/FixupDist.pl cd .. -tar cvzf $DISTFILE $DISTDIR +tar cvzf --hard-dereference $DISTFILE $DISTDIR rm -r $DISTDIR diff --git a/lib/perl/lib/Thrift.pm b/lib/perl/lib/Thrift.pm index 107b2a26218..9a7c6bbf873 100644 --- a/lib/perl/lib/Thrift.pm +++ b/lib/perl/lib/Thrift.pm @@ -31,6 +31,6 @@ use warnings; # package Thrift; -use version 0.77; our $VERSION = version->declare("v0.12.0_0"); +use version 0.77; our $VERSION = version->declare("v0.14.0"); 1; diff --git a/lib/perl/lib/Thrift/MemoryBuffer.pm b/lib/perl/lib/Thrift/MemoryBuffer.pm index be97ce4f8e2..5192cfb99a3 100644 --- a/lib/perl/lib/Thrift/MemoryBuffer.pm +++ b/lib/perl/lib/Thrift/MemoryBuffer.pm @@ -117,7 +117,7 @@ sub readAll my $avail = ($self->{wPos} - $self->{rPos}); if ($avail < $len) { - die TTransportException->new("Attempt to readAll($len) found only $avail available", + die Thrift::TTransportException->new("Attempt to readAll($len) found only $avail available", Thrift::TTransportException::END_OF_FILE); } diff --git a/lib/perl/test/Makefile.am b/lib/perl/t/Makefile.am similarity index 100% rename from lib/perl/test/Makefile.am rename to lib/perl/t/Makefile.am diff --git a/lib/perl/test/memory_buffer.t b/lib/perl/t/memory_buffer.t similarity index 92% rename from lib/perl/test/memory_buffer.t rename to lib/perl/t/memory_buffer.t index 8fa9fd72e79..4a4ba0f7d9b 100644 --- a/lib/perl/test/memory_buffer.t +++ b/lib/perl/t/memory_buffer.t @@ -17,7 +17,8 @@ # under the License. # -use Test::More tests => 6; +use Test::More tests => 7; +use Test::Exception; use strict; use warnings; @@ -33,6 +34,8 @@ use ThriftTest::Types; my $transport = Thrift::MemoryBuffer->new(); my $protocol = Thrift::BinaryProtocol->new($transport); +throws_ok { $protocol->readByte } 'Thrift::TTransportException'; + my $a = ThriftTest::Xtruct->new(); $a->i32_thing(10); $a->i64_thing(30); diff --git a/lib/perl/test/multiplex.t b/lib/perl/t/multiplex.t similarity index 100% rename from lib/perl/test/multiplex.t rename to lib/perl/t/multiplex.t diff --git a/lib/perl/test/processor.t b/lib/perl/t/processor.t similarity index 100% rename from lib/perl/test/processor.t rename to lib/perl/t/processor.t diff --git a/lib/php/Makefile.am b/lib/php/Makefile.am index 8d9050a1dd9..1857d034b49 100755 --- a/lib/php/Makefile.am +++ b/lib/php/Makefile.am @@ -33,117 +33,132 @@ phpmoduledir = `php-config --extension-dir` phpmodule_SCRIPTS = src/ext/thrift_protocol/modules/thrift_protocol.so distclean-local: + if [ -f src/ext/thrift_protocol/Makefile ]; then cd src/ext/thrift_protocol/ && $(MAKE) distclean; fi cd $(phpmodule_SCRIPTS) && $(PHPIZE) --clean endif phpdir = $(PHP_PREFIX)/ php_DATA = \ - lib/TMultiplexedProcessor.php + lib/TMultiplexedProcessor.php phpbasedir = $(phpdir)/Base phpbase_DATA = \ - lib/Base/TBase.php + lib/Base/TBase.php phpclassloaderdir = $(phpdir)/ClassLoader phpclassloader_DATA = \ - lib/ClassLoader/ThriftClassLoader.php + lib/ClassLoader/ThriftClassLoader.php phpexceptiondir = $(phpdir)/Exception phpexception_DATA = \ - lib/Exception/TApplicationException.php \ - lib/Exception/TException.php \ - lib/Exception/TProtocolException.php \ - lib/Exception/TTransportException.php + lib/Exception/TApplicationException.php \ + lib/Exception/TException.php \ + lib/Exception/TProtocolException.php \ + lib/Exception/TTransportException.php phpfactorydir = $(phpdir)/Factory phpfactory_DATA = \ - lib/Factory/TBinaryProtocolFactory.php \ - lib/Factory/TCompactProtocolFactory.php \ - lib/Factory/TJSONProtocolFactory.php \ - lib/Factory/TProtocolFactory.php \ - lib/Factory/TStringFuncFactory.php \ - lib/Factory/TTransportFactory.php + lib/Factory/TBinaryProtocolFactory.php \ + lib/Factory/TCompactProtocolFactory.php \ + lib/Factory/TJSONProtocolFactory.php \ + lib/Factory/TProtocolFactory.php \ + lib/Factory/TStringFuncFactory.php \ + lib/Factory/TTransportFactory.php phpprotocoldir = $(phpdir)/Protocol phpprotocol_DATA = \ - lib/Protocol/TBinaryProtocolAccelerated.php \ - lib/Protocol/TBinaryProtocol.php \ - lib/Protocol/TCompactProtocol.php \ - lib/Protocol/TJSONProtocol.php \ - lib/Protocol/TMultiplexedProtocol.php \ - lib/Protocol/TProtocol.php \ - lib/Protocol/TProtocolDecorator.php \ - lib/Protocol/TSimpleJSONProtocol.php + lib/Protocol/TBinaryProtocolAccelerated.php \ + lib/Protocol/TBinaryProtocol.php \ + lib/Protocol/TCompactProtocol.php \ + lib/Protocol/TJSONProtocol.php \ + lib/Protocol/TMultiplexedProtocol.php \ + lib/Protocol/TProtocol.php \ + lib/Protocol/TProtocolDecorator.php \ + lib/Protocol/TSimpleJSONProtocol.php phpprotocoljsondir = $(phpprotocoldir)/JSON phpprotocoljson_DATA = \ - lib/Protocol/JSON/BaseContext.php \ - lib/Protocol/JSON/ListContext.php \ - lib/Protocol/JSON/LookaheadReader.php \ - lib/Protocol/JSON/PairContext.php + lib/Protocol/JSON/BaseContext.php \ + lib/Protocol/JSON/ListContext.php \ + lib/Protocol/JSON/LookaheadReader.php \ + lib/Protocol/JSON/PairContext.php phpprotocolsimplejsondir = $(phpprotocoldir)/SimpleJSON phpprotocolsimplejson_DATA = \ - lib/Protocol/SimpleJSON/CollectionMapKeyException.php \ - lib/Protocol/SimpleJSON/Context.php \ - lib/Protocol/SimpleJSON/ListContext.php \ - lib/Protocol/SimpleJSON/MapContext.php \ - lib/Protocol/SimpleJSON/StructContext.php + lib/Protocol/SimpleJSON/CollectionMapKeyException.php \ + lib/Protocol/SimpleJSON/Context.php \ + lib/Protocol/SimpleJSON/ListContext.php \ + lib/Protocol/SimpleJSON/MapContext.php \ + lib/Protocol/SimpleJSON/StructContext.php phpserializerdir = $(phpdir)/Serializer phpserializer_DATA = \ - lib/Serializer/TBinarySerializer.php + lib/Serializer/TBinarySerializer.php phpserverdir = $(phpdir)/Server phpserver_DATA = \ - lib/Server/TServerSocket.php \ - lib/Server/TForkingServer.php \ - lib/Server/TServer.php \ - lib/Server/TServerTransport.php \ - lib/Server/TSimpleServer.php + lib/Server/TServerSocket.php \ + lib/Server/TForkingServer.php \ + lib/Server/TServer.php \ + lib/Server/TServerTransport.php \ + lib/Server/TSimpleServer.php phpstringfuncdir = $(phpdir)/StringFunc phpstringfunc_DATA = \ - lib/StringFunc/Mbstring.php \ - lib/StringFunc/Core.php \ - lib/StringFunc/TStringFunc.php + lib/StringFunc/Mbstring.php \ + lib/StringFunc/Core.php \ + lib/StringFunc/TStringFunc.php phptransportdir = $(phpdir)/Transport phptransport_DATA = \ - lib/Transport/TBufferedTransport.php \ - lib/Transport/TCurlClient.php \ - lib/Transport/TFramedTransport.php \ - lib/Transport/THttpClient.php \ - lib/Transport/TMemoryBuffer.php \ - lib/Transport/TNullTransport.php \ - lib/Transport/TPhpStream.php \ - lib/Transport/TSocket.php \ - lib/Transport/TSocketPool.php \ - lib/Transport/TTransport.php + lib/Transport/TBufferedTransport.php \ + lib/Transport/TCurlClient.php \ + lib/Transport/TFramedTransport.php \ + lib/Transport/THttpClient.php \ + lib/Transport/TMemoryBuffer.php \ + lib/Transport/TNullTransport.php \ + lib/Transport/TPhpStream.php \ + lib/Transport/TSocket.php \ + lib/Transport/TSocketPool.php \ + lib/Transport/TTransport.php phptypedir = $(phpdir)/Type phptype_DATA = \ - lib/Type/TMessageType.php \ - lib/Type/TType.php \ - lib/Type/TConstant.php + lib/Type/TMessageType.php \ + lib/Type/TType.php \ + lib/Type/TConstant.php + +clean-local: + if [ -f src/ext/thrift_protocol/Makefile ]; then cd src/ext/thrift_protocol/ && $(MAKE) clean; fi + EXTRA_DIST = \ - lib \ - src/autoload.php \ - src/ext/thrift_protocol/config.m4 \ - src/ext/thrift_protocol/config.w32 \ - src/ext/thrift_protocol/php_thrift_protocol.cpp \ - src/ext/thrift_protocol/php_thrift_protocol.h \ - src/ext/thrift_protocol/run-tests.php \ - src/Thrift.php \ - src/TStringUtils.php \ - coding_standards.md \ - thrift_protocol.ini \ - README.apache.md \ - README.md + lib \ + src/autoload.php \ + src/ext/thrift_protocol/config.m4 \ + src/ext/thrift_protocol/config.w32 \ + src/ext/thrift_protocol/php_thrift_protocol.cpp \ + src/ext/thrift_protocol/php_thrift_protocol.h \ + src/Thrift.php \ + src/TStringUtils.php \ + coding_standards.md \ + thrift_protocol.ini \ + README.apache.md \ + README.md \ + test/Fixtures.php \ + test/TestValidators.thrift \ + test/JsonSerialize/JsonSerializeTest.php \ + test/Protocol/BinarySerializerTest.php \ + test/Protocol/TJSONProtocolFixtures.php \ + test/Protocol/TJSONProtocolTest.php \ + test/Protocol/TSimpleJSONProtocolFixtures.php \ + test/Protocol/TSimpleJSONProtocolTest.php \ + test/Validator/BaseValidatorTest.php \ + test/Validator/ValidatorTest.php \ + test/Validator/ValidatorTestOop.php + MAINTAINERCLEANFILES = \ - Makefile \ - Makefile.in + Makefile.in diff --git a/lib/php/README.md b/lib/php/README.md index 7170104dff4..e7144fe444e 100644 --- a/lib/php/README.md +++ b/lib/php/README.md @@ -23,7 +23,7 @@ under the License. Thrift requires PHP 5. Thrift makes as few assumptions about your PHP environment as possible while trying to make some more advanced PHP -features (i.e. APC cacheing using asbolute path URLs) as simple as possible. +features (i.e. APCu cacheing using asbolute path URLs) as simple as possible. To use Thrift in your PHP codebase, take the following steps: @@ -46,9 +46,9 @@ PHP_INT_SIZE used by the TBinaryProtocol to properly use pack() and unpack() to serialize data. -apc_fetch(), apc_store() +apcu_fetch(), apcu_store() - APC cache is used by the TSocketPool class. If you do not have APC installed, + APCu cache is used by the TSocketPool class. If you do not have APCu installed, Thrift will fill in null stub function definitions. # Breaking Changes diff --git a/lib/php/lib/ClassLoader/ThriftClassLoader.php b/lib/php/lib/ClassLoader/ThriftClassLoader.php index 4361bd84e96..e4b4a17c0c4 100644 --- a/lib/php/lib/ClassLoader/ThriftClassLoader.php +++ b/lib/php/lib/ClassLoader/ThriftClassLoader.php @@ -40,26 +40,26 @@ class ThriftClassLoader protected $definitions = array(); /** - * Do we use APC cache ? + * Do we use APCu cache ? * @var boolean */ - protected $apc = false; + protected $apcu = false; /** - * APC Cache prefix + * APCu Cache prefix * @var string */ - protected $apc_prefix; + protected $apcu_prefix; /** - * Set autoloader to use APC cache + * Set autoloader to use APCu cache * @param boolean $apc - * @param string $apc_prefix + * @param string $apcu_prefix */ - public function __construct($apc = false, $apc_prefix = null) + public function __construct($apc = false, $apcu_prefix = null) { - $this->apc = $apc; - $this->apc_prefix = $apc_prefix; + $this->apcu = $apc; + $this->apcu_prefix = $apcu_prefix; } /** @@ -101,7 +101,7 @@ public function register($prepend = false) */ public function loadClass($class) { - if ((true === $this->apc && ($file = $this->findFileInApc($class))) or + if ((true === $this->apcu && ($file = $this->findFileInApcu($class))) or ($file = $this->findFile($class)) ) { require_once $file; @@ -109,14 +109,14 @@ public function loadClass($class) } /** - * Loads the given class or interface in APC. + * Loads the given class or interface in APCu. * @param string $class The name of the class * @return string */ - protected function findFileInApc($class) + protected function findFileInApcu($class) { - if (false === $file = apc_fetch($this->apc_prefix . $class)) { - apc_store($this->apc_prefix . $class, $file = $this->findFile($class)); + if (false === $file = apcu_fetch($this->apcu_prefix . $class)) { + apcu_store($this->apcu_prefix . $class, $file = $this->findFile($class)); } return $file; diff --git a/lib/php/lib/Protocol/TProtocol.php b/lib/php/lib/Protocol/TProtocol.php index 81aceb623cf..f7b581f7bef 100644 --- a/lib/php/lib/Protocol/TProtocol.php +++ b/lib/php/lib/Protocol/TProtocol.php @@ -22,6 +22,8 @@ namespace Thrift\Protocol; +use Thrift\Exception\TException; +use Thrift\Transport\TTransport; use Thrift\Type\TType; use Thrift\Exception\TProtocolException; @@ -38,7 +40,7 @@ abstract class TProtocol protected $trans_; /** - * Constructor + * @param TTransport $trans */ protected function __construct($trans) { diff --git a/lib/php/lib/Transport/TCurlClient.php b/lib/php/lib/Transport/TCurlClient.php index 482b43b72cd..2087433dd54 100644 --- a/lib/php/lib/Transport/TCurlClient.php +++ b/lib/php/lib/Transport/TCurlClient.php @@ -83,6 +83,13 @@ class TCurlClient extends TTransport */ protected $timeout_; + /** + * Connection timeout + * + * @var float + */ + protected $connectionTimeout_; + /** * http headers * @@ -99,7 +106,7 @@ class TCurlClient extends TTransport */ public function __construct($host, $port = 80, $uri = '', $scheme = 'http') { - if ((TStringFuncFactory::create()->strlen($uri) > 0) && ($uri{0} != '/')) { + if ((TStringFuncFactory::create()->strlen($uri) > 0) && ($uri[0] != '/')) { $uri = '/' . $uri; } $this->scheme_ = $scheme; @@ -109,6 +116,7 @@ public function __construct($host, $port = 80, $uri = '', $scheme = 'http') $this->request_ = ''; $this->response_ = null; $this->timeout_ = null; + $this->connectionTimeout_ = null; $this->headers_ = array(); } @@ -122,6 +130,16 @@ public function setTimeoutSecs($timeout) $this->timeout_ = $timeout; } + /** + * Set connection timeout + * + * @param float $connectionTimeout + */ + public function setConnectionTimeoutSecs($connectionTimeout) + { + $this->connectionTimeout_ = $connectionTimeout; + } + /** * Whether this transport is open. * @@ -230,7 +248,20 @@ public function flush() curl_setopt(self::$curlHandle, CURLOPT_HTTPHEADER, $headers); if ($this->timeout_ > 0) { - curl_setopt(self::$curlHandle, CURLOPT_TIMEOUT, $this->timeout_); + if ($this->timeout_ < 1.0) { + // Timestamps smaller than 1 second are ignored when CURLOPT_TIMEOUT is used + curl_setopt(self::$curlHandle, CURLOPT_TIMEOUT_MS, 1000 * $this->timeout_); + } else { + curl_setopt(self::$curlHandle, CURLOPT_TIMEOUT, $this->timeout_); + } + } + if ($this->connectionTimeout_ > 0) { + if ($this->connectionTimeout_ < 1.0) { + // Timestamps smaller than 1 second are ignored when CURLOPT_CONNECTTIMEOUT is used + curl_setopt(self::$curlHandle, CURLOPT_CONNECTTIMEOUT_MS, 1000 * $this->connectionTimeout_); + } else { + curl_setopt(self::$curlHandle, CURLOPT_CONNECTTIMEOUT, $this->connectionTimeout_); + } } curl_setopt(self::$curlHandle, CURLOPT_POSTFIELDS, $this->request_); $this->request_ = ''; diff --git a/lib/php/lib/Transport/THttpClient.php b/lib/php/lib/Transport/THttpClient.php index 0158809d293..4d6be32fed2 100644 --- a/lib/php/lib/Transport/THttpClient.php +++ b/lib/php/lib/Transport/THttpClient.php @@ -106,7 +106,7 @@ class THttpClient extends TTransport */ public function __construct($host, $port = 80, $uri = '', $scheme = 'http', array $context = array()) { - if ((TStringFuncFactory::create()->strlen($uri) > 0) && ($uri{0} != '/')) { + if ((TStringFuncFactory::create()->strlen($uri) > 0) && ($uri[0] != '/')) { $uri = '/' . $uri; } $this->scheme_ = $scheme; diff --git a/lib/php/lib/Transport/TSocket.php b/lib/php/lib/Transport/TSocket.php index 5147efa6301..8fe60fdaae7 100644 --- a/lib/php/lib/Transport/TSocket.php +++ b/lib/php/lib/Transport/TSocket.php @@ -251,8 +251,9 @@ public function open() } if (function_exists('socket_import_stream') && function_exists('socket_set_option')) { - $socket = socket_import_stream($this->handle_); - socket_set_option($socket, SOL_TCP, TCP_NODELAY, 1); + // warnings silenced due to bug https://bugs.php.net/bug.php?id=70939 + $socket = @socket_import_stream($this->handle_); + @socket_set_option($socket, SOL_TCP, TCP_NODELAY, 1); } } @@ -328,7 +329,8 @@ public function write($buf) if ($writable > 0) { // write buffer to stream $written = fwrite($this->handle_, $buf); - if ($written === -1 || $written === false) { + $closed_socket = $written === 0 && feof($this->handle_); + if ($written === -1 || $written === false || $closed_socket) { throw new TTransportException( 'TSocket: Could not write ' . TStringFuncFactory::create()->strlen($buf) . ' bytes ' . $this->host_ . ':' . $this->port_ diff --git a/lib/php/lib/Transport/TSocketPool.php b/lib/php/lib/Transport/TSocketPool.php index cb9e8ddfa81..307885f507a 100644 --- a/lib/php/lib/Transport/TSocketPool.php +++ b/lib/php/lib/Transport/TSocketPool.php @@ -25,18 +25,18 @@ use Thrift\Exception\TException; /** - * This library makes use of APC cache to make hosts as down in a web - * environment. If you are running from the CLI or on a system without APC + * This library makes use of APCu cache to make hosts as down in a web + * environment. If you are running from the CLI or on a system without APCu * installed, then these null functions will step in and act like cache * misses. */ -if (!function_exists('apc_fetch')) { - function apc_fetch($key) +if (!function_exists('apcu_fetch')) { + function apcu_fetch($key) { return false; } - function apc_store($key, $var, $ttl = 0) + function apcu_store($key, $var, $ttl = 0) { return false; } @@ -202,11 +202,11 @@ public function open() // This extracts the $host and $port variables extract($this->servers_[$i]); - // Check APC cache for a record of this server being down + // Check APCu cache for a record of this server being down $failtimeKey = 'thrift_failtime:' . $host . ':' . $port . '~'; // Cache miss? Assume it's OK - $lastFailtime = apc_fetch($failtimeKey); + $lastFailtime = apcu_fetch($failtimeKey); if ($lastFailtime === false) { $lastFailtime = 0; } @@ -251,7 +251,7 @@ public function open() // Only clear the failure counts if required to do so if ($lastFailtime > 0) { - apc_store($failtimeKey, 0); + apcu_store($failtimeKey, 0); } // Successful connection, return now @@ -265,7 +265,7 @@ public function open() $consecfailsKey = 'thrift_consecfails:' . $host . ':' . $port . '~'; // Ignore cache misses - $consecfails = apc_fetch($consecfailsKey); + $consecfails = apcu_fetch($consecfailsKey); if ($consecfails === false) { $consecfails = 0; } @@ -284,12 +284,12 @@ public function open() ); } // Store the failure time - apc_store($failtimeKey, time()); + apcu_store($failtimeKey, time()); // Clear the count of consecutive failures - apc_store($consecfailsKey, 0); + apcu_store($consecfailsKey, 0); } else { - apc_store($consecfailsKey, $consecfails); + apcu_store($consecfailsKey, $consecfails); } } } diff --git a/lib/php/src/ext/thrift_protocol/php_thrift_protocol.cpp b/lib/php/src/ext/thrift_protocol/php_thrift_protocol.cpp index 63c8905a53d..c8bc50ecc54 100644 --- a/lib/php/src/ext/thrift_protocol/php_thrift_protocol.cpp +++ b/lib/php/src/ext/thrift_protocol/php_thrift_protocol.cpp @@ -414,7 +414,7 @@ void createObject(const char* obj_typename, zval* return_value, int nargs = 0, z object_and_properties_init(return_value, ce, nullptr); zend_function* constructor = zend_std_get_constructor(Z_OBJ_P(return_value)); zval ctor_rv; - zend_call_method(return_value, ce, &constructor, NULL, 0, &ctor_rv, nargs, arg1, arg2); + zend_call_method(return_value, ce, &constructor, nullptr, 0, &ctor_rv, nargs, arg1, arg2); zval_dtor(&ctor_rv); if (EG(exception)) { zend_object *ex = EG(exception); @@ -1013,7 +1013,7 @@ void binary_serialize_spec(zval* zthis, PHPOutputTransport& transport, HashTable zval* prop = zend_read_property(Z_OBJCE_P(zthis), zthis, varname, strlen(varname), false, &rv); if (Z_TYPE_P(prop) == IS_REFERENCE){ - ZVAL_UNREF(prop); + ZVAL_DEREF(prop); } if (Z_TYPE_P(prop) != IS_NULL) { transport.writeI8(ttype); diff --git a/lib/php/test/Makefile.am b/lib/php/test/Makefile.am index 48246882647..6f4e50a50b6 100755 --- a/lib/php/test/Makefile.am +++ b/lib/php/test/Makefile.am @@ -48,6 +48,8 @@ check: deps stubs \ check-validator \ check-json-serializer +distclean-local: + clean-local: $(RM) -r ./packages $(RM) TEST-*.xml diff --git a/lib/py/CMakeLists.txt b/lib/py/CMakeLists.txt index 7bb91fe6712..9c4368b6b9e 100644 --- a/lib/py/CMakeLists.txt +++ b/lib/py/CMakeLists.txt @@ -20,6 +20,7 @@ include_directories(${PYTHON_INCLUDE_DIRS}) add_custom_target(python_build ALL + COMMAND ${THRIFT_COMPILER} --gen py test/test_thrift_file/TestServer.thrift COMMAND ${PYTHON_EXECUTABLE} setup.py build WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} COMMENT "Building Python library" @@ -28,4 +29,9 @@ add_custom_target(python_build ALL if(BUILD_TESTING) add_test(PythonTestSSLSocket ${PYTHON_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/test/test_sslsocket.py) add_test(PythonThriftJson ${PYTHON_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/test/thrift_json.py) + add_test(PythonThriftTransport ${PYTHON_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/test/thrift_transport.py) + add_test(PythonThriftTBinaryProtocol ${PYTHON_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/test/thrift_TBinaryProtocol.py) + add_test(PythonThriftTZlibTransport ${PYTHON_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/test/thrift_TZlibTransport.py) + add_test(PythonThriftProtocol ${PYTHON_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/test/thrift_TCompactProtocol.py) + add_test(PythonThriftTNonblockingServer ${PYTHON_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/test/thrift_TNonblockingServer.py) endif() diff --git a/lib/py/Makefile.am b/lib/py/Makefile.am index 5861858f46e..b16305790d8 100644 --- a/lib/py/Makefile.am +++ b/lib/py/Makefile.am @@ -24,7 +24,12 @@ py3-build: $(PYTHON3) setup.py build py3-test: py3-build $(PYTHON3) test/thrift_json.py + $(PYTHON3) test/thrift_transport.py $(PYTHON3) test/test_sslsocket.py + $(PYTHON3) test/thrift_TBinaryProtocol.py + $(PYTHON3) test/thrift_TZlibTransport.py + $(PYTHON3) test/thrift_TCompactProtocol.py + $(PYTHON3) test/thrift_TNonblockingServer.py else py3-build: py3-test: @@ -32,6 +37,7 @@ endif all-local: py3-build $(PYTHON) setup.py build + ${THRIFT} --gen py test/test_thrift_file/TestServer.thrift # We're ignoring prefix here because site-packages seems to be # the equivalent of /usr/local/lib in Python land. @@ -40,12 +46,26 @@ all-local: py3-build install-exec-hook: $(PYTHON) setup.py install --root=$(DESTDIR) --prefix=$(PY_PREFIX) $(PYTHON_SETUPUTIL_ARGS) -clean-local: - $(RM) -r build - check-local: all py3-test $(PYTHON) test/thrift_json.py + $(PYTHON) test/thrift_transport.py $(PYTHON) test/test_sslsocket.py + $(PYTHON) test/test_socket.py + $(PYTHON) test/thrift_TBinaryProtocol.py + $(PYTHON) test/thrift_TZlibTransport.py + $(PYTHON) test/thrift_TCompactProtocol.py + $(PYTHON) test/thrift_TNonblockingServer.py + + +clean-local: + $(RM) -r build + $(RM) -r gen-py + find . -type f \( -iname "*.pyc" \) | xargs rm -f + find . -type d \( -iname "__pycache__" -or -iname "_trial_temp" \) | xargs rm -rf + +dist-hook: + find $(distdir) -type f \( -iname "*.pyc" \) | xargs rm -f + find $(distdir) -type d \( -iname "__pycache__" -or -iname "_trial_temp" \) | xargs rm -rf EXTRA_DIST = \ CMakeLists.txt \ diff --git a/lib/py/setup.py b/lib/py/setup.py index 6ee28df01d6..27a392d08c5 100644 --- a/lib/py/setup.py +++ b/lib/py/setup.py @@ -67,6 +67,7 @@ def run_setup(with_binary): extensions = dict( ext_modules=[ Extension('thrift.protocol.fastbinary', + extra_compile_args=['-std=c++11'], sources=[ 'src/ext/module.cpp', 'src/ext/types.cpp', @@ -90,9 +91,9 @@ def run_setup(with_binary): twisted_deps = ['twisted'] setup(name='thrift', - version='0.12.0', + version='0.14.0', description='Python bindings for the Apache Thrift RPC system', - author='Thrift Developers', + author='Apache Thrift Developers', author_email='dev@thrift.apache.org', url='http://thrift.apache.org', license='Apache License 2.0', diff --git a/lib/py/src/TMultiplexedProcessor.py b/lib/py/src/TMultiplexedProcessor.py index 3ac5af05c9e..ff88430bd0b 100644 --- a/lib/py/src/TMultiplexedProcessor.py +++ b/lib/py/src/TMultiplexedProcessor.py @@ -17,33 +17,61 @@ # under the License. # -from thrift.Thrift import TProcessor, TMessageType, TException +from thrift.Thrift import TProcessor, TMessageType from thrift.protocol import TProtocolDecorator, TMultiplexedProtocol +from thrift.protocol.TProtocol import TProtocolException class TMultiplexedProcessor(TProcessor): def __init__(self): + self.defaultProcessor = None self.services = {} + def registerDefault(self, processor): + """ + If a non-multiplexed processor connects to the server and wants to + communicate, use the given processor to handle it. This mechanism + allows servers to upgrade from non-multiplexed to multiplexed in a + backwards-compatible way and still handle old clients. + """ + self.defaultProcessor = processor + def registerProcessor(self, serviceName, processor): self.services[serviceName] = processor + def on_message_begin(self, func): + for key in self.services.keys(): + self.services[key].on_message_begin(func) + def process(self, iprot, oprot): (name, type, seqid) = iprot.readMessageBegin() if type != TMessageType.CALL and type != TMessageType.ONEWAY: - raise TException("TMultiplexed protocol only supports CALL & ONEWAY") + raise TProtocolException( + TProtocolException.NOT_IMPLEMENTED, + "TMultiplexedProtocol only supports CALL & ONEWAY") index = name.find(TMultiplexedProtocol.SEPARATOR) if index < 0: - raise TException("Service name not found in message name: " + name + ". Did you forget to use TMultiplexedProtocol in your client?") + if self.defaultProcessor: + return self.defaultProcessor.process( + StoredMessageProtocol(iprot, (name, type, seqid)), oprot) + else: + raise TProtocolException( + TProtocolException.NOT_IMPLEMENTED, + "Service name not found in message name: " + name + ". " + + "Did you forget to use TMultiplexedProtocol in your client?") serviceName = name[0:index] call = name[index + len(TMultiplexedProtocol.SEPARATOR):] if serviceName not in self.services: - raise TException("Service name not found: " + serviceName + ". Did you forget to call registerProcessor()?") + raise TProtocolException( + TProtocolException.NOT_IMPLEMENTED, + "Service name not found: " + serviceName + ". " + + "Did you forget to call registerProcessor()?") standardMessage = (call, type, seqid) - return self.services[serviceName].process(StoredMessageProtocol(iprot, standardMessage), oprot) + return self.services[serviceName].process( + StoredMessageProtocol(iprot, standardMessage), oprot) class StoredMessageProtocol(TProtocolDecorator.TProtocolDecorator): diff --git a/lib/py/src/Thrift.py b/lib/py/src/Thrift.py index 00941d8d536..ef655ea5579 100644 --- a/lib/py/src/Thrift.py +++ b/lib/py/src/Thrift.py @@ -17,8 +17,6 @@ # under the License. # -import sys - class TType(object): STOP = 0 @@ -72,21 +70,24 @@ class TProcessor(object): """Base class for processor, which works on two streams.""" def process(self, iprot, oprot): + """ + Process a request. The normal behvaior is to have the + processor invoke the correct handler and then it is the + server's responsibility to write the response to oprot. + """ + pass + + def on_message_begin(self, func): + """ + Install a callback that receives (name, type, seqid) + after the message header is read. + """ pass class TException(Exception): """Base class for all thrift exceptions.""" - # BaseException.message is deprecated in Python v[2.6,3.0) - if (2, 6, 0) <= sys.version_info < (3, 0): - def _get_message(self): - return self._message - - def _set_message(self, message): - self._message = message - message = property(_get_message, _set_message) - def __init__(self, message=None): Exception.__init__(self, message) self.message = message diff --git a/lib/py/src/ext/endian.h b/lib/py/src/ext/endian.h index 91372a7b6b7..1660cbd98e0 100644 --- a/lib/py/src/ext/endian.h +++ b/lib/py/src/ext/endian.h @@ -79,6 +79,10 @@ #include #define ntohll(n) bswap_64(n) #define htonll(n) bswap_64(n) +#elif defined(_MSC_VER) +#include +#define ntohll(n) _byteswap_uint64(n) +#define htonll(n) _byteswap_uint64(n) #else /* GNUC & GLIBC */ #define ntohll(n) ((((unsigned long long)ntohl(n)) << 32) + ntohl(n >> 32)) #define htonll(n) ((((unsigned long long)htonl(n)) << 32) + htonl(n >> 32)) diff --git a/lib/py/src/ext/module.cpp b/lib/py/src/ext/module.cpp index 7158b8fdfc3..f14ddaeb60a 100644 --- a/lib/py/src/ext/module.cpp +++ b/lib/py/src/ext/module.cpp @@ -48,20 +48,20 @@ namespace py { template static PyObject* encode_impl(PyObject* args) { if (!args) - return NULL; + return nullptr; - PyObject* enc_obj = NULL; - PyObject* type_args = NULL; + PyObject* enc_obj = nullptr; + PyObject* type_args = nullptr; if (!PyArg_ParseTuple(args, "OO", &enc_obj, &type_args)) { - return NULL; + return nullptr; } if (!enc_obj || !type_args) { - return NULL; + return nullptr; } T protocol; if (!protocol.prepareEncodeBuffer() || !protocol.encodeValue(enc_obj, T_STRUCT, type_args)) { - return NULL; + return nullptr; } return protocol.getEncodedValue(); @@ -79,11 +79,11 @@ static inline long as_long_then_delete(PyObject* value, long default_value) { template static PyObject* decode_impl(PyObject* args) { - PyObject* output_obj = NULL; - PyObject* oprot = NULL; - PyObject* typeargs = NULL; + PyObject* output_obj = nullptr; + PyObject* oprot = nullptr; + PyObject* typeargs = nullptr; if (!PyArg_ParseTuple(args, "OOO", &output_obj, &oprot, &typeargs)) { - return NULL; + return nullptr; } T protocol; @@ -96,16 +96,16 @@ static PyObject* decode_impl(PyObject* args) { default_limit)); ScopedPyObject transport(PyObject_GetAttr(oprot, INTERN_STRING(trans))); if (!transport) { - return NULL; + return nullptr; } StructTypeArgs parsedargs; if (!parse_struct_args(&parsedargs, typeargs)) { - return NULL; + return nullptr; } if (!protocol.prepareDecodeBufferFromTransport(transport.get())) { - return NULL; + return nullptr; } return protocol.readStruct(output_obj, parsedargs.klass, parsedargs.spec); @@ -141,22 +141,22 @@ static PyMethodDef ThriftFastBinaryMethods[] = { {"decode_binary", decode_binary, METH_VARARGS, ""}, {"encode_compact", encode_compact, METH_VARARGS, ""}, {"decode_compact", decode_compact, METH_VARARGS, ""}, - {NULL, NULL, 0, NULL} /* Sentinel */ + {nullptr, nullptr, 0, nullptr} /* Sentinel */ }; #if PY_MAJOR_VERSION >= 3 static struct PyModuleDef ThriftFastBinaryDef = {PyModuleDef_HEAD_INIT, "thrift.protocol.fastbinary", - NULL, + nullptr, 0, ThriftFastBinaryMethods, - NULL, - NULL, - NULL, - NULL}; + nullptr, + nullptr, + nullptr, + nullptr}; -#define INITERROR return NULL; +#define INITERROR return nullptr; PyObject* PyInit_fastbinary() { @@ -167,7 +167,7 @@ PyObject* PyInit_fastbinary() { void initfastbinary() { PycString_IMPORT; - if (PycStringIO == NULL) + if (PycStringIO == nullptr) INITERROR #endif @@ -193,7 +193,7 @@ void initfastbinary() { #else Py_InitModule("thrift.protocol.fastbinary", ThriftFastBinaryMethods); #endif - if (module == NULL) + if (module == nullptr) INITERROR; #if PY_MAJOR_VERSION >= 3 diff --git a/lib/py/src/ext/protocol.h b/lib/py/src/ext/protocol.h index 521b7ee9289..c0cd43724ac 100644 --- a/lib/py/src/ext/protocol.h +++ b/lib/py/src/ext/protocol.h @@ -35,7 +35,7 @@ class ProtocolBase { ProtocolBase() : stringLimit_((std::numeric_limits::max)()), containerLimit_((std::numeric_limits::max)()), - output_(NULL) {} + output_(nullptr) {} inline virtual ~ProtocolBase(); bool prepareDecodeBufferFromTransport(PyObject* trans); diff --git a/lib/py/src/ext/protocol.tcc b/lib/py/src/ext/protocol.tcc index e15df7ea0fd..aad5a3c88e5 100644 --- a/lib/py/src/ext/protocol.tcc +++ b/lib/py/src/ext/protocol.tcc @@ -48,7 +48,7 @@ inline EncodeBuffer* new_encode_buffer(size_t size) { PycString_IMPORT; } if (!PycStringIO) { - return NULL; + return nullptr; } return PycStringIO->NewOutput(size); } @@ -83,7 +83,7 @@ PyObject* ProtocolBase::getEncodedValue() { PycString_IMPORT; } if (!PycStringIO) { - return NULL; + return nullptr; } return PycStringIO->cgetvalue(output_); } @@ -174,7 +174,7 @@ inline bool ProtocolBase::writeBuffer(char* data, size_t size) { if (output_->buf.capacity() < need) { try { output_->buf.reserve(need); - } catch (std::bad_alloc& ex) { + } catch (std::bad_alloc&) { PyErr_SetString(PyExc_MemoryError, "Failed to allocate write buffer"); return false; } @@ -277,7 +277,7 @@ bool ProtocolBase::readBytes(char** output, int len) { } else { // using building functions as this is a rare codepath ScopedPyObject newiobuf(PyObject_CallFunction(input_.refill_callable.get(), refill_signature, - *output, rlen, len, NULL)); + *output, rlen, len, nullptr)); if (!newiobuf) { return false; } @@ -332,7 +332,7 @@ bool ProtocolBase::prepareDecodeBufferFromTransport(PyObject* trans) { template bool ProtocolBase::prepareEncodeBuffer() { output_ = detail::new_encode_buffer(INIT_OUTBUF_SIZE); - return output_ != NULL; + return output_ != nullptr; } template @@ -484,8 +484,8 @@ bool ProtocolBase::encodeValue(PyObject* value, TType type, PyObject* type return false; } Py_ssize_t pos = 0; - PyObject* k = NULL; - PyObject* v = NULL; + PyObject* k = nullptr; + PyObject* v = nullptr; // TODO(bmaurer): should support any mapping, not just dicts while (PyDict_Next(value, &pos, &k, &v)) { if (!encodeValue(k, parsedargs.ktag, parsedargs.ktypeargs) @@ -647,7 +647,7 @@ PyObject* ProtocolBase::decodeValue(TType type, PyObject* typeargs) { case T_BOOL: { bool v = 0; if (!impl()->readBool(v)) { - return NULL; + return nullptr; } if (v) { Py_RETURN_TRUE; @@ -658,21 +658,21 @@ PyObject* ProtocolBase::decodeValue(TType type, PyObject* typeargs) { case T_I08: { int8_t v = 0; if (!impl()->readI8(v)) { - return NULL; + return nullptr; } return PyInt_FromLong(v); } case T_I16: { int16_t v = 0; if (!impl()->readI16(v)) { - return NULL; + return nullptr; } return PyInt_FromLong(v); } case T_I32: { int32_t v = 0; if (!impl()->readI32(v)) { - return NULL; + return nullptr; } return PyInt_FromLong(v); } @@ -680,7 +680,7 @@ PyObject* ProtocolBase::decodeValue(TType type, PyObject* typeargs) { case T_I64: { int64_t v = 0; if (!impl()->readI64(v)) { - return NULL; + return nullptr; } // TODO(dreiss): Find out if we can take this fastpath always when // sizeof(long) == sizeof(long long). @@ -693,19 +693,19 @@ PyObject* ProtocolBase::decodeValue(TType type, PyObject* typeargs) { case T_DOUBLE: { double v = 0.0; if (!impl()->readDouble(v)) { - return NULL; + return nullptr; } return PyFloat_FromDouble(v); } case T_STRING: { - char* buf = NULL; + char* buf = nullptr; int len = impl()->readString(&buf); if (len < 0) { - return NULL; + return nullptr; } if (isUtf8(typeargs)) { - return PyUnicode_DecodeUTF8(buf, len, 0); + return PyUnicode_DecodeUTF8(buf, len, "replace"); } else { return PyBytes_FromStringAndSize(buf, len); } @@ -715,28 +715,28 @@ PyObject* ProtocolBase::decodeValue(TType type, PyObject* typeargs) { case T_SET: { SetListTypeArgs parsedargs; if (!parse_set_list_args(&parsedargs, typeargs)) { - return NULL; + return nullptr; } TType etype = T_STOP; int32_t len = impl()->readListBegin(etype); if (len < 0) { - return NULL; + return nullptr; } if (len > 0 && !checkType(etype, parsedargs.element_type)) { - return NULL; + return nullptr; } bool use_tuple = type == T_LIST && parsedargs.immutable; ScopedPyObject ret(use_tuple ? PyTuple_New(len) : PyList_New(len)); if (!ret) { - return NULL; + return nullptr; } for (int i = 0; i < len; i++) { PyObject* item = decodeValue(etype, parsedargs.typeargs); if (!item) { - return NULL; + return nullptr; } if (use_tuple) { PyTuple_SET_ITEM(ret.get(), i, item); @@ -758,32 +758,32 @@ PyObject* ProtocolBase::decodeValue(TType type, PyObject* typeargs) { case T_MAP: { MapTypeArgs parsedargs; if (!parse_map_args(&parsedargs, typeargs)) { - return NULL; + return nullptr; } TType ktype = T_STOP; TType vtype = T_STOP; uint32_t len = impl()->readMapBegin(ktype, vtype); if (len > 0 && (!checkType(ktype, parsedargs.ktag) || !checkType(vtype, parsedargs.vtag))) { - return NULL; + return nullptr; } ScopedPyObject ret(PyDict_New()); if (!ret) { - return NULL; + return nullptr; } for (uint32_t i = 0; i < len; i++) { ScopedPyObject k(decodeValue(ktype, parsedargs.ktypeargs)); if (!k) { - return NULL; + return nullptr; } ScopedPyObject v(decodeValue(vtype, parsedargs.vtypeargs)); if (!v) { - return NULL; + return nullptr; } if (PyDict_SetItem(ret.get(), k.get(), v.get()) == -1) { - return NULL; + return nullptr; } } @@ -792,12 +792,12 @@ PyObject* ProtocolBase::decodeValue(TType type, PyObject* typeargs) { ThriftModule = PyImport_ImportModule("thrift.Thrift"); } if (!ThriftModule) { - return NULL; + return nullptr; } ScopedPyObject cls(PyObject_GetAttr(ThriftModule, INTERN_STRING(TFrozenDict))); if (!cls) { - return NULL; + return nullptr; } ScopedPyObject arg(PyTuple_New(1)); @@ -811,7 +811,7 @@ PyObject* ProtocolBase::decodeValue(TType type, PyObject* typeargs) { case T_STRUCT: { StructTypeArgs parsedargs; if (!parse_struct_args(&parsedargs, typeargs)) { - return NULL; + return nullptr; } return readStruct(Py_None, parsedargs.klass, parsedargs.spec); } @@ -823,7 +823,7 @@ PyObject* ProtocolBase::decodeValue(TType type, PyObject* typeargs) { case T_U64: default: PyErr_Format(PyExc_TypeError, "Unexpected TType for decodeValue: %d", type); - return NULL; + return nullptr; } } @@ -833,26 +833,26 @@ PyObject* ProtocolBase::readStruct(PyObject* output, PyObject* klass, PyOb bool immutable = output == Py_None; ScopedPyObject kwargs; if (spec_seq_len == -1) { - return NULL; + return nullptr; } if (immutable) { kwargs.reset(PyDict_New()); if (!kwargs) { PyErr_SetString(PyExc_TypeError, "failed to prepare kwargument storage"); - return NULL; + return nullptr; } } detail::ReadStructScope scope = detail::readStructScope(this); if (!scope) { - return NULL; + return nullptr; } while (true) { TType type = T_STOP; int16_t tag; if (!impl()->readFieldBegin(type, tag)) { - return NULL; + return nullptr; } if (type == T_STOP) { break; @@ -860,7 +860,7 @@ PyObject* ProtocolBase::readStruct(PyObject* output, PyObject* klass, PyOb if (tag < 0 || tag >= spec_seq_len) { if (!skip(type)) { PyErr_SetString(PyExc_TypeError, "Error while skipping unknown field"); - return NULL; + return nullptr; } continue; } @@ -869,38 +869,38 @@ PyObject* ProtocolBase::readStruct(PyObject* output, PyObject* klass, PyOb if (item_spec == Py_None) { if (!skip(type)) { PyErr_SetString(PyExc_TypeError, "Error while skipping unknown field"); - return NULL; + return nullptr; } continue; } StructItemSpec parsedspec; if (!parse_struct_item_spec(&parsedspec, item_spec)) { - return NULL; + return nullptr; } if (parsedspec.type != type) { if (!skip(type)) { PyErr_Format(PyExc_TypeError, "struct field had wrong type: expected %d but got %d", parsedspec.type, type); - return NULL; + return nullptr; } continue; } ScopedPyObject fieldval(decodeValue(parsedspec.type, parsedspec.typeargs)); if (!fieldval) { - return NULL; + return nullptr; } if ((immutable && PyDict_SetItem(kwargs.get(), parsedspec.attrname, fieldval.get()) == -1) || (!immutable && PyObject_SetAttr(output, parsedspec.attrname, fieldval.get()) == -1)) { - return NULL; + return nullptr; } } if (immutable) { ScopedPyObject args(PyTuple_New(0)); if (!args) { PyErr_SetString(PyExc_TypeError, "failed to prepare argument storage"); - return NULL; + return nullptr; } return PyObject_Call(klass, args.get(), kwargs.get()); } diff --git a/lib/py/src/ext/types.cpp b/lib/py/src/ext/types.cpp index 68443fbe83d..e8d6939b1d2 100644 --- a/lib/py/src/ext/types.cpp +++ b/lib/py/src/ext/types.cpp @@ -24,7 +24,7 @@ namespace apache { namespace thrift { namespace py { -PyObject* ThriftModule = NULL; +PyObject* ThriftModule = nullptr; #if PY_MAJOR_VERSION < 3 char refill_signature[] = {'s', '#', 'i'}; diff --git a/lib/py/src/ext/types.h b/lib/py/src/ext/types.h index 5cd8dda9e01..9b45dd065f5 100644 --- a/lib/py/src/ext/types.h +++ b/lib/py/src/ext/types.h @@ -83,7 +83,7 @@ enum TType { // replace with unique_ptr when we're OK with C++11 class ScopedPyObject { public: - ScopedPyObject() : obj_(NULL) {} + ScopedPyObject() : obj_(nullptr) {} explicit ScopedPyObject(PyObject* py_object) : obj_(py_object) {} ~ScopedPyObject() { if (obj_) @@ -98,7 +98,7 @@ class ScopedPyObject { } PyObject* release() throw() { PyObject* tmp = obj_; - obj_ = NULL; + obj_ = nullptr; return tmp; } void swap(ScopedPyObject& other) throw() { diff --git a/lib/py/src/protocol/TBase.py b/lib/py/src/protocol/TBase.py index 9ae1b118272..6c6ef18e877 100644 --- a/lib/py/src/protocol/TBase.py +++ b/lib/py/src/protocol/TBase.py @@ -80,3 +80,7 @@ def read(cls, iprot): [self.__class__, self.thrift_spec]) else: return iprot.readStruct(cls, cls.thrift_spec, True) + + +class TFrozenExceptionBase(TFrozenBase, TExceptionBase): + pass diff --git a/lib/py/src/protocol/TBinaryProtocol.py b/lib/py/src/protocol/TBinaryProtocol.py index f6be7721a5c..6b2facc4f72 100644 --- a/lib/py/src/protocol/TBinaryProtocol.py +++ b/lib/py/src/protocol/TBinaryProtocol.py @@ -17,7 +17,7 @@ # under the License. # -from .TProtocol import TType, TProtocolBase, TProtocolException +from .TProtocol import TType, TProtocolBase, TProtocolException, TProtocolFactory from struct import pack, unpack @@ -235,7 +235,7 @@ def readBinary(self): return s -class TBinaryProtocolFactory(object): +class TBinaryProtocolFactory(TProtocolFactory): def __init__(self, strictRead=False, strictWrite=True, **kwargs): self.strictRead = strictRead self.strictWrite = strictWrite @@ -284,7 +284,7 @@ def __init__(self, *args, **kwargs): self._fast_encode = fastbinary.encode_binary -class TBinaryProtocolAcceleratedFactory(object): +class TBinaryProtocolAcceleratedFactory(TProtocolFactory): def __init__(self, string_length_limit=None, container_length_limit=None, diff --git a/lib/py/src/protocol/TCompactProtocol.py b/lib/py/src/protocol/TCompactProtocol.py index e485cffb145..700e792f79b 100644 --- a/lib/py/src/protocol/TCompactProtocol.py +++ b/lib/py/src/protocol/TCompactProtocol.py @@ -17,7 +17,7 @@ # under the License. # -from .TProtocol import TType, TProtocolBase, TProtocolException, checkIntegerLimits +from .TProtocol import TType, TProtocolBase, TProtocolException, TProtocolFactory, checkIntegerLimits from struct import pack, unpack from ..compat import binary_to_str, str_to_binary @@ -58,6 +58,7 @@ def fromZigZag(n): def writeVarint(trans, n): + assert n >= 0, "Input to TCompactProtocol writeVarint cannot be negative!" out = bytearray() while True: if n & ~0x7f == 0: @@ -156,7 +157,14 @@ def writeMessageBegin(self, name, type, seqid): assert self.state == CLEAR self.__writeUByte(self.PROTOCOL_ID) self.__writeUByte(self.VERSION | (type << self.TYPE_SHIFT_AMOUNT)) - self.__writeVarint(seqid) + # The sequence id is a signed 32-bit integer but the compact protocol + # writes this out as a "var int" which is always positive, and attempting + # to write a negative number results in an infinite loop, so we may + # need to do some conversion here... + tseqid = seqid + if tseqid < 0: + tseqid = 2147483648 + (2147483648 + tseqid) + self.__writeVarint(tseqid) self.__writeBinary(str_to_binary(name)) self.state = VALUE_WRITE @@ -334,6 +342,10 @@ def readMessageBegin(self): raise TProtocolException(TProtocolException.BAD_VERSION, 'Bad version: %d (expect %d)' % (version, self.VERSION)) seqid = self.__readVarint() + # the sequence is a compact "var int" which is treaded as unsigned, + # however the sequence is actually signed... + if seqid > 2147483647: + seqid = -2147483648 - (2147483648 - seqid) name = binary_to_str(self.__readBinary()) return (name, type, seqid) @@ -416,7 +428,7 @@ def __getTType(self, byte): return TTYPES[byte & 0x0f] -class TCompactProtocolFactory(object): +class TCompactProtocolFactory(TProtocolFactory): def __init__(self, string_length_limit=None, container_length_limit=None): @@ -458,7 +470,7 @@ def __init__(self, *args, **kwargs): self._fast_encode = fastbinary.encode_compact -class TCompactProtocolAcceleratedFactory(object): +class TCompactProtocolAcceleratedFactory(TProtocolFactory): def __init__(self, string_length_limit=None, container_length_limit=None, diff --git a/lib/py/src/protocol/THeaderProtocol.py b/lib/py/src/protocol/THeaderProtocol.py index b27a7499539..4b58e639da2 100644 --- a/lib/py/src/protocol/THeaderProtocol.py +++ b/lib/py/src/protocol/THeaderProtocol.py @@ -19,7 +19,7 @@ from thrift.protocol.TBinaryProtocol import TBinaryProtocolAccelerated from thrift.protocol.TCompactProtocol import TCompactProtocolAccelerated -from thrift.protocol.TProtocol import TProtocolBase, TProtocolException +from thrift.protocol.TProtocol import TProtocolBase, TProtocolException, TProtocolFactory from thrift.Thrift import TApplicationException, TMessageType from thrift.transport.THeaderTransport import THeaderTransport, THeaderSubprotocolID, THeaderClientType @@ -35,7 +35,9 @@ class THeaderProtocol(TProtocolBase): THeaderProtocol frames other Thrift protocols and adds support for optional out-of-band headers. The currently supported subprotocols are - TBinaryProtocol and TCompactProtocol. + TBinaryProtocol and TCompactProtocol. When used as a client, the + subprotocol to frame can be chosen with the `default_protocol` parameter to + the constructor. It's also possible to apply transforms to the encoded message payload. The only transform currently supported is to gzip. @@ -53,14 +55,14 @@ class THeaderProtocol(TProtocolBase): """ - def __init__(self, transport, allowed_client_types): + def __init__(self, transport, allowed_client_types, default_protocol=THeaderSubprotocolID.BINARY): # much of the actual work for THeaderProtocol happens down in # THeaderTransport since we need to do low-level shenanigans to detect # if the client is sending us headers or one of the headerless formats # we support. this wraps the real transport with the one that does all # the magic. if not isinstance(transport, THeaderTransport): - transport = THeaderTransport(transport, allowed_client_types) + transport = THeaderTransport(transport, allowed_client_types, default_protocol) super(THeaderProtocol, self).__init__(transport) self._set_protocol() @@ -217,9 +219,14 @@ def readBinary(self): return self._protocol.readBinary() -class THeaderProtocolFactory(object): - def __init__(self, allowed_client_types=(THeaderClientType.HEADERS,)): +class THeaderProtocolFactory(TProtocolFactory): + def __init__( + self, + allowed_client_types=(THeaderClientType.HEADERS,), + default_protocol=THeaderSubprotocolID.BINARY, + ): self.allowed_client_types = allowed_client_types + self.default_protocol = default_protocol def getProtocol(self, trans): - return THeaderProtocol(trans, self.allowed_client_types) + return THeaderProtocol(trans, self.allowed_client_types, self.default_protocol) diff --git a/lib/py/src/protocol/TJSONProtocol.py b/lib/py/src/protocol/TJSONProtocol.py index db2099a345b..17417027a0a 100644 --- a/lib/py/src/protocol/TJSONProtocol.py +++ b/lib/py/src/protocol/TJSONProtocol.py @@ -18,7 +18,7 @@ # from .TProtocol import (TType, TProtocolBase, TProtocolException, - checkIntegerLimits) + TProtocolFactory, checkIntegerLimits) import base64 import math import sys @@ -577,7 +577,7 @@ def writeBinary(self, binary): self.writeJSONBase64(binary) -class TJSONProtocolFactory(object): +class TJSONProtocolFactory(TProtocolFactory): def getProtocol(self, trans): return TJSONProtocol(trans) @@ -671,7 +671,7 @@ def writeBinary(self, binary): self.writeJSONBase64(binary) -class TSimpleJSONProtocolFactory(object): +class TSimpleJSONProtocolFactory(TProtocolFactory): def getProtocol(self, trans): return TSimpleJSONProtocol(trans) diff --git a/lib/py/src/protocol/TProtocol.py b/lib/py/src/protocol/TProtocol.py index 8314cf69df8..339a2839d71 100644 --- a/lib/py/src/protocol/TProtocol.py +++ b/lib/py/src/protocol/TProtocol.py @@ -191,9 +191,7 @@ def readUtf8(self): return self.readString().decode('utf8') def skip(self, ttype): - if ttype == TType.STOP: - return - elif ttype == TType.BOOL: + if ttype == TType.BOOL: self.readBool() elif ttype == TType.BYTE: self.readByte() @@ -232,6 +230,10 @@ def skip(self, ttype): for i in range(size): self.skip(etype) self.readListEnd() + else: + raise TProtocolException( + TProtocolException.INVALID_DATA, + "invalid TType") # tuple of: ( 'reader method' name, is_container bool, 'writer_method' name ) _TTYPE_HANDLERS = ( @@ -301,8 +303,14 @@ def readContainerSet(self, spec): def readContainerStruct(self, spec): (obj_class, obj_spec) = spec - obj = obj_class() - obj.read(self) + + # If obj_class.read is a classmethod (e.g. in frozen structs), + # call it as such. + if getattr(obj_class.read, '__self__', None) is obj_class: + obj = obj_class.read(self) + else: + obj = obj_class() + obj.read(self) return obj def readContainerMap(self, spec): diff --git a/lib/py/src/protocol/__init__.py b/lib/py/src/protocol/__init__.py index 7148f66b334..06647a24b9f 100644 --- a/lib/py/src/protocol/__init__.py +++ b/lib/py/src/protocol/__init__.py @@ -18,4 +18,4 @@ # __all__ = ['fastbinary', 'TBase', 'TBinaryProtocol', 'TCompactProtocol', - 'TJSONProtocol', 'TProtocol'] + 'TJSONProtocol', 'TProtocol', 'TProtocolDecorator'] diff --git a/lib/py/src/server/THttpServer.py b/lib/py/src/server/THttpServer.py index 85cf4005eab..47e817df773 100644 --- a/lib/py/src/server/THttpServer.py +++ b/lib/py/src/server/THttpServer.py @@ -21,6 +21,7 @@ from six.moves import BaseHTTPServer +from thrift.Thrift import TMessageType from thrift.server import TServer from thrift.transport import TTransport @@ -32,7 +33,9 @@ class ResponseException(Exception): to override this behavior (e.g., to simulate a misconfigured or overloaded web server during testing), it can raise a ResponseException. The function passed to the constructor will be called with the - RequestHandler as its only argument. + RequestHandler as its only argument. Note that this is irrelevant + for ONEWAY requests, as the HTTP response must be sent before the + RPC is processed. """ def __init__(self, handler): self.handler = handler @@ -43,6 +46,9 @@ class THttpServer(TServer.TServer): This class is not very performant, but it is useful (for example) for acting as a mock version of an Apache-based PHP Thrift endpoint. + Also important to note the HTTP implementation pretty much violates the + transport/protocol/processor/server layering, by performing the transport + functions here. This means things like oneway handling are oddly exposed. """ def __init__(self, processor, @@ -68,26 +74,45 @@ def __init__(self, inputProtocolFactory, outputProtocolFactory) thttpserver = self + self._replied = None class RequestHander(BaseHTTPServer.BaseHTTPRequestHandler): def do_POST(self): # Don't care about the request path. - itrans = TTransport.TFileObjectTransport(self.rfile) - otrans = TTransport.TFileObjectTransport(self.wfile) + thttpserver._replied = False + iftrans = TTransport.TFileObjectTransport(self.rfile) itrans = TTransport.TBufferedTransport( - itrans, int(self.headers['Content-Length'])) + iftrans, int(self.headers['Content-Length'])) otrans = TTransport.TMemoryBuffer() iprot = thttpserver.inputProtocolFactory.getProtocol(itrans) oprot = thttpserver.outputProtocolFactory.getProtocol(otrans) try: + thttpserver.processor.on_message_begin(self.on_begin) thttpserver.processor.process(iprot, oprot) except ResponseException as exn: exn.handler(self) else: + if not thttpserver._replied: + # If the request was ONEWAY we would have replied already + data = otrans.getvalue() + self.send_response(200) + self.send_header("Content-Length", len(data)) + self.send_header("Content-Type", "application/x-thrift") + self.end_headers() + self.wfile.write(data) + + def on_begin(self, name, type, seqid): + """ + Inspect the message header. + + This allows us to post an immediate transport response + if the request is a ONEWAY message type. + """ + if type == TMessageType.ONEWAY: self.send_response(200) - self.send_header("content-type", "application/x-thrift") + self.send_header("Content-Type", "application/x-thrift") self.end_headers() - self.wfile.write(otrans.getvalue()) + thttpserver._replied = True self.httpd = server_class(server_address, RequestHander) diff --git a/lib/py/src/transport/THeaderTransport.py b/lib/py/src/transport/THeaderTransport.py index c0d56401229..7c9827ba3a9 100644 --- a/lib/py/src/transport/THeaderTransport.py +++ b/lib/py/src/transport/THeaderTransport.py @@ -87,7 +87,7 @@ def _writeString(trans, value): class THeaderTransport(TTransportBase, CReadableTransport): - def __init__(self, transport, allowed_client_types): + def __init__(self, transport, allowed_client_types, default_protocol=THeaderSubprotocolID.BINARY): self._transport = transport self._client_type = THeaderClientType.HEADERS self._allowed_client_types = allowed_client_types @@ -101,7 +101,7 @@ def __init__(self, transport, allowed_client_types): self.flags = 0 self.sequence_id = 0 - self._protocol_id = THeaderSubprotocolID.BINARY + self._protocol_id = default_protocol self._max_frame_size = HARD_MAX_FRAME_SIZE def isOpen(self): diff --git a/lib/py/src/transport/THttpClient.py b/lib/py/src/transport/THttpClient.py index 37b0a4d8ddc..212da3aa50c 100644 --- a/lib/py/src/transport/THttpClient.py +++ b/lib/py/src/transport/THttpClient.py @@ -185,3 +185,7 @@ def flush(self): self.code = self.__http_response.status self.message = self.__http_response.reason self.headers = self.__http_response.msg + + # Saves the cookie sent by the server response + if 'Set-Cookie' in self.headers: + self.__http.putheader('Cookie', self.headers['Set-Cookie']) diff --git a/lib/py/src/transport/TSSLSocket.py b/lib/py/src/transport/TSSLSocket.py index b54ca5dd9c2..5b3ae599194 100644 --- a/lib/py/src/transport/TSSLSocket.py +++ b/lib/py/src/transport/TSSLSocket.py @@ -79,8 +79,8 @@ def ssl_context(self): SSL_VERSION = _default_protocol """ Default SSL version. - For backword compatibility, it can be modified. - Use __init__ keywoard argument "ssl_version" instead. + For backwards compatibility, it can be modified. + Use __init__ keyword argument "ssl_version" instead. """ def _deprecated_arg(self, args, kwargs, pos, key): @@ -89,12 +89,12 @@ def _deprecated_arg(self, args, kwargs, pos, key): real_pos = pos + 3 warnings.warn( '%dth positional argument is deprecated.' - 'please use keyward argument insteand.' + 'please use keyword argument instead.' % real_pos, DeprecationWarning, stacklevel=3) if key in kwargs: raise TypeError( - 'Duplicate argument: %dth argument and %s keyward argument.' + 'Duplicate argument: %dth argument and %s keyword argument.' % (real_pos, key)) kwargs[key] = args[pos] @@ -118,7 +118,7 @@ def __init__(self, server_side, host, ssl_opts): if TSSLBase.SSL_VERSION != self._default_protocol: warnings.warn( 'SSL_VERSION is deprecated.' - 'please use ssl_version keyward argument instead.', + 'please use ssl_version keyword argument instead.', DeprecationWarning, stacklevel=2) self._context = ssl_opts.pop('ssl_context', None) self._server_hostname = None @@ -232,6 +232,7 @@ def __init__(self, host='localhost', port=9090, *args, **kwargs): ``validate_callback`` (cert, hostname) -> None: Called after SSL handshake. Can raise when hostname does not match the cert. + ``socket_keepalive`` enable TCP keepalive, default off. """ self.is_valid = False self.peercert = None @@ -259,9 +260,20 @@ def __init__(self, host='localhost', port=9090, *args, **kwargs): kwargs['cert_reqs'] = ssl.CERT_REQUIRED if validate else ssl.CERT_NONE unix_socket = kwargs.pop('unix_socket', None) + socket_keepalive = kwargs.pop('socket_keepalive', False) self._validate_callback = kwargs.pop('validate_callback', _match_hostname) TSSLBase.__init__(self, False, host, kwargs) - TSocket.TSocket.__init__(self, host, port, unix_socket) + TSocket.TSocket.__init__(self, host, port, unix_socket, + socket_keepalive=socket_keepalive) + + def close(self): + try: + self.handle.settimeout(0.001) + self.handle = self.handle.unwrap() + except (ssl.SSLError, socket.error, OSError): + # could not complete shutdown in a reasonable amount of time. bail. + pass + TSocket.TSocket.close(self) @property def validate(self): @@ -279,11 +291,11 @@ def _do_open(self, family, socktype): plain_sock = socket.socket(family, socktype) try: return self._wrap_socket(plain_sock) - except Exception: + except Exception as ex: plain_sock.close() msg = 'failed to initialize SSL' logger.exception(msg) - raise TTransportException(TTransportException.NOT_OPEN, msg) + raise TTransportException(type=TTransportException.NOT_OPEN, message=msg, inner=ex) def open(self): super(TSSLSocket, self).open() @@ -295,7 +307,7 @@ def open(self): except TTransportException: raise except Exception as ex: - raise TTransportException(TTransportException.UNKNOWN, str(ex)) + raise TTransportException(message=str(ex), inner=ex) class TSSLServerSocket(TSocket.TServerSocket, TSSLBase): diff --git a/lib/py/src/transport/TSocket.py b/lib/py/src/transport/TSocket.py index a7d661703dc..3c7a3ca7db6 100644 --- a/lib/py/src/transport/TSocket.py +++ b/lib/py/src/transport/TSocket.py @@ -39,7 +39,7 @@ def _resolveAddr(self): self._socket_family, socket.SOCK_STREAM, 0, - socket.AI_PASSIVE | socket.AI_ADDRCONFIG) + socket.AI_PASSIVE) def close(self): if self.handle: @@ -50,7 +50,9 @@ def close(self): class TSocket(TSocketBase): """Socket implementation of TTransport base.""" - def __init__(self, host='localhost', port=9090, unix_socket=None, socket_family=socket.AF_UNSPEC): + def __init__(self, host='localhost', port=9090, unix_socket=None, + socket_family=socket.AF_UNSPEC, + socket_keepalive=False): """Initialize a TSocket @param host(str) The host to connect to. @@ -58,6 +60,7 @@ def __init__(self, host='localhost', port=9090, unix_socket=None, socket_family= @param unix_socket(str) The filename of a unix socket to connect to. (host and port will be ignored.) @param socket_family(int) The socket family to use with this socket. + @param socket_keepalive(bool) enable TCP keepalive, default off. """ self.host = host self.port = port @@ -65,12 +68,37 @@ def __init__(self, host='localhost', port=9090, unix_socket=None, socket_family= self._unix_socket = unix_socket self._timeout = None self._socket_family = socket_family + self._socket_keepalive = socket_keepalive def setHandle(self, h): self.handle = h def isOpen(self): - return self.handle is not None + if self.handle is None: + return False + + # this lets us cheaply see if the other end of the socket is still + # connected. if disconnected, we'll get EOF back (expressed as zero + # bytes of data) otherwise we'll get one byte or an error indicating + # we'd have to block for data. + # + # note that we're not doing this with socket.MSG_DONTWAIT because 1) + # it's linux-specific and 2) gevent-patched sockets hide EAGAIN from us + # when timeout is non-zero. + original_timeout = self.handle.gettimeout() + try: + self.handle.settimeout(0) + try: + peeked_bytes = self.handle.recv(1, socket.MSG_PEEK) + except (socket.error, OSError) as exc: # on modern python this is just BlockingIOError + if exc.errno in (errno.EWOULDBLOCK, errno.EAGAIN): + return True + return False + finally: + self.handle.settimeout(original_timeout) + + # the length will be zero if we got EOF (indicating connection closed) + return len(peeked_bytes) == 1 def setTimeout(self, ms): if ms is None: @@ -90,15 +118,20 @@ def _address(self): def open(self): if self.handle: - raise TTransportException(TTransportException.ALREADY_OPEN) + raise TTransportException(type=TTransportException.ALREADY_OPEN, message="already open") try: addrs = self._resolveAddr() - except socket.gaierror: + except socket.gaierror as gai: msg = 'failed to resolve sockaddr for ' + str(self._address) logger.exception(msg) - raise TTransportException(TTransportException.NOT_OPEN, msg) + raise TTransportException(type=TTransportException.NOT_OPEN, message=msg, inner=gai) for family, socktype, _, _, sockaddr in addrs: handle = self._do_open(family, socktype) + + # TCP_KEEPALIVE + if self._socket_keepalive: + handle.setsockopt(socket.IPPROTO_TCP, socket.SO_KEEPALIVE, 1) + handle.settimeout(self._timeout) try: handle.connect(sockaddr) @@ -110,7 +143,7 @@ def open(self): msg = 'Could not connect to any of %s' % list(map(lambda a: a[4], addrs)) logger.error(msg) - raise TTransportException(TTransportException.NOT_OPEN, msg) + raise TTransportException(type=TTransportException.NOT_OPEN, message=msg) def read(self, sz): try: @@ -125,8 +158,10 @@ def read(self, sz): self.close() # Trigger the check to raise the END_OF_FILE exception below. buff = '' + elif e.args[0] == errno.ETIMEDOUT: + raise TTransportException(type=TTransportException.TIMED_OUT, message="read timeout", inner=e) else: - raise + raise TTransportException(message="unexpected exception", inner=e) if len(buff) == 0: raise TTransportException(type=TTransportException.END_OF_FILE, message='TSocket read 0 bytes') @@ -139,12 +174,15 @@ def write(self, buff): sent = 0 have = len(buff) while sent < have: - plus = self.handle.send(buff) - if plus == 0: - raise TTransportException(type=TTransportException.END_OF_FILE, - message='TSocket sent 0 bytes') - sent += plus - buff = buff[plus:] + try: + plus = self.handle.send(buff) + if plus == 0: + raise TTransportException(type=TTransportException.END_OF_FILE, + message='TSocket sent 0 bytes') + sent += plus + buff = buff[plus:] + except socket.error as e: + raise TTransportException(message="unexpected exception", inner=e) def flush(self): pass diff --git a/lib/py/src/transport/TTransport.py b/lib/py/src/transport/TTransport.py index d13060f810f..9dbe95df498 100644 --- a/lib/py/src/transport/TTransport.py +++ b/lib/py/src/transport/TTransport.py @@ -34,9 +34,10 @@ class TTransportException(TException): SIZE_LIMIT = 6 INVALID_CLIENT_TYPE = 7 - def __init__(self, type=UNKNOWN, message=None): + def __init__(self, type=UNKNOWN, message=None, inner=None): TException.__init__(self, message) self.type = type + self.inner = inner class TTransportBase(object): @@ -376,7 +377,7 @@ def open(self): if not self.transport.isOpen(): self.transport.open() - self.send_sasl_msg(self.START, self.sasl.mechanism) + self.send_sasl_msg(self.START, bytes(self.sasl.mechanism, 'ascii')) self.send_sasl_msg(self.OK, self.sasl.process()) while True: @@ -417,7 +418,7 @@ def write(self, data): def flush(self): data = self.__wbuf.getvalue() encoded = self.sasl.wrap(data) - self.transport.write(''.join((pack("!i", len(encoded)), encoded))) + self.transport.write(pack("!i", len(encoded)) + encoded) self.transport.flush() self.__wbuf = BufferIO() diff --git a/lib/py/test/test_socket.py b/lib/py/test/test_socket.py new file mode 100644 index 00000000000..95124dcbe16 --- /dev/null +++ b/lib/py/test/test_socket.py @@ -0,0 +1,57 @@ +import errno +import unittest + +from test_sslsocket import ServerAcceptor + +import _import_local_thrift # noqa + +from thrift.transport.TSocket import TServerSocket +from thrift.transport.TSocket import TSocket +from thrift.transport.TTransport import TTransportException + + +class TSocketTest(unittest.TestCase): + def test_isOpen_checks_for_readability(self): + # https://docs.python.org/3/library/socket.html#notes-on-socket-timeouts + # https://docs.python.org/3/library/socket.html#socket.socket.settimeout + timeouts = [ + None, # blocking mode + 0, # non-blocking mode + 1.0, # timeout mode + ] + + for timeout in timeouts: + acc = ServerAcceptor(TServerSocket(port=0)) + acc.start() + + sock = TSocket(host="localhost", port=acc.port) + sock.open() + sock.setTimeout(timeout) + + # the socket shows as open immediately after connecting + self.assertTrue(sock.isOpen()) + + # and remains open during usage + sock.write(b"hello") + self.assertTrue(sock.isOpen()) + while True: + try: + sock.read(5) + except TTransportException as exc: + if exc.inner.errno == errno.EAGAIN: + # try again when we're in non-blocking mode + continue + raise + break + self.assertTrue(sock.isOpen()) + + # once the server side closes, it no longer shows open + acc.client.close() # this also blocks until the other thread is done + acc.close() + self.assertFalse(sock.isOpen()) + + sock.close() + + +if __name__ == "__main__": + unittest.main() diff --git a/lib/py/test/test_sslsocket.py b/lib/py/test/test_sslsocket.py index f1344e57d24..f4c87f195ab 100644 --- a/lib/py/test/test_sslsocket.py +++ b/lib/py/test/test_sslsocket.py @@ -75,6 +75,9 @@ def run(self): try: self._client = self._server.accept() + if self._client: + self._client.read(5) # hello + self._client.write(b"there") except Exception: logging.exception('error on server side (%s):' % self.name) if not self._expect_failure: @@ -141,7 +144,8 @@ def _assert_connection_failure(self, server, path=None, **client_args): client.setTimeout(20) with self._assert_raises(TTransportException): client.open() - self.assertTrue(acc.client is None) + client.write(b"hello") + client.read(5) # b"there" finally: logging.disable(logging.NOTSET) @@ -153,8 +157,10 @@ def _assert_raises(self, exc): def _assert_connection_success(self, server, path=None, **client_args): with self._connectable_client(server, path=path, **client_args) as (acc, client): - client.open() try: + client.open() + client.write(b"hello") + self.assertEqual(client.read(5), b"there") self.assertTrue(acc.client is not None) finally: client.close() @@ -213,6 +219,7 @@ def test_unix_domain_socket(self): return fd, path = tempfile.mkstemp() os.close(fd) + os.unlink(path) try: server = self._server_socket(unix_socket=path, keyfile=SERVER_KEY, certfile=SERVER_CERT) self._assert_connection_success(server, path=path, cert_reqs=ssl.CERT_NONE) diff --git a/lib/py/test/test_thrift_file/TestServer.thrift b/lib/py/test/test_thrift_file/TestServer.thrift new file mode 100644 index 00000000000..0de8856a0a8 --- /dev/null +++ b/lib/py/test/test_thrift_file/TestServer.thrift @@ -0,0 +1,23 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + + +service TestServer{ + string add_and_get_msg(1:string msg) +} diff --git a/lib/py/test/thrift_TBinaryProtocol.py b/lib/py/test/thrift_TBinaryProtocol.py new file mode 100644 index 00000000000..f7d05ff975d --- /dev/null +++ b/lib/py/test/thrift_TBinaryProtocol.py @@ -0,0 +1,264 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +import unittest + +import _import_local_thrift # noqa +from thrift.protocol.TBinaryProtocol import TBinaryProtocol +from thrift.transport import TTransport + + +def testNaked(type, data): + buf = TTransport.TMemoryBuffer() + transport = TTransport.TBufferedTransportFactory().getTransport(buf) + protocol = TBinaryProtocol(transport) + if type.capitalize() == 'Byte': + protocol.writeByte(data) + + if type.capitalize() == 'I16': + protocol.writeI16(data) + + if type.capitalize() == 'I32': + protocol.writeI32(data) + + if type.capitalize() == 'I64': + protocol.writeI64(data) + + if type.capitalize() == 'String': + protocol.writeString(data) + + if type.capitalize() == 'Double': + protocol.writeDouble(data) + + if type.capitalize() == 'Binary': + protocol.writeBinary(data) + + if type.capitalize() == 'Bool': + protocol.writeBool(data) + + transport.flush() + data_r = buf.getvalue() + buf = TTransport.TMemoryBuffer(data_r) + transport = TTransport.TBufferedTransportFactory().getTransport(buf) + protocol = TBinaryProtocol(transport) + if type.capitalize() == 'Byte': + return protocol.readByte() + + if type.capitalize() == 'I16': + return protocol.readI16() + + if type.capitalize() == 'I32': + return protocol.readI32() + + if type.capitalize() == 'I64': + return protocol.readI64() + + if type.capitalize() == 'String': + return protocol.readString() + + if type.capitalize() == 'Double': + return protocol.readDouble() + + if type.capitalize() == 'Binary': + return protocol.readBinary() + + if type.capitalize() == 'Bool': + return protocol.readBool() + + +def testField(type, data): + TType = {"Bool": 2, "Byte": 3, "Binary": 5, "I16": 6, "I32": 8, "I64": 10, "Double": 11, "String": 12} + buf = TTransport.TMemoryBuffer() + transport = TTransport.TBufferedTransportFactory().getTransport(buf) + protocol = TBinaryProtocol(transport) + protocol.writeStructBegin('struct') + protocol.writeFieldBegin("field", TType[type.capitalize()], 10) + if type.capitalize() == 'Byte': + protocol.writeByte(data) + + if type.capitalize() == 'I16': + protocol.writeI16(data) + + if type.capitalize() == 'I32': + protocol.writeI32(data) + + if type.capitalize() == 'I64': + protocol.writeI64(data) + + if type.capitalize() == 'String': + protocol.writeString(data) + + if type.capitalize() == 'Double': + protocol.writeDouble(data) + + if type.capitalize() == 'Binary': + protocol.writeBinary(data) + + if type.capitalize() == 'Bool': + protocol.writeBool(data) + + protocol.writeFieldEnd() + protocol.writeStructEnd() + + transport.flush() + data_r = buf.getvalue() + + buf = TTransport.TMemoryBuffer(data_r) + transport = TTransport.TBufferedTransportFactory().getTransport(buf) + protocol = TBinaryProtocol(transport) + protocol.readStructBegin() + protocol.readFieldBegin() + if type.capitalize() == 'Byte': + return protocol.readByte() + + if type.capitalize() == 'I16': + return protocol.readI16() + + if type.capitalize() == 'I32': + return protocol.readI32() + + if type.capitalize() == 'I64': + return protocol.readI64() + + if type.capitalize() == 'String': + return protocol.readString() + + if type.capitalize() == 'Double': + return protocol.readDouble() + + if type.capitalize() == 'Binary': + return protocol.readBinary() + + if type.capitalize() == 'Bool': + return protocol.readBool() + + protocol.readFieldEnd() + protocol.readStructEnd() + + +def testMessage(data): + message = {} + message['name'] = data[0] + message['type'] = data[1] + message['seqid'] = data[2] + + buf = TTransport.TMemoryBuffer() + transport = TTransport.TBufferedTransportFactory().getTransport(buf) + protocol = TBinaryProtocol(transport) + protocol.writeMessageBegin(message['name'], message['type'], message['seqid']) + protocol.writeMessageEnd() + + transport.flush() + data_r = buf.getvalue() + + buf = TTransport.TMemoryBuffer(data_r) + transport = TTransport.TBufferedTransportFactory().getTransport(buf) + protocol = TBinaryProtocol(transport) + result = protocol.readMessageBegin() + protocol.readMessageEnd() + return result + + +class TestTBinaryProtocol(unittest.TestCase): + + def test_TBinaryProtocol_write_read(self): + try: + testNaked('Byte', 123) + for i in range(0, 128): + self.assertEqual(i, testField('Byte', i)) + self.assertEqual(-i, testField('Byte', -i)) + + self.assertEqual(0, testNaked("I16", 0)) + self.assertEqual(1, testNaked("I16", 1)) + self.assertEqual(15000, testNaked("I16", 15000)) + self.assertEqual(0x7fff, testNaked("I16", 0x7fff)) + self.assertEqual(-1, testNaked("I16", -1)) + self.assertEqual(-15000, testNaked("I16", -15000)) + self.assertEqual(-0x7fff, testNaked("I16", -0x7fff)) + self.assertEqual(32767, testNaked("I16", 32767)) + self.assertEqual(-32768, testNaked("I16", -32768)) + + self.assertEqual(0, testField("I16", 0)) + self.assertEqual(1, testField("I16", 1)) + self.assertEqual(7, testField("I16", 7)) + self.assertEqual(150, testField("I16", 150)) + self.assertEqual(15000, testField("I16", 15000)) + self.assertEqual(0x7fff, testField("I16", 0x7fff)) + self.assertEqual(-1, testField("I16", -1)) + self.assertEqual(-7, testField("I16", -7)) + self.assertEqual(-150, testField("I16", -150)) + self.assertEqual(-15000, testField("I16", -15000)) + self.assertEqual(-0xfff, testField("I16", -0xfff)) + + self.assertEqual(0, testNaked("I32", 0)) + self.assertEqual(1, testNaked("I32", 1)) + self.assertEqual(15000, testNaked("I32", 15000)) + self.assertEqual(0xffff, testNaked("I32", 0xffff)) + self.assertEqual(-1, testNaked("I32", -1)) + self.assertEqual(-15000, testNaked("I32", -15000)) + self.assertEqual(-0xffff, testNaked("I32", -0xffff)) + self.assertEqual(2147483647, testNaked("I32", 2147483647)) + self.assertEqual(-2147483647, testNaked("I32", -2147483647)) + + self.assertEqual(0, testField("I32", 0)) + self.assertEqual(1, testField("I32", 1)) + self.assertEqual(7, testField("I32", 7)) + self.assertEqual(150, testField("I32", 150)) + self.assertEqual(15000, testField("I32", 15000)) + self.assertEqual(31337, testField("I32", 31337)) + self.assertEqual(0xffff, testField("I32", 0xffff)) + self.assertEqual(0xffffff, testField("I32", 0xffffff)) + self.assertEqual(-1, testField("I32", -1)) + self.assertEqual(-7, testField("I32", -7)) + self.assertEqual(-150, testField("I32", -150)) + self.assertEqual(-15000, testField("I32", -15000)) + self.assertEqual(-0xffff, testField("I32", -0xffff)) + self.assertEqual(-0xffffff, testField("I32", -0xffffff)) + + self.assertEqual(9223372036854775807, testNaked("I64", 9223372036854775807)) + self.assertEqual(-9223372036854775807, testNaked("I64", -9223372036854775807)) + self.assertEqual(-0, testNaked("I64", 0)) + + self.assertEqual(True, testNaked("Bool", True)) + self.assertEqual(3.14159261, testNaked("Double", 3.14159261)) + self.assertEqual("hello thrift", testNaked("String", "hello thrift")) + self.assertEqual(True, testField('Bool', True)) + self.assertEqual(3.1415926, testNaked("Double", 3.1415926)) + self.assertEqual("hello thrift", testNaked("String", "hello thrift")) + + TMessageType = {"T_CALL": 1, "T_REPLY": 2, "T_EXCEPTION": 3, "T_ONEWAY": 4} + test_data = [("short message name", TMessageType['T_CALL'], 0), + ("1", TMessageType['T_REPLY'], 12345), + ("loooooooooooooooooooooooooooooooooong", TMessageType['T_EXCEPTION'], 1 << 16), + ("one way push", TMessageType['T_ONEWAY'], 12), + ("Janky", TMessageType['T_CALL'], 0)] + + for dt in test_data: + result = testMessage(dt) + self.assertEqual(result[0], dt[0]) + self.assertEqual(result[1], dt[1]) + self.assertEqual(result[2], dt[2]) + + except Exception as e: + print("Assertion fail") + raise e + + +if __name__ == '__main__': + unittest.main() diff --git a/lib/py/test/thrift_TCompactProtocol.py b/lib/py/test/thrift_TCompactProtocol.py new file mode 100644 index 00000000000..1d6af8ee2ad --- /dev/null +++ b/lib/py/test/thrift_TCompactProtocol.py @@ -0,0 +1,288 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +import _import_local_thrift # noqa +from thrift.protocol import TCompactProtocol +from thrift.transport import TTransport +import unittest + +CLEAR = 0 +FIELD_WRITE = 1 +VALUE_WRITE = 2 +CONTAINER_WRITE = 3 +BOOL_WRITE = 4 +FIELD_READ = 5 +CONTAINER_READ = 6 +VALUE_READ = 7 +BOOL_READ = 8 + + +def testNaked(type, data): + buf = TTransport.TMemoryBuffer() + transport = TTransport.TBufferedTransportFactory().getTransport(buf) + protocol = TCompactProtocol.TCompactProtocol(transport) + + if type.capitalize() == 'Byte': + protocol.state = VALUE_WRITE + protocol.writeByte(data) + + elif type.capitalize() == 'I16': + protocol.state = CONTAINER_WRITE + protocol.writeI16(data) + + elif type.capitalize() == 'I32': + protocol.state = CONTAINER_WRITE + protocol.writeI32(data) + + elif type.capitalize() == 'I64': + protocol.state = CONTAINER_WRITE + protocol.writeI64(data) + + elif type.capitalize() == 'String': + protocol.state = CONTAINER_WRITE + protocol.writeString(data) + + elif type.capitalize() == 'Double': + protocol.state = VALUE_WRITE + protocol.writeDouble(data) + + elif type.capitalize() == 'Binary': + protocol.state = FIELD_WRITE + protocol.writeBinary(data) + + elif type.capitalize() == 'Bool': + protocol.state = CONTAINER_WRITE + protocol.writeBool(True) + + transport.flush() + data_r = buf.getvalue() + buf = TTransport.TMemoryBuffer(data_r) + transport = TTransport.TBufferedTransportFactory().getTransport(buf) + protocol = TCompactProtocol.TCompactProtocol(transport) + if type.capitalize() == 'Byte': + protocol.state = VALUE_READ + return protocol.readByte() + + elif type.capitalize() == 'I16': + protocol.state = CONTAINER_READ + return protocol.readI16() + + elif type.capitalize() == 'I32': + protocol.state = CONTAINER_READ + return protocol.readI32() + + elif type.capitalize() == 'I64': + protocol.state = CONTAINER_READ + return protocol.readI64() + + elif type.capitalize() == 'String': + protocol.state = VALUE_READ + return protocol.readString() + + elif type.capitalize() == 'Double': + protocol.state = VALUE_READ + return protocol.readDouble() + + elif type.capitalize() == 'Binary': + protocol.state = FIELD_READ + return protocol.readBinary() + + elif type.capitalize() == 'Bool': + protocol.state = CONTAINER_READ + return protocol.readBool() + + +def testField(type, data): + TType = {"Bool": 2, "Byte": 3, "Binary": 5, "I16": 6, "I32": 8, "I64": 10, "Double": 11, "String": 12} + buf = TTransport.TMemoryBuffer() + transport = TTransport.TBufferedTransportFactory().getTransport(buf) + protocol = TCompactProtocol.TCompactProtocol(transport) + protocol.writeStructBegin('struct') + protocol.writeFieldBegin("field", TType[type.capitalize()], 10) + if type.capitalize() == 'Byte': + protocol.writeByte(data) + + elif type.capitalize() == 'I16': + protocol.writeI16(data) + + elif type.capitalize() == 'I32': + protocol.writeI32(data) + + elif type.capitalize() == 'I64': + protocol.writeI64(data) + + elif type.capitalize() == 'String': + protocol.writeString(data) + + elif type.capitalize() == 'Double': + protocol.writeDouble(data) + + elif type.capitalize() == 'Binary': + protocol.writeBinary(data) + + elif type.capitalize() == 'Bool': + protocol.writeBool(data) + + protocol.writeFieldEnd() + protocol.writeStructEnd() + + transport.flush() + data_r = buf.getvalue() + + buf = TTransport.TMemoryBuffer(data_r) + transport = TTransport.TBufferedTransportFactory().getTransport(buf) + protocol = TCompactProtocol.TCompactProtocol(transport) + protocol.readStructBegin() + protocol.readFieldBegin() + if type.capitalize() == 'Byte': + return protocol.readByte() + + elif type.capitalize() == 'I16': + return protocol.readI16() + + elif type.capitalize() == 'I32': + return protocol.readI32() + + elif type.capitalize() == 'I64': + return protocol.readI32() + + elif type.capitalize() == 'String': + return protocol.readString() + + elif type.capitalize() == 'Double': + return protocol.readDouble() + + elif type.capitalize() == 'Binary': + return protocol.readBinary() + + elif type.capitalize() == 'Bool': + return protocol.readBool() + + protocol.readFieldEnd() + protocol.readStructEnd() + + +def testMessage(data): + message = {} + message['name'] = data[0] + message['type'] = data[1] + message['seqid'] = data[2] + + buf = TTransport.TMemoryBuffer() + transport = TTransport.TBufferedTransportFactory().getTransport(buf) + protocol = TCompactProtocol.TCompactProtocol(transport) + protocol.writeMessageBegin(message['name'], message['type'], message['seqid']) + protocol.writeMessageEnd() + + transport.flush() + data_r = buf.getvalue() + + buf = TTransport.TMemoryBuffer(data_r) + transport = TTransport.TBufferedTransportFactory().getTransport(buf) + protocol = TCompactProtocol.TCompactProtocol(transport) + result = protocol.readMessageBegin() + protocol.readMessageEnd() + return result + + +class TestTCompactProtocol(unittest.TestCase): + + def __init__(self, *args, **kwargs): + unittest.TestCase.__init__(self, *args, **kwargs) + + def test_TCompactProtocol_write_read(self): + try: + testNaked('Byte', 123) + for i in range(0, 128): + self.assertEqual(i, testField('Byte', i)) + self.assertEqual(-i, testField('Byte', -i)) + + self.assertEqual(0, testNaked("I16", 0)) + self.assertEqual(1, testNaked("I16", 1)) + self.assertEqual(15000, testNaked("I16", 15000)) + self.assertEqual(0x7fff, testNaked('I16', 0x7fff)) + self.assertEqual(-1, testNaked('I16', -1)) + self.assertEqual(-15000, testNaked('I16', -15000)) + self.assertEqual(-0x7fff, testNaked('I16', -0x7fff)) + self.assertEqual(32767, testNaked('I16', 32767)) + + self.assertEqual(0, testField('I16', 0)) + self.assertEqual(1, testField('I16', 1)) + self.assertEqual(7, testField('I16', 7)) + self.assertEqual(150, testField('I16', 150)) + self.assertEqual(15000, testField('I16', 15000)) + self.assertEqual(0x7fff, testField('I16', 0x7fff)) + self.assertEqual(-1, testField('I16', -1)) + self.assertEqual(-7, testField('I16', -7)) + self.assertEqual(-150, testField('I16', -150)) + self.assertEqual(-15000, testField('I16', -15000)) + self.assertEqual(-0xfff, testField('I16', -0xfff)) + + self.assertEqual(0, testNaked('I32', 0)) + self.assertEqual(1, testNaked('I32', 1)) + self.assertEqual(15000, testNaked('I32', 15000)) + self.assertEqual(0xfff, testNaked('I32', 0xfff)) + self.assertEqual(-1, testNaked('I32', -1)) + self.assertEqual(-15000, testNaked('I32', -15000)) + self.assertEqual(-0xfff, testNaked('I32', -0xfff)) + self.assertEqual(2147483647, testNaked('I32', 2147483647)) + self.assertEqual(-2147483647, testNaked('I32', -2147483647)) + + self.assertEqual(0, testField('I32', 0)) + self.assertEqual(1, testField('I32', 1)) + self.assertEqual(7, testField('I32', 7)) + self.assertEqual(150, testField('I32', 150)) + self.assertEqual(15000, testField('I32', 15000)) + self.assertEqual(31337, testField('I32', 31337)) + self.assertEqual(0xffff, testField('I32', 0xffff)) + self.assertEqual(0xffffff, testField('I32', 0xffffff)) + self.assertEqual(-1, testField('I32', -1)) + self.assertEqual(-7, testField('I32', -7)) + self.assertEqual(-150, testField('I32', -150)) + self.assertEqual(-15000, testField('I32', -15000)) + self.assertEqual(-0xffff, testField('I32', -0xffff)) + self.assertEqual(-0xffffff, testField('I32', -0xffffff)) + + self.assertEqual(9223372036854775807, testNaked("I64", 9223372036854775807)) + self.assertEqual(-9223372036854775807, testNaked('I64', -9223372036854775807)) + self.assertEqual(-0, testNaked('I64', 0)) + self.assertEqual(True, testNaked('Bool', True)) + self.assertEqual(3.14159261, testNaked('Double', 3.14159261)) + self.assertEqual("hello thrift", testNaked('String', "hello thrift")) + self.assertEqual(True, testField('Bool', True)) + self.assertEqual(3.14159261, testField('Double', 3.14159261)) + self.assertEqual("hello thrift", testField('String', "hello thrift")) + TMessage = {"T_CALL": 1, "T_REPLY": 2, "T_EXCEPTION": 3, "T_ONEWAY": 4} + test_data = [("short message name", TMessage["T_CALL"], 0), + ("1", TMessage["T_REPLY"], 12345), + ("loooooooooooooooooooong", TMessage["T_EXCEPTION"], 1 << 16), + ("one way push", TMessage["T_ONEWAY"], 12), + ("JANKY", TMessage["T_CALL"], 0)] + for dt in test_data: + result = testMessage(dt) + self.assertEqual(result[0], dt[0]) + self.assertEqual(result[1], dt[1]) + self.assertEqual(result[2], dt[2]) + except Exception as e: + print("Assertion fail") + raise e + + +if __name__ == "__main__": + unittest.main() diff --git a/lib/py/test/thrift_TNonblockingServer.py b/lib/py/test/thrift_TNonblockingServer.py new file mode 100644 index 00000000000..7220879ac5d --- /dev/null +++ b/lib/py/test/thrift_TNonblockingServer.py @@ -0,0 +1,101 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +import os +import sys +import threading +import unittest +import time + +gen_path = os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))), "gen-py") +sys.path.append(gen_path) +import _import_local_thrift # noqa +from TestServer import TestServer +from thrift.transport import TSocket, TTransport +from thrift.protocol import TBinaryProtocol +from thrift.server import TNonblockingServer + + +class Handler: + + def add_and_get_msg(self, msg): + return msg + + +class Server: + + def __init__(self): + handler = Handler() + processor = TestServer.Processor(handler) + transport = TSocket.TServerSocket("127.0.0.1", 30030) + self.server = TNonblockingServer.TNonblockingServer(processor, transport) + + def start_server(self): + print("-------start server ------\n") + self.server.serve() + print("------stop server -----\n") + + def close_server(self): + self.server.stop() + self.server.close() + + +class Client: + + def start_client(self): + transport = TSocket.TSocket("127.0.0.1", 30030) + trans = TTransport.TFramedTransport(transport) + protocol = TBinaryProtocol.TBinaryProtocol(trans) + client = TestServer.Client(protocol) + trans.open() + self.msg = client.add_and_get_msg("hello thrift") + + def get_message(self): + try: + msg = self.msg + return msg + except AttributeError as e: + raise e + print("self.msg not exit\n") + + +class TestNonblockingServer(unittest.TestCase): + + def test_normalconnection(self): + serve = Server() + client = Client() + + serve_thread = threading.Thread(target=serve.start_server) + client_thread = threading.Thread(target=client.start_client) + serve_thread.start() + time.sleep(10) + client_thread.start() + client_thread.join(0.5) + try: + msg = client.get_message() + self.assertEqual("hello thrift", msg) + except AssertionError as e: + raise e + print("assert failure") + finally: + serve.close_server() + + +if __name__ == '__main__': + unittest.main() diff --git a/lib/py/test/thrift_TZlibTransport.py b/lib/py/test/thrift_TZlibTransport.py new file mode 100644 index 00000000000..ded34b03d9d --- /dev/null +++ b/lib/py/test/thrift_TZlibTransport.py @@ -0,0 +1,99 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +import unittest +import random +import string + +import _import_local_thrift # noqa +from thrift.transport import TTransport +from thrift.transport import TZlibTransport + + +def generate_random_buff(): + data = [] + buf_len = 1024 * 32 + index = 0 + + while index < buf_len: + run_len = random.randint(1, 64) + if index + run_len > buf_len: + run_len = buf_len - index + for i in range(run_len): + data.extend(random.sample(string.printable, 1)) + index += 1 + + new_data = ''.join(data) + return new_data + + +class TestTZlibTransport(unittest.TestCase): + + def test_write_then_read(self): + buff = TTransport.TMemoryBuffer() + trans = TTransport.TBufferedTransportFactory().getTransport(buff) + zlib_trans = TZlibTransport.TZlibTransport(trans) + data_w = generate_random_buff() + zlib_trans.write(data_w.encode('utf-8')) + zlib_trans.flush() + + value = buff.getvalue() + zlib_trans.close() + + buff = TTransport.TMemoryBuffer(value) + trans = TTransport.TBufferedTransportFactory().getTransport(buff) + zlib_trans = TZlibTransport.TZlibTransport(trans) + data_r = zlib_trans.read(len(data_w)) + zlib_trans.close() + + try: + self.assertEqual(data_w, data_r.decode('utf-8')) + self.assertEqual(len(data_w), len(data_r.decode('utf-8'))) + except AssertionError: + raise + + def test_after_flushd_write_then_read(self): + buff = TTransport.TMemoryBuffer() + trans = TTransport.TBufferedTransportFactory().getTransport(buff) + zlib_trans = TZlibTransport.TZlibTransport(trans) + data_w_1 = "hello thrift !@#" * 50 + zlib_trans.write(data_w_1.encode('utf-8')) + zlib_trans.flush() + data_w_2 = "{'name': 'thrift', 1: ['abcd' , 233, ('a','c')]}" * 20 + zlib_trans.write(data_w_2.encode('utf-8')) + zlib_trans.flush() + + value = buff.getvalue() + zlib_trans.close() + + buff = TTransport.TMemoryBuffer(value) + trans = TTransport.TBufferedTransportFactory().getTransport(buff) + zlib_trans = TZlibTransport.TZlibTransport(trans) + data_r = zlib_trans.read(len(data_w_1) + len(data_w_2)) + zlib_trans.close() + + try: + self.assertEqual(data_w_1 + data_w_2, data_r.decode('utf-8')) + self.assertEqual(len(data_w_1) + len(data_w_2), len(data_r.decode('utf-8'))) + except AssertionError: + raise + + +if __name__ == '__main__': + unittest.main() diff --git a/lib/py/test/thrift_json.py b/lib/py/test/thrift_json.py index 40e7a47e389..125ea59f6c7 100644 --- a/lib/py/test/thrift_json.py +++ b/lib/py/test/thrift_json.py @@ -46,6 +46,71 @@ def test_escaped_unicode_string(self): unicode_text = unicode_text.encode('utf8') self.assertEqual(protocol.readString(), unicode_text) + def test_TJSONProtocol_write(self): + write_data = '{"software":"thrift","1":[23,1.2010000000000001,32767,2147483647,9223372036854775807],"base64":"aGVsbG8gdGhyaWZ0","bool":0}' + + buff = TTransport.TMemoryBuffer() + transport = TTransport.TBufferedTransportFactory().getTransport(buff) + protocol = TJSONProtocol(transport) + protocol.writeJSONObjectStart() + protocol.writeJSONString("software") + protocol.writeJSONString("thrift") + protocol.writeJSONString("1") + protocol.writeJSONArrayStart() + protocol.writeJSONNumber(23) + protocol.writeDouble(1.201) + protocol.writeI16(32767) + protocol.writeI32(2147483647) + protocol.writeI64(9223372036854775807) + protocol.writeJSONArrayEnd() + protocol.writeJSONString("base64") + protocol.writeJSONBase64("hello thrift".encode('utf-8')) + protocol.writeJSONString("bool") + protocol.writeBool(0) + protocol.writeJSONObjectEnd() + + transport.flush() + value = buff.getvalue() + + self.assertEqual(write_data, value.decode('utf-8')) + + def test_TJSONProtol_read(self): + expected = "{'software':'thrift','1':[23,1.2010000000000001,32767,2147483647,9223372036854775807],'base64':'hello thrift','bool':False}" + read_data = '{"software":"thrift","1":[23,1.2010000000000001,32767,2147483647,9223372036854775807],"base64":"aGVsbG8gdGhyaWZ0","bool":0}' + + buff = TTransport.TMemoryBuffer(read_data.encode('utf-8')) + transport = TTransport.TBufferedTransportFactory().getTransport(buff) + protocol = TJSONProtocol(transport) + protocol.readJSONObjectStart() + u_1 = protocol.readString() + u_2 = protocol.readString() + u_3 = protocol.readString() + protocol.readJSONArrayStart() + u_4 = protocol.readNumber() + u_5 = protocol.readDouble() + u_6 = protocol.readI16() + u_7 = protocol.readI32() + u_8 = protocol.readI64() + protocol.readJSONArrayEnd() + u_9 = protocol.readString() + u_10 = protocol.readJSONBase64() + u_11 = protocol.readString() + u_12 = protocol.readBool() + protocol.writeJSONObjectEnd() + + result_read = {} + result_read[u_1] = u_2 + result_read[u_3] = [] + result_read[u_3].append(u_4) + result_read[u_3].append(u_5) + result_read[u_3].append(u_6) + result_read[u_3].append(u_7) + result_read[u_3].append(u_8) + result_read[u_9] = u_10.decode('utf-8') + result_read[u_11] = u_12 + + self.assertEqual(eval(expected), result_read) + if __name__ == '__main__': unittest.main() diff --git a/lib/py/test/thrift_transport.py b/lib/py/test/thrift_transport.py new file mode 100644 index 00000000000..cb1bb0ce71a --- /dev/null +++ b/lib/py/test/thrift_transport.py @@ -0,0 +1,70 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +import unittest +import os + +import _import_local_thrift # noqa +from thrift.transport import TTransport + + +class TestTFileObjectTransport(unittest.TestCase): + + def test_TFileObjectTransport(self): + test_dir = os.path.dirname(os.path.abspath(__file__)) + datatxt_path = os.path.join(test_dir, 'data.txt') + buffer = '{"soft":"thrift","version":0.13,"1":true}' + with open(datatxt_path, "w+") as f: + buf = TTransport.TFileObjectTransport(f) + buf.write(buffer) + buf.flush() + buf.close() + + with open(datatxt_path, "rb") as f: + buf = TTransport.TFileObjectTransport(f) + value = buf.read(len(buffer)).decode('utf-8') + self.assertEqual(buffer, value) + buf.close() + os.remove(datatxt_path) + + +class TestMemoryBuffer(unittest.TestCase): + + def test_memorybuffer_write(self): + data = '{"1":[1,"hello"],"a":{"A":"abc"},"bool":true,"num":12345}' + + buffer_w = TTransport.TMemoryBuffer() + buffer_w.write(data.encode('utf-8')) + value = buffer_w.getvalue() + self.assertEqual(value.decode('utf-8'), data) + buffer_w.close() + + def test_memorybuffer_read(self): + data = '{"1":[1, "hello"],"a":{"A":"abc"},"bool":true,"num":12345}' + + buffer_r = TTransport.TMemoryBuffer(data.encode('utf-8')) + value_r = buffer_r.read(len(data)) + value = buffer_r.getvalue() + self.assertEqual(value.decode('utf-8'), data) + self.assertEqual(value_r.decode('utf-8'), data) + buffer_r.close() + + +if __name__ == '__main__': + unittest.main() diff --git a/lib/rb/Makefile.am b/lib/rb/Makefile.am index 137edb4d424..1841065f5a3 100755 --- a/lib/rb/Makefile.am +++ b/lib/rb/Makefile.am @@ -31,6 +31,7 @@ install-exec-hook: clean-local: $(BUNDLER) install $(BUNDLER) exec rake clean + $(RM) -r spec/gen-rb/ check-local: all $(BUNDLER) install @@ -38,6 +39,9 @@ check-local: all endif +dist-hook: + $(RM) -r $(distdir)/spec/gen-rb/ + EXTRA_DIST = \ coding_standards.md \ Rakefile \ diff --git a/lib/rb/Rakefile b/lib/rb/Rakefile index cdecaa68c1d..5e5e5acaa99 100644 --- a/lib/rb/Rakefile +++ b/lib/rb/Rakefile @@ -71,6 +71,7 @@ end desc "Build the native library" task :build_ext => :'gen-rb' do + next if defined?(RUBY_ENGINE) && RUBY_ENGINE =~ /jruby/ Dir::chdir(File::dirname('ext/extconf.rb')) do unless sh "ruby #{File::basename('ext/extconf.rb')}" $stderr.puts "Failed to run extconf" diff --git a/lib/rb/lib/thrift/protocol/base_protocol.rb b/lib/rb/lib/thrift/protocol/base_protocol.rb index 5c693e99f1a..4d83a21ddb7 100644 --- a/lib/rb/lib/thrift/protocol/base_protocol.rb +++ b/lib/rb/lib/thrift/protocol/base_protocol.rb @@ -323,8 +323,6 @@ def read_type(field_info) def skip(type) case type - when Types::STOP - nil when Types::BOOL read_bool when Types::BYTE @@ -367,6 +365,8 @@ def skip(type) skip(etype) end read_list_end + else + raise ProtocolException.new(ProtocolException::INVALID_DATA, 'Invalid data') end end diff --git a/lib/rb/lib/thrift/transport/http_client_transport.rb b/lib/rb/lib/thrift/transport/http_client_transport.rb index 5c1dd5c8a9c..c84304d9912 100644 --- a/lib/rb/lib/thrift/transport/http_client_transport.rb +++ b/lib/rb/lib/thrift/transport/http_client_transport.rb @@ -47,6 +47,8 @@ def flush http.use_ssl = @url.scheme == 'https' http.verify_mode = @ssl_verify_mode if @url.scheme == 'https' resp = http.post(@url.request_uri, @outbuf, @headers) + raise TransportException.new(TransportException::UNKNOWN, "#{self.class.name} Could not connect to #{@url}, HTTP status code #{resp.code.to_i}") unless (200..299).include?(resp.code.to_i) + data = resp.body data = Bytes.force_binary_encoding(data) @inbuf = StringIO.new data diff --git a/lib/rb/spec/base_protocol_spec.rb b/lib/rb/spec/base_protocol_spec.rb index eca936b239d..cfa7573d8c5 100644 --- a/lib/rb/spec/base_protocol_spec.rb +++ b/lib/rb/spec/base_protocol_spec.rb @@ -163,7 +163,6 @@ @prot.skip(Thrift::Types::I64) @prot.skip(Thrift::Types::DOUBLE) @prot.skip(Thrift::Types::STRING) - @prot.skip(Thrift::Types::STOP) # should do absolutely nothing end it "should skip structs" do diff --git a/lib/rb/spec/http_client_spec.rb b/lib/rb/spec/http_client_spec.rb index df472ab335e..292c7521ebb 100644 --- a/lib/rb/spec/http_client_spec.rb +++ b/lib/rb/spec/http_client_spec.rb @@ -45,6 +45,7 @@ expect(http).to receive(:post).with("/path/to/service?param=value", "a test frame", {"Content-Type"=>"application/x-thrift"}) do double("Net::HTTPOK").tap do |response| expect(response).to receive(:body).and_return "data" + expect(response).to receive(:code).and_return "200" end end end @@ -65,6 +66,7 @@ expect(http).to receive(:post).with("/path/to/service?param=value", "test", headers) do double("Net::HTTPOK").tap do |response| expect(response).to receive(:body).and_return "data" + expect(response).to receive(:code).and_return "200" end end end @@ -86,6 +88,24 @@ expect(@client.instance_variable_get(:@outbuf)).to eq(Thrift::Bytes.empty_byte_buffer) end + it 'should raise TransportError on HTTP failures' do + @client.write "test" + + expect(Net::HTTP).to receive(:new).with("my.domain.com", 80) do + double("Net::HTTP").tap do |http| + expect(http).to receive(:use_ssl=).with(false) + expect(http).to receive(:post).with("/path/to/service?param=value", "test", {"Content-Type"=>"application/x-thrift"}) do + double("Net::HTTPOK").tap do |response| + expect(response).not_to receive(:body) + expect(response).to receive(:code).at_least(:once).and_return "503" + end + end + end + end + + expect { @client.flush }.to raise_error(Thrift::TransportException) + end + end describe 'ssl enabled' do @@ -107,6 +127,7 @@ "Content-Type" => "application/x-thrift") do double("Net::HTTPOK").tap do |response| expect(response).to receive(:body).and_return "data" + expect(response).to receive(:code).and_return "200" end end end @@ -128,6 +149,7 @@ "Content-Type" => "application/x-thrift") do double("Net::HTTPOK").tap do |response| expect(response).to receive(:body).and_return "data" + expect(response).to receive(:code).and_return "200" end end end diff --git a/lib/rb/spec/union_spec.rb b/lib/rb/spec/union_spec.rb index 0ce6306298f..efb38534638 100644 --- a/lib/rb/spec/union_spec.rb +++ b/lib/rb/spec/union_spec.rb @@ -51,8 +51,7 @@ it "should raise for wrong set field when hash initialized and type checking is off" do Thrift.type_checking = false union = SpecNamespace::My_union.new({incorrect_field: :incorrect}) - example = lambda { Thrift::Serializer.new.serialize(union) } - expect(example).to raise_error(RuntimeError, "set_field is not valid for this union!") + expect { Thrift::Serializer.new.serialize(union) }.to raise_error(RuntimeError, "set_field is not valid for this union!") end it "should not be equal to nil" do diff --git a/lib/rb/thrift.gemspec b/lib/rb/thrift.gemspec index 5a3a0ae1aaf..c6f32cb9aea 100644 --- a/lib/rb/thrift.gemspec +++ b/lib/rb/thrift.gemspec @@ -3,8 +3,8 @@ $:.push File.expand_path("../lib", __FILE__) Gem::Specification.new do |s| s.name = 'thrift' - s.version = '0.12.0' - s.authors = ['Thrift Developers'] + s.version = '0.14.0' + s.authors = ['Apache Thrift Developers'] s.email = ['dev@thrift.apache.org'] s.homepage = 'http://thrift.apache.org' s.summary = %q{Ruby bindings for Apache Thrift} @@ -31,7 +31,7 @@ Gem::Specification.new do |s| s.add_development_dependency 'pry', '~> 0.11.3' s.add_development_dependency 'pry-byebug', '~> 3.6' s.add_development_dependency 'pry-stack_explorer', '~> 0.4.9.2' - s.add_development_dependency 'rack', '~> 2.0' + s.add_development_dependency 'rack', '= 2.0.8' s.add_development_dependency 'rack-test', '~> 0.8.3' s.add_development_dependency 'rake', '~> 12.3' s.add_development_dependency 'rspec', '~> 3.7' diff --git a/lib/rs/Cargo.toml b/lib/rs/Cargo.toml index e79198dc665..4f2b4c82c25 100644 --- a/lib/rs/Cargo.toml +++ b/lib/rs/Cargo.toml @@ -1,19 +1,20 @@ [package] name = "thrift" description = "Rust bindings for the Apache Thrift RPC system" -version = "0.12.0" +edition = "2018" +version = "0.14.0" license = "Apache-2.0" authors = ["Apache Thrift Developers "] homepage = "http://thrift.apache.org" -documentation = "https://thrift.apache.org" +documentation = "https://docs.rs/thrift" +repository = "https://github.com/apache/thrift/tree/master/lib/rs" readme = "README.md" -exclude = ["Makefile*", "test/**"] +exclude = ["Makefile*", "test/**", "*.iml"] keywords = ["thrift"] [dependencies] -byteorder = "~1.2.1" -integer-encoding = "~1.0.4" -log = "~0.3.8" -threadpool = "~1.7.1" -try_from = "~0.2.2" - +byteorder = "1.3" +integer-encoding = ">=1.1.4" # https://issues.apache.org/jira/browse/THRIFT-5131 +log = "0.4" +ordered-float = "1.0" +threadpool = "1.7" diff --git a/lib/rs/Makefile.am b/lib/rs/Makefile.am index 0a34120a3b9..6d74348ebe3 100644 --- a/lib/rs/Makefile.am +++ b/lib/rs/Makefile.am @@ -43,4 +43,7 @@ clean-local: EXTRA_DIST = \ src \ Cargo.toml \ - README.md + README.md \ + release.sh \ + RELEASING.md + diff --git a/lib/rs/README.md b/lib/rs/README.md index 7c37a10bc6c..555d219d2ff 100644 --- a/lib/rs/README.md +++ b/lib/rs/README.md @@ -46,6 +46,23 @@ It does not currently use any Rust 2018 features. Breaking changes are minimized. When they are made they will be outlined below with transition guidelines. +##### Thrift 0.14.0 + +* **[THRIFT-5158]** - Rust library and generator now support Rust 2018 only. Required rust 1.40.0 or higher + + The Rust `thrift` library was updated to Rust 2018 via `cargo fix --edition`. + All test code in the repo was updated as well. The code generator was also updated + to support Rust 2018 only. + +##### Thrift 0.13.0 + +* **[THRIFT-4536]** - Use TryFrom from std, required rust 1.34.0 or higher + + Previously TryFrom was from try_from crate, it is now from the std library, + but this functionality is only available in rust 1.34.0. Additionally, + ordered-float is now re-exported under the thrift module to reduce + possible dependency mismatches. + ##### Thrift 0.12.0 * **[THRIFT-4529]** - Rust enum variants are now camel-cased instead of uppercased to conform to Rust naming conventions diff --git a/lib/rs/RELEASING.md b/lib/rs/RELEASING.md new file mode 100644 index 00000000000..073d7a02a71 --- /dev/null +++ b/lib/rs/RELEASING.md @@ -0,0 +1,57 @@ +# Publishing the thrift crate + +Publishing the Rust thrift crate is straightforward, and involves two major steps: + +1. Setting up your [crates.io](https://www.crates.io) account _(one-time)_ + +2. Packaging/publishing the Rust thrift crate itself + +## Set up your crates.io account (one-time) + +1. Go to [crates.io](https://www.crates.io) and click the `Log In` button at the top right. + + Log in **as the Github user with write permissions to the thrift repo!** + +2. Click your user icon button at the top right and select `Account Settings`. + +3. Click `New Token` next to `API Access`. + + This generates a new API key that cargo uses to publish packages to crates.io. + Store this API key somewhere safe. If you will only use this Github account to + publish crates to crates.io you can follow the instructions to save the + generated key to `~/.cargo/credentials`. + +## Package and Publish + +You can use the automated script or run the release steps manually. + +**Important**: `cargo` expects that version numbers follow the semantic versioning format. +This means that `THRIFT_RELEASE_VERSION` must have a major, minor and patch number, i.e., must +be in the form `#.##.##`. + +#### Automated + +Run `./release.sh [THRIFT_RELEASE_VERSION]`. + +_Requires you to have stored your credentials in `~/.cargo/credentials`._ + +#### Manual + +1. Edit `Cargo.toml` and update the `version = 1.0` key to `version = [THRIFT_RELEASE_VERSION]` + +2. `git add Cargo.toml` + +3. `git commit -m "Update thrift crate version to [THRIFT_RELEASE_VERSION]" -m "Client: rs"` + +4. `cargo login` + + _(not required if you have stored your credentials in `~/.cargo/credentials`)_ + +5. `cargo clean` + +6. `cargo package` + + This step fails if there are any uncommitted or ignored files. Do **not** use the `--allow-dirty` + flag! Instead, add the highlighted files as entries in the `Cargo.toml` `exclude` key. + +7. `cargo publish` diff --git a/lib/rs/release.sh b/lib/rs/release.sh new file mode 100755 index 00000000000..c4e5b48928d --- /dev/null +++ b/lib/rs/release.sh @@ -0,0 +1,26 @@ +#!/bin/bash + +set -o errexit +set -o pipefail +set -o nounset + +if ! [[ $# -eq 1 && $1 =~ ^[0-9](\.[0-9][0-9]*){2}$ ]]; then + (>&2 echo "Usage: ./publish-crate.sh [THRIFT_RELEASE_VERSION] ") + (>&2 echo " THRIFT_RELEASE_VERSION is in semantic versioning format, i.e. #.##.##") + exit 1 +fi + +THRIFT_RELEASE_VERSION=${1:-} + +echo "Updating Cargo.toml to ${THRIFT_RELEASE_VERSION}" +sed -i.old -e "s/^version = .*$/version = \"${THRIFT_RELEASE_VERSION}\"/g" Cargo.toml +rm Cargo.toml.old + +echo "Committing updated Cargo.toml" +git add Cargo.toml +git commit -m "Update thrift crate version to ${THRIFT_RELEASE_VERSION}" -m "Client: rs" + +echo "Packaging and releasing rust thrift crate with version ${THRIFT_RELEASE_VERSION}" +cargo clean +cargo package +cargo publish diff --git a/lib/rs/src/autogen.rs b/lib/rs/src/autogen.rs index 54d4080e82b..9e45272efd7 100644 --- a/lib/rs/src/autogen.rs +++ b/lib/rs/src/autogen.rs @@ -22,17 +22,17 @@ //! to implement required functionality. Users should never have to use code //! in this module directly. -use protocol::{TInputProtocol, TOutputProtocol}; +use crate::protocol::{TInputProtocol, TOutputProtocol}; /// Specifies the minimum functionality an auto-generated client should provide /// to communicate with a Thrift server. pub trait TThriftClient { /// Returns the input protocol used to read serialized Thrift messages /// from the Thrift server. - fn i_prot_mut(&mut self) -> &mut TInputProtocol; + fn i_prot_mut(&mut self) -> &mut dyn TInputProtocol; /// Returns the output protocol used to write serialized Thrift messages /// to the Thrift server. - fn o_prot_mut(&mut self) -> &mut TOutputProtocol; + fn o_prot_mut(&mut self) -> &mut dyn TOutputProtocol; /// Returns the sequence number of the last message written to the Thrift /// server. Returns `0` if no messages have been written. Sequence /// numbers should *never* be negative, and this method returns an `i32` diff --git a/lib/rs/src/errors.rs b/lib/rs/src/errors.rs index 16a25766a19..41a055eca18 100644 --- a/lib/rs/src/errors.rs +++ b/lib/rs/src/errors.rs @@ -19,9 +19,9 @@ use std::convert::{From, Into}; use std::error::Error as StdError; use std::fmt::{Debug, Display, Formatter}; use std::{error, fmt, io, string}; -use try_from::TryFrom; +use std::convert::TryFrom; -use protocol::{TFieldIdentifier, TInputProtocol, TOutputProtocol, TStructIdentifier, TType}; +use crate::protocol::{TFieldIdentifier, TInputProtocol, TOutputProtocol, TStructIdentifier, TType}; // FIXME: should all my error structs impl error::Error as well? // FIXME: should all fields in TransportError, ProtocolError and ApplicationError be optional? @@ -58,7 +58,6 @@ use protocol::{TFieldIdentifier, TInputProtocol, TOutputProtocol, TStructIdentif /// Create a `TransportError`. /// /// ``` -/// use thrift; /// use thrift::{TransportError, TransportErrorKind}; /// /// // explicit @@ -104,7 +103,6 @@ use protocol::{TFieldIdentifier, TInputProtocol, TOutputProtocol, TStructIdentif /// Create an error from a string. /// /// ``` -/// use thrift; /// use thrift::{ApplicationError, ApplicationErrorKind}; /// /// // we just use `From::from` to convert a `String` into a `thrift::Error` @@ -134,7 +132,6 @@ use protocol::{TFieldIdentifier, TInputProtocol, TOutputProtocol, TStructIdentif /// ``` /// /// ``` -/// use std::convert::From; /// use std::error::Error; /// use std::fmt; /// use std::fmt::{Display, Formatter}; @@ -191,7 +188,7 @@ pub enum Error { /// functions are automatically returned as an `ApplicationError`. Application(ApplicationError), /// IDL-defined exception structs. - User(Box), + User(Box), } impl Error { @@ -199,8 +196,8 @@ impl Error { /// /// Application code **should never** call this method directly. pub fn read_application_error_from_in_protocol( - i: &mut TInputProtocol, - ) -> ::Result { + i: &mut dyn TInputProtocol, + ) -> crate::Result { let mut message = "general remote error".to_owned(); let mut kind = ApplicationErrorKind::Unknown; @@ -239,8 +236,8 @@ impl Error { i.read_struct_end()?; Ok(ApplicationError { - kind: kind, - message: message, + kind, + message, }) } @@ -250,8 +247,8 @@ impl Error { /// Application code **should never** call this method directly. pub fn write_application_error_to_out_protocol( e: &ApplicationError, - o: &mut TOutputProtocol, - ) -> ::Result<()> { + o: &mut dyn TOutputProtocol, + ) -> crate::Result<()> { o.write_struct_begin(&TStructIdentifier { name: "TApplicationException".to_owned(), })?; @@ -286,7 +283,7 @@ impl error::Error for Error { } impl Debug for Error { - fn fmt(&self, f: &mut Formatter) -> fmt::Result { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { match *self { Error::Transport(ref e) => Debug::fmt(e, f), Error::Protocol(ref e) => Debug::fmt(e, f), @@ -297,7 +294,7 @@ impl Debug for Error { } impl Display for Error { - fn fmt(&self, f: &mut Formatter) -> fmt::Result { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { match *self { Error::Transport(ref e) => Display::fmt(e, f), Error::Protocol(ref e) => Display::fmt(e, f), @@ -365,7 +362,7 @@ impl TransportError { /// Create a new `TransportError`. pub fn new>(kind: TransportErrorKind, message: S) -> TransportError { TransportError { - kind: kind, + kind, message: message.into(), } } @@ -407,14 +404,14 @@ impl TransportError { } impl Display for TransportError { - fn fmt(&self, f: &mut Formatter) -> fmt::Result { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { write!(f, "{}", self.description()) } } impl TryFrom for TransportErrorKind { - type Err = Error; - fn try_from(from: i32) -> Result { + type Error = Error; + fn try_from(from: i32) -> Result { match from { 0 => Ok(TransportErrorKind::Unknown), 1 => Ok(TransportErrorKind::NotOpen), @@ -493,7 +490,7 @@ impl ProtocolError { /// Create a new `ProtocolError`. pub fn new>(kind: ProtocolErrorKind, message: S) -> ProtocolError { ProtocolError { - kind: kind, + kind, message: message.into(), } } @@ -537,14 +534,14 @@ impl ProtocolError { } impl Display for ProtocolError { - fn fmt(&self, f: &mut Formatter) -> fmt::Result { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { write!(f, "{}", self.description()) } } impl TryFrom for ProtocolErrorKind { - type Err = Error; - fn try_from(from: i32) -> Result { + type Error = Error; + fn try_from(from: i32) -> Result { match from { 0 => Ok(ProtocolErrorKind::Unknown), 1 => Ok(ProtocolErrorKind::InvalidData), @@ -584,7 +581,7 @@ impl ApplicationError { /// Create a new `ApplicationError`. pub fn new>(kind: ApplicationErrorKind, message: S) -> ApplicationError { ApplicationError { - kind: kind, + kind, message: message.into(), } } @@ -641,14 +638,14 @@ impl ApplicationError { } impl Display for ApplicationError { - fn fmt(&self, f: &mut Formatter) -> fmt::Result { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { write!(f, "{}", self.description()) } } impl TryFrom for ApplicationErrorKind { - type Err = Error; - fn try_from(from: i32) -> Result { + type Error = Error; + fn try_from(from: i32) -> Result { match from { 0 => Ok(ApplicationErrorKind::Unknown), 1 => Ok(ApplicationErrorKind::UnknownMethod), diff --git a/lib/rs/src/lib.rs b/lib/rs/src/lib.rs index ca5c7d649dc..f71c3e88a7d 100644 --- a/lib/rs/src/lib.rs +++ b/lib/rs/src/lib.rs @@ -47,14 +47,7 @@ #![crate_type = "lib"] #![doc(test(attr(allow(unused_variables), deny(warnings))))] - -extern crate byteorder; -extern crate integer_encoding; -extern crate threadpool; -extern crate try_from; - -#[macro_use] -extern crate log; +#![deny(bare_trait_objects)] // NOTE: this macro has to be defined before any modules. See: // https://danielkeep.github.io/quick-intro-to-macros.html#some-more-gotchas @@ -75,13 +68,16 @@ pub mod server; pub mod transport; mod errors; -pub use errors::*; +pub use crate::errors::*; mod autogen; -pub use autogen::*; +pub use crate::autogen::*; /// Result type returned by all runtime library functions. /// /// As is convention this is a typedef of `std::result::Result` /// with `E` defined as the `thrift::Error` type. pub type Result = std::result::Result; + +// Re-export ordered-float, since it is used by the generator +pub use ordered_float::OrderedFloat as OrderedFloat; \ No newline at end of file diff --git a/lib/rs/src/protocol/binary.rs b/lib/rs/src/protocol/binary.rs index 19aff3d6cf1..22b496c07d3 100644 --- a/lib/rs/src/protocol/binary.rs +++ b/lib/rs/src/protocol/binary.rs @@ -16,18 +16,17 @@ // under the License. use byteorder::{BigEndian, ByteOrder, ReadBytesExt, WriteBytesExt}; -use std::convert::From; -use try_from::TryFrom; +use std::convert::{From, TryFrom}; use super::{ TFieldIdentifier, TInputProtocol, TInputProtocolFactory, TListIdentifier, TMapIdentifier, TMessageIdentifier, TMessageType, }; use super::{TOutputProtocol, TOutputProtocolFactory, TSetIdentifier, TStructIdentifier, TType}; -use transport::{TReadTransport, TWriteTransport}; -use {ProtocolError, ProtocolErrorKind}; +use crate::transport::{TReadTransport, TWriteTransport}; +use crate::{ProtocolError, ProtocolErrorKind}; -const BINARY_PROTOCOL_VERSION_1: u32 = 0x80010000; +const BINARY_PROTOCOL_VERSION_1: u32 = 0x8001_0000; /// Read messages encoded in the Thrift simple binary encoding. /// @@ -70,8 +69,8 @@ where /// version number in the protocol header. pub fn new(transport: T, strict: bool) -> TBinaryInputProtocol { TBinaryInputProtocol { - strict: strict, - transport: transport, + strict, + transport, } } } @@ -80,8 +79,8 @@ impl TInputProtocol for TBinaryInputProtocol where T: TReadTransport, { - #[cfg_attr(feature = "cargo-clippy", allow(collapsible_if))] - fn read_message_begin(&mut self) -> ::Result { + #[allow(clippy::collapsible_if)] + fn read_message_begin(&mut self) -> crate::Result { let mut first_bytes = vec![0; 4]; self.transport.read_exact(&mut first_bytes[..])?; @@ -92,7 +91,7 @@ where // apparently we got a protocol-version header - check // it, and if it matches, read the rest of the fields if first_bytes[0..2] != [0x80, 0x01] { - Err(::Error::Protocol(ProtocolError { + Err(crate::Error::Protocol(ProtocolError { kind: ProtocolErrorKind::BadVersion, message: format!("received bad version: {:?}", &first_bytes[0..2]), })) @@ -108,7 +107,7 @@ where if self.strict { // we're in strict mode however, and that always // requires the protocol-version header to be written first - Err(::Error::Protocol(ProtocolError { + Err(crate::Error::Protocol(ProtocolError { kind: ProtocolErrorKind::BadVersion, message: format!("received bad version: {:?}", &first_bytes[0..2]), })) @@ -129,19 +128,19 @@ where } } - fn read_message_end(&mut self) -> ::Result<()> { + fn read_message_end(&mut self) -> crate::Result<()> { Ok(()) } - fn read_struct_begin(&mut self) -> ::Result> { + fn read_struct_begin(&mut self) -> crate::Result> { Ok(None) } - fn read_struct_end(&mut self) -> ::Result<()> { + fn read_struct_end(&mut self) -> crate::Result<()> { Ok(()) } - fn read_field_begin(&mut self) -> ::Result { + fn read_field_begin(&mut self) -> crate::Result { let field_type_byte = self.read_byte()?; let field_type = field_type_from_u8(field_type_byte)?; let id = match field_type { @@ -153,11 +152,11 @@ where )) } - fn read_field_end(&mut self) -> ::Result<()> { + fn read_field_end(&mut self) -> crate::Result<()> { Ok(()) } - fn read_bytes(&mut self) -> ::Result> { + fn read_bytes(&mut self) -> crate::Result> { let num_bytes = self.transport.read_i32::()? as usize; let mut buf = vec![0u8; num_bytes]; self.transport @@ -166,7 +165,7 @@ where .map_err(From::from) } - fn read_bool(&mut self) -> ::Result { + fn read_bool(&mut self) -> crate::Result { let b = self.read_i8()?; match b { 0 => Ok(false), @@ -174,66 +173,66 @@ where } } - fn read_i8(&mut self) -> ::Result { + fn read_i8(&mut self) -> crate::Result { self.transport.read_i8().map_err(From::from) } - fn read_i16(&mut self) -> ::Result { + fn read_i16(&mut self) -> crate::Result { self.transport.read_i16::().map_err(From::from) } - fn read_i32(&mut self) -> ::Result { + fn read_i32(&mut self) -> crate::Result { self.transport.read_i32::().map_err(From::from) } - fn read_i64(&mut self) -> ::Result { + fn read_i64(&mut self) -> crate::Result { self.transport.read_i64::().map_err(From::from) } - fn read_double(&mut self) -> ::Result { + fn read_double(&mut self) -> crate::Result { self.transport.read_f64::().map_err(From::from) } - fn read_string(&mut self) -> ::Result { + fn read_string(&mut self) -> crate::Result { let bytes = self.read_bytes()?; String::from_utf8(bytes).map_err(From::from) } - fn read_list_begin(&mut self) -> ::Result { + fn read_list_begin(&mut self) -> crate::Result { let element_type: TType = self.read_byte().and_then(field_type_from_u8)?; let size = self.read_i32()?; Ok(TListIdentifier::new(element_type, size)) } - fn read_list_end(&mut self) -> ::Result<()> { + fn read_list_end(&mut self) -> crate::Result<()> { Ok(()) } - fn read_set_begin(&mut self) -> ::Result { + fn read_set_begin(&mut self) -> crate::Result { let element_type: TType = self.read_byte().and_then(field_type_from_u8)?; let size = self.read_i32()?; Ok(TSetIdentifier::new(element_type, size)) } - fn read_set_end(&mut self) -> ::Result<()> { + fn read_set_end(&mut self) -> crate::Result<()> { Ok(()) } - fn read_map_begin(&mut self) -> ::Result { + fn read_map_begin(&mut self) -> crate::Result { let key_type: TType = self.read_byte().and_then(field_type_from_u8)?; let value_type: TType = self.read_byte().and_then(field_type_from_u8)?; let size = self.read_i32()?; Ok(TMapIdentifier::new(key_type, value_type, size)) } - fn read_map_end(&mut self) -> ::Result<()> { + fn read_map_end(&mut self) -> crate::Result<()> { Ok(()) } // utility // - fn read_byte(&mut self) -> ::Result { + fn read_byte(&mut self) -> crate::Result { self.transport.read_u8().map_err(From::from) } } @@ -250,7 +249,7 @@ impl TBinaryInputProtocolFactory { } impl TInputProtocolFactory for TBinaryInputProtocolFactory { - fn create(&self, transport: Box) -> Box { + fn create(&self, transport: Box) -> Box { Box::new(TBinaryInputProtocol::new(transport, true)) } } @@ -296,8 +295,8 @@ where /// protocol version number in the protocol header. pub fn new(transport: T, strict: bool) -> TBinaryOutputProtocol { TBinaryOutputProtocol { - strict: strict, - transport: transport, + strict, + transport, } } } @@ -306,7 +305,7 @@ impl TOutputProtocol for TBinaryOutputProtocol where T: TWriteTransport, { - fn write_message_begin(&mut self, identifier: &TMessageIdentifier) -> ::Result<()> { + fn write_message_begin(&mut self, identifier: &TMessageIdentifier) -> crate::Result<()> { if self.strict { let message_type: u8 = identifier.message_type.into(); let header = BINARY_PROTOCOL_VERSION_1 | (message_type as u32); @@ -320,21 +319,21 @@ where } } - fn write_message_end(&mut self) -> ::Result<()> { + fn write_message_end(&mut self) -> crate::Result<()> { Ok(()) } - fn write_struct_begin(&mut self, _: &TStructIdentifier) -> ::Result<()> { + fn write_struct_begin(&mut self, _: &TStructIdentifier) -> crate::Result<()> { Ok(()) } - fn write_struct_end(&mut self) -> ::Result<()> { + fn write_struct_end(&mut self) -> crate::Result<()> { Ok(()) } - fn write_field_begin(&mut self, identifier: &TFieldIdentifier) -> ::Result<()> { + fn write_field_begin(&mut self, identifier: &TFieldIdentifier) -> crate::Result<()> { if identifier.id.is_none() && identifier.field_type != TType::Stop { - return Err(::Error::Protocol(ProtocolError { + return Err(crate::Error::Protocol(ProtocolError { kind: ProtocolErrorKind::Unknown, message: format!( "cannot write identifier {:?} without sequence number", @@ -351,20 +350,20 @@ where } } - fn write_field_end(&mut self) -> ::Result<()> { + fn write_field_end(&mut self) -> crate::Result<()> { Ok(()) } - fn write_field_stop(&mut self) -> ::Result<()> { + fn write_field_stop(&mut self) -> crate::Result<()> { self.write_byte(field_type_to_u8(TType::Stop)) } - fn write_bytes(&mut self, b: &[u8]) -> ::Result<()> { + fn write_bytes(&mut self, b: &[u8]) -> crate::Result<()> { self.write_i32(b.len() as i32)?; self.transport.write_all(b).map_err(From::from) } - fn write_bool(&mut self, b: bool) -> ::Result<()> { + fn write_bool(&mut self, b: bool) -> crate::Result<()> { if b { self.write_i8(1) } else { @@ -372,49 +371,49 @@ where } } - fn write_i8(&mut self, i: i8) -> ::Result<()> { + fn write_i8(&mut self, i: i8) -> crate::Result<()> { self.transport.write_i8(i).map_err(From::from) } - fn write_i16(&mut self, i: i16) -> ::Result<()> { + fn write_i16(&mut self, i: i16) -> crate::Result<()> { self.transport.write_i16::(i).map_err(From::from) } - fn write_i32(&mut self, i: i32) -> ::Result<()> { + fn write_i32(&mut self, i: i32) -> crate::Result<()> { self.transport.write_i32::(i).map_err(From::from) } - fn write_i64(&mut self, i: i64) -> ::Result<()> { + fn write_i64(&mut self, i: i64) -> crate::Result<()> { self.transport.write_i64::(i).map_err(From::from) } - fn write_double(&mut self, d: f64) -> ::Result<()> { + fn write_double(&mut self, d: f64) -> crate::Result<()> { self.transport.write_f64::(d).map_err(From::from) } - fn write_string(&mut self, s: &str) -> ::Result<()> { + fn write_string(&mut self, s: &str) -> crate::Result<()> { self.write_bytes(s.as_bytes()) } - fn write_list_begin(&mut self, identifier: &TListIdentifier) -> ::Result<()> { + fn write_list_begin(&mut self, identifier: &TListIdentifier) -> crate::Result<()> { self.write_byte(field_type_to_u8(identifier.element_type))?; self.write_i32(identifier.size) } - fn write_list_end(&mut self) -> ::Result<()> { + fn write_list_end(&mut self) -> crate::Result<()> { Ok(()) } - fn write_set_begin(&mut self, identifier: &TSetIdentifier) -> ::Result<()> { + fn write_set_begin(&mut self, identifier: &TSetIdentifier) -> crate::Result<()> { self.write_byte(field_type_to_u8(identifier.element_type))?; self.write_i32(identifier.size) } - fn write_set_end(&mut self) -> ::Result<()> { + fn write_set_end(&mut self) -> crate::Result<()> { Ok(()) } - fn write_map_begin(&mut self, identifier: &TMapIdentifier) -> ::Result<()> { + fn write_map_begin(&mut self, identifier: &TMapIdentifier) -> crate::Result<()> { let key_type = identifier .key_type .expect("map identifier to write should contain key type"); @@ -426,18 +425,18 @@ where self.write_i32(identifier.size) } - fn write_map_end(&mut self) -> ::Result<()> { + fn write_map_end(&mut self) -> crate::Result<()> { Ok(()) } - fn flush(&mut self) -> ::Result<()> { + fn flush(&mut self) -> crate::Result<()> { self.transport.flush().map_err(From::from) } // utility // - fn write_byte(&mut self, b: u8) -> ::Result<()> { + fn write_byte(&mut self, b: u8) -> crate::Result<()> { self.transport.write_u8(b).map_err(From::from) } } @@ -454,7 +453,7 @@ impl TBinaryOutputProtocolFactory { } impl TOutputProtocolFactory for TBinaryOutputProtocolFactory { - fn create(&self, transport: Box) -> Box { + fn create(&self, transport: Box) -> Box { Box::new(TBinaryOutputProtocol::new(transport, true)) } } @@ -479,7 +478,7 @@ fn field_type_to_u8(field_type: TType) -> u8 { } } -fn field_type_from_u8(b: u8) -> ::Result { +fn field_type_from_u8(b: u8) -> crate::Result { match b { 0x00 => Ok(TType::Stop), 0x01 => Ok(TType::Void), @@ -496,7 +495,7 @@ fn field_type_from_u8(b: u8) -> ::Result { 0x0F => Ok(TType::List), 0x10 => Ok(TType::Utf8), 0x11 => Ok(TType::Utf16), - unkn => Err(::Error::Protocol(ProtocolError { + unkn => Err(crate::Error::Protocol(ProtocolError { kind: ProtocolErrorKind::InvalidData, message: format!("cannot convert {} to TType", unkn), })), @@ -506,11 +505,11 @@ fn field_type_from_u8(b: u8) -> ::Result { #[cfg(test)] mod tests { - use protocol::{ + use crate::protocol::{ TFieldIdentifier, TInputProtocol, TListIdentifier, TMapIdentifier, TMessageIdentifier, TMessageType, TOutputProtocol, TSetIdentifier, TStructIdentifier, TType, }; - use transport::{ReadHalf, TBufferChannel, TIoChannel, WriteHalf}; + use crate::transport::{ReadHalf, TBufferChannel, TIoChannel, WriteHalf}; use super::*; @@ -521,7 +520,7 @@ mod tests { let ident = TMessageIdentifier::new("test", TMessageType::Call, 1); assert!(o_prot.write_message_begin(&ident).is_ok()); - #[cfg_attr(rustfmt, rustfmt::skip)] + #[rustfmt::skip] let expected: [u8; 16] = [ 0x80, 0x01, @@ -551,7 +550,7 @@ mod tests { let ident = TMessageIdentifier::new("test", TMessageType::Call, 1); assert!(o_prot.write_message_begin(&ident).is_ok()); - #[cfg_attr(rustfmt, rustfmt::skip)] + #[rustfmt::skip] let expected: [u8; 13] = [ 0x00, 0x00, @@ -578,7 +577,7 @@ mod tests { let ident = TMessageIdentifier::new("test", TMessageType::Reply, 10); assert!(o_prot.write_message_begin(&ident).is_ok()); - #[cfg_attr(rustfmt, rustfmt::skip)] + #[rustfmt::skip] let expected: [u8; 16] = [ 0x80, 0x01, @@ -608,7 +607,7 @@ mod tests { let ident = TMessageIdentifier::new("test", TMessageType::Reply, 10); assert!(o_prot.write_message_begin(&ident).is_ok()); - #[cfg_attr(rustfmt, rustfmt::skip)] + #[rustfmt::skip] let expected: [u8; 13] = [ 0x00, 0x00, @@ -893,7 +892,7 @@ mod tests { fn must_round_trip_bytes() { let (mut i_prot, mut o_prot) = test_objects(true); - #[cfg_attr(rustfmt, rustfmt::skip)] + #[rustfmt::skip] let bytes: [u8; 25] = [ 0x20, 0xFD, @@ -948,7 +947,7 @@ mod tests { fn assert_no_write(mut write_fn: F, strict: bool) where - F: FnMut(&mut TBinaryOutputProtocol>) -> ::Result<()>, + F: FnMut(&mut TBinaryOutputProtocol>) -> crate::Result<()>, { let (_, mut o_prot) = test_objects(strict); assert!(write_fn(&mut o_prot).is_ok()); diff --git a/lib/rs/src/protocol/compact.rs b/lib/rs/src/protocol/compact.rs index df5edaa8278..6fa364fa0af 100644 --- a/lib/rs/src/protocol/compact.rs +++ b/lib/rs/src/protocol/compact.rs @@ -15,18 +15,17 @@ // specific language governing permissions and limitations // under the License. -use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt}; +use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt}; use integer_encoding::{VarIntReader, VarIntWriter}; -use std::convert::From; +use std::convert::{From, TryFrom}; use std::io; -use try_from::TryFrom; use super::{ TFieldIdentifier, TInputProtocol, TInputProtocolFactory, TListIdentifier, TMapIdentifier, TMessageIdentifier, TMessageType, }; use super::{TOutputProtocol, TOutputProtocolFactory, TSetIdentifier, TStructIdentifier, TType}; -use transport::{TReadTransport, TWriteTransport}; +use crate::transport::{TReadTransport, TWriteTransport}; const COMPACT_PROTOCOL_ID: u8 = 0x82; const COMPACT_VERSION: u8 = 0x01; @@ -77,11 +76,11 @@ where last_read_field_id: 0, read_field_id_stack: Vec::new(), pending_read_bool_value: None, - transport: transport, + transport, } } - fn read_list_set_begin(&mut self) -> ::Result<(TType, i32)> { + fn read_list_set_begin(&mut self) -> crate::Result<(TType, i32)> { let header = self.read_byte()?; let element_type = collection_u8_to_type(header & 0x0F)?; @@ -102,11 +101,11 @@ impl TInputProtocol for TCompactInputProtocol where T: TReadTransport, { - fn read_message_begin(&mut self) -> ::Result { + fn read_message_begin(&mut self) -> crate::Result { let compact_id = self.read_byte()?; if compact_id != COMPACT_PROTOCOL_ID { - Err(::Error::Protocol(::ProtocolError { - kind: ::ProtocolErrorKind::BadVersion, + Err(crate::Error::Protocol(crate::ProtocolError { + kind: crate::ProtocolErrorKind::BadVersion, message: format!("invalid compact protocol header {:?}", compact_id), })) } else { @@ -116,8 +115,8 @@ where let type_and_byte = self.read_byte()?; let received_version = type_and_byte & COMPACT_VERSION_MASK; if received_version != COMPACT_VERSION { - Err(::Error::Protocol(::ProtocolError { - kind: ::ProtocolErrorKind::BadVersion, + Err(crate::Error::Protocol(crate::ProtocolError { + kind: crate::ProtocolErrorKind::BadVersion, message: format!( "cannot process compact protocol version {:?}", received_version @@ -141,17 +140,17 @@ where )) } - fn read_message_end(&mut self) -> ::Result<()> { + fn read_message_end(&mut self) -> crate::Result<()> { Ok(()) } - fn read_struct_begin(&mut self) -> ::Result> { + fn read_struct_begin(&mut self) -> crate::Result> { self.read_field_id_stack.push(self.last_read_field_id); self.last_read_field_id = 0; Ok(None) } - fn read_struct_end(&mut self) -> ::Result<()> { + fn read_struct_end(&mut self) -> crate::Result<()> { self.last_read_field_id = self .read_field_id_stack .pop() @@ -159,7 +158,7 @@ where Ok(()) } - fn read_field_begin(&mut self) -> ::Result { + fn read_field_begin(&mut self) -> crate::Result { // we can read at least one byte, which is: // - the type // - the field delta and the type @@ -194,18 +193,18 @@ where Ok(TFieldIdentifier { name: None, - field_type: field_type, + field_type, id: Some(self.last_read_field_id), }) } } } - fn read_field_end(&mut self) -> ::Result<()> { + fn read_field_end(&mut self) -> crate::Result<()> { Ok(()) } - fn read_bool(&mut self) -> ::Result { + fn read_bool(&mut self) -> crate::Result { match self.pending_read_bool_value.take() { Some(b) => Ok(b), None => { @@ -213,8 +212,8 @@ where match b { 0x01 => Ok(true), 0x02 => Ok(false), - unkn => Err(::Error::Protocol(::ProtocolError { - kind: ::ProtocolErrorKind::InvalidData, + unkn => Err(crate::Error::Protocol(crate::ProtocolError { + kind: crate::ProtocolErrorKind::InvalidData, message: format!("cannot convert {} into bool", unkn), })), } @@ -222,7 +221,7 @@ where } } - fn read_bytes(&mut self) -> ::Result> { + fn read_bytes(&mut self) -> crate::Result> { let len = self.transport.read_varint::()?; let mut buf = vec![0u8; len as usize]; self.transport @@ -231,50 +230,50 @@ where .map(|_| buf) } - fn read_i8(&mut self) -> ::Result { + fn read_i8(&mut self) -> crate::Result { self.read_byte().map(|i| i as i8) } - fn read_i16(&mut self) -> ::Result { + fn read_i16(&mut self) -> crate::Result { self.transport.read_varint::().map_err(From::from) } - fn read_i32(&mut self) -> ::Result { + fn read_i32(&mut self) -> crate::Result { self.transport.read_varint::().map_err(From::from) } - fn read_i64(&mut self) -> ::Result { + fn read_i64(&mut self) -> crate::Result { self.transport.read_varint::().map_err(From::from) } - fn read_double(&mut self) -> ::Result { - self.transport.read_f64::().map_err(From::from) + fn read_double(&mut self) -> crate::Result { + self.transport.read_f64::().map_err(From::from) } - fn read_string(&mut self) -> ::Result { + fn read_string(&mut self) -> crate::Result { let bytes = self.read_bytes()?; String::from_utf8(bytes).map_err(From::from) } - fn read_list_begin(&mut self) -> ::Result { + fn read_list_begin(&mut self) -> crate::Result { let (element_type, element_count) = self.read_list_set_begin()?; Ok(TListIdentifier::new(element_type, element_count)) } - fn read_list_end(&mut self) -> ::Result<()> { + fn read_list_end(&mut self) -> crate::Result<()> { Ok(()) } - fn read_set_begin(&mut self) -> ::Result { + fn read_set_begin(&mut self) -> crate::Result { let (element_type, element_count) = self.read_list_set_begin()?; Ok(TSetIdentifier::new(element_type, element_count)) } - fn read_set_end(&mut self) -> ::Result<()> { + fn read_set_end(&mut self) -> crate::Result<()> { Ok(()) } - fn read_map_begin(&mut self) -> ::Result { + fn read_map_begin(&mut self) -> crate::Result { let element_count = self.transport.read_varint::()? as i32; if element_count == 0 { Ok(TMapIdentifier::new(None, None, 0)) @@ -286,14 +285,14 @@ where } } - fn read_map_end(&mut self) -> ::Result<()> { + fn read_map_end(&mut self) -> crate::Result<()> { Ok(()) } // utility // - fn read_byte(&mut self) -> ::Result { + fn read_byte(&mut self) -> crate::Result { let mut buf = [0u8; 1]; self.transport .read_exact(&mut buf) @@ -323,7 +322,7 @@ impl TCompactInputProtocolFactory { } impl TInputProtocolFactory for TCompactInputProtocolFactory { - fn create(&self, transport: Box) -> Box { + fn create(&self, transport: Box) -> Box { Box::new(TCompactInputProtocol::new(transport)) } } @@ -372,12 +371,12 @@ where last_write_field_id: 0, write_field_id_stack: Vec::new(), pending_write_bool_field_identifier: None, - transport: transport, + transport, } } // FIXME: field_type as unconstrained u8 is bad - fn write_field_header(&mut self, field_type: u8, field_id: i16) -> ::Result<()> { + fn write_field_header(&mut self, field_type: u8, field_id: i16) -> crate::Result<()> { let field_delta = field_id - self.last_write_field_id; if field_delta > 0 && field_delta < 15 { self.write_byte(((field_delta as u8) << 4) | field_type)?; @@ -389,7 +388,7 @@ where Ok(()) } - fn write_list_set_begin(&mut self, element_type: TType, element_count: i32) -> ::Result<()> { + fn write_list_set_begin(&mut self, element_type: TType, element_count: i32) -> crate::Result<()> { let elem_identifier = collection_type_to_u8(element_type); if element_count <= 14 { let header = (element_count as u8) << 4 | elem_identifier; @@ -415,7 +414,7 @@ impl TOutputProtocol for TCompactOutputProtocol where T: TWriteTransport, { - fn write_message_begin(&mut self, identifier: &TMessageIdentifier) -> ::Result<()> { + fn write_message_begin(&mut self, identifier: &TMessageIdentifier) -> crate::Result<()> { self.write_byte(COMPACT_PROTOCOL_ID)?; self.write_byte((u8::from(identifier.message_type) << 5) | COMPACT_VERSION)?; self.write_i32(identifier.sequence_number)?; @@ -423,18 +422,18 @@ where Ok(()) } - fn write_message_end(&mut self) -> ::Result<()> { + fn write_message_end(&mut self) -> crate::Result<()> { self.assert_no_pending_bool_write(); Ok(()) } - fn write_struct_begin(&mut self, _: &TStructIdentifier) -> ::Result<()> { + fn write_struct_begin(&mut self, _: &TStructIdentifier) -> crate::Result<()> { self.write_field_id_stack.push(self.last_write_field_id); self.last_write_field_id = 0; Ok(()) } - fn write_struct_end(&mut self) -> ::Result<()> { + fn write_struct_end(&mut self) -> crate::Result<()> { self.assert_no_pending_bool_write(); self.last_write_field_id = self .write_field_id_stack @@ -443,7 +442,7 @@ where Ok(()) } - fn write_field_begin(&mut self, identifier: &TFieldIdentifier) -> ::Result<()> { + fn write_field_begin(&mut self, identifier: &TFieldIdentifier) -> crate::Result<()> { match identifier.field_type { TType::Bool => { if self.pending_write_bool_field_identifier.is_some() { @@ -464,17 +463,17 @@ where } } - fn write_field_end(&mut self) -> ::Result<()> { + fn write_field_end(&mut self) -> crate::Result<()> { self.assert_no_pending_bool_write(); Ok(()) } - fn write_field_stop(&mut self) -> ::Result<()> { + fn write_field_stop(&mut self) -> crate::Result<()> { self.assert_no_pending_bool_write(); self.write_byte(type_to_u8(TType::Stop)) } - fn write_bool(&mut self, b: bool) -> ::Result<()> { + fn write_bool(&mut self, b: bool) -> crate::Result<()> { match self.pending_write_bool_field_identifier.take() { Some(pending) => { let field_id = pending.id.expect("bool field should have a field id"); @@ -491,61 +490,61 @@ where } } - fn write_bytes(&mut self, b: &[u8]) -> ::Result<()> { + fn write_bytes(&mut self, b: &[u8]) -> crate::Result<()> { self.transport.write_varint(b.len() as u32)?; self.transport.write_all(b).map_err(From::from) } - fn write_i8(&mut self, i: i8) -> ::Result<()> { + fn write_i8(&mut self, i: i8) -> crate::Result<()> { self.write_byte(i as u8) } - fn write_i16(&mut self, i: i16) -> ::Result<()> { + fn write_i16(&mut self, i: i16) -> crate::Result<()> { self.transport .write_varint(i) .map_err(From::from) .map(|_| ()) } - fn write_i32(&mut self, i: i32) -> ::Result<()> { + fn write_i32(&mut self, i: i32) -> crate::Result<()> { self.transport .write_varint(i) .map_err(From::from) .map(|_| ()) } - fn write_i64(&mut self, i: i64) -> ::Result<()> { + fn write_i64(&mut self, i: i64) -> crate::Result<()> { self.transport .write_varint(i) .map_err(From::from) .map(|_| ()) } - fn write_double(&mut self, d: f64) -> ::Result<()> { - self.transport.write_f64::(d).map_err(From::from) + fn write_double(&mut self, d: f64) -> crate::Result<()> { + self.transport.write_f64::(d).map_err(From::from) } - fn write_string(&mut self, s: &str) -> ::Result<()> { + fn write_string(&mut self, s: &str) -> crate::Result<()> { self.write_bytes(s.as_bytes()) } - fn write_list_begin(&mut self, identifier: &TListIdentifier) -> ::Result<()> { + fn write_list_begin(&mut self, identifier: &TListIdentifier) -> crate::Result<()> { self.write_list_set_begin(identifier.element_type, identifier.size) } - fn write_list_end(&mut self) -> ::Result<()> { + fn write_list_end(&mut self) -> crate::Result<()> { Ok(()) } - fn write_set_begin(&mut self, identifier: &TSetIdentifier) -> ::Result<()> { + fn write_set_begin(&mut self, identifier: &TSetIdentifier) -> crate::Result<()> { self.write_list_set_begin(identifier.element_type, identifier.size) } - fn write_set_end(&mut self) -> ::Result<()> { + fn write_set_end(&mut self) -> crate::Result<()> { Ok(()) } - fn write_map_begin(&mut self, identifier: &TMapIdentifier) -> ::Result<()> { + fn write_map_begin(&mut self, identifier: &TMapIdentifier) -> crate::Result<()> { if identifier.size == 0 { self.write_byte(0) } else { @@ -566,18 +565,18 @@ where } } - fn write_map_end(&mut self) -> ::Result<()> { + fn write_map_end(&mut self) -> crate::Result<()> { Ok(()) } - fn flush(&mut self) -> ::Result<()> { + fn flush(&mut self) -> crate::Result<()> { self.transport.flush().map_err(From::from) } // utility // - fn write_byte(&mut self, b: u8) -> ::Result<()> { + fn write_byte(&mut self, b: u8) -> crate::Result<()> { self.transport.write(&[b]).map_err(From::from).map(|_| ()) } } @@ -594,7 +593,7 @@ impl TCompactOutputProtocolFactory { } impl TOutputProtocolFactory for TCompactOutputProtocolFactory { - fn create(&self, transport: Box) -> Box { + fn create(&self, transport: Box) -> Box { Box::new(TCompactOutputProtocol::new(transport)) } } @@ -626,14 +625,14 @@ fn type_to_u8(field_type: TType) -> u8 { } } -fn collection_u8_to_type(b: u8) -> ::Result { +fn collection_u8_to_type(b: u8) -> crate::Result { match b { 0x01 => Ok(TType::Bool), o => u8_to_type(o), } } -fn u8_to_type(b: u8) -> ::Result { +fn u8_to_type(b: u8) -> crate::Result { match b { 0x00 => Ok(TType::Stop), 0x03 => Ok(TType::I08), // equivalent to TType::Byte @@ -646,8 +645,8 @@ fn u8_to_type(b: u8) -> ::Result { 0x0A => Ok(TType::Set), 0x0B => Ok(TType::Map), 0x0C => Ok(TType::Struct), - unkn => Err(::Error::Protocol(::ProtocolError { - kind: ::ProtocolErrorKind::InvalidData, + unkn => Err(crate::Error::Protocol(crate::ProtocolError { + kind: crate::ProtocolErrorKind::InvalidData, message: format!("cannot convert {} into TType", unkn), })), } @@ -656,11 +655,11 @@ fn u8_to_type(b: u8) -> ::Result { #[cfg(test)] mod tests { - use protocol::{ + use crate::protocol::{ TFieldIdentifier, TInputProtocol, TListIdentifier, TMapIdentifier, TMessageIdentifier, TMessageType, TOutputProtocol, TSetIdentifier, TStructIdentifier, TType, }; - use transport::{ReadHalf, TBufferChannel, TIoChannel, WriteHalf}; + use crate::transport::{ReadHalf, TBufferChannel, TIoChannel, WriteHalf}; use super::*; @@ -674,7 +673,7 @@ mod tests { 431 ))); - #[cfg_attr(rustfmt, rustfmt::skip)] + #[rustfmt::skip] let expected: [u8; 8] = [ 0x82, /* protocol ID */ 0x21, /* message type | protocol version */ @@ -696,10 +695,10 @@ mod tests { assert_success!(o_prot.write_message_begin(&TMessageIdentifier::new( "bar", TMessageType::Reply, - 991828 + 991_828 ))); - #[cfg_attr(rustfmt, rustfmt::skip)] + #[rustfmt::skip] let expected: [u8; 9] = [ 0x82, /* protocol ID */ 0x41, /* message type | protocol version */ @@ -715,11 +714,36 @@ mod tests { assert_eq_written_bytes!(o_prot, expected); } + #[test] + fn must_round_trip_upto_i64_maxvalue() { + // See https://issues.apache.org/jira/browse/THRIFT-5131 + for i in 0..64 { + let (mut i_prot, mut o_prot) = test_objects(); + let val: i64 = ((1u64 << i) - 1) as i64; + + o_prot + .write_field_begin(&TFieldIdentifier::new( + "val", + TType::I64, + 1 + )) + .unwrap(); + o_prot.write_i64(val).unwrap(); + o_prot.write_field_end().unwrap(); + o_prot.flush().unwrap(); + + copy_write_buffer_to_read_buffer!(o_prot); + + i_prot.read_field_begin().unwrap(); + assert_eq!(val, i_prot.read_i64().unwrap()); + } + } + #[test] fn must_round_trip_message_begin() { let (mut i_prot, mut o_prot) = test_objects(); - let ident = TMessageIdentifier::new("service_call", TMessageType::Call, 1283948); + let ident = TMessageIdentifier::new("service_call", TMessageType::Call, 1_283_948); assert_success!(o_prot.write_message_begin(&ident)); @@ -763,7 +787,7 @@ mod tests { assert_success!(o_prot.write_field_stop()); assert_success!(o_prot.write_struct_end()); - #[cfg_attr(rustfmt, rustfmt::skip)] + #[rustfmt::skip] let expected: [u8; 5] = [ 0x03, /* field type */ 0x00, /* first field id */ @@ -878,7 +902,7 @@ mod tests { assert_success!(o_prot.write_field_stop()); assert_success!(o_prot.write_struct_end()); - #[cfg_attr(rustfmt, rustfmt::skip)] + #[rustfmt::skip] let expected: [u8; 4] = [ 0x15, /* field delta (1) | field type */ 0x1A, /* field delta (1) | field type */ @@ -991,7 +1015,7 @@ mod tests { assert_success!(o_prot.write_field_stop()); assert_success!(o_prot.write_struct_end()); - #[cfg_attr(rustfmt, rustfmt::skip)] + #[rustfmt::skip] let expected: [u8; 8] = [ 0x05, /* field type */ 0x00, /* first field id */ @@ -1115,7 +1139,7 @@ mod tests { assert_success!(o_prot.write_field_stop()); assert_success!(o_prot.write_struct_end()); - #[cfg_attr(rustfmt, rustfmt::skip)] + #[rustfmt::skip] let expected: [u8; 10] = [ 0x16, /* field delta (1) | field type */ 0x85, /* field delta (8) | field type */ @@ -1132,6 +1156,7 @@ mod tests { assert_eq_written_bytes!(o_prot, expected); } + #[allow(clippy::cognitive_complexity)] #[test] fn must_round_trip_struct_with_mix_of_long_and_delta_fields() { let (mut i_prot, mut o_prot) = test_objects(); @@ -1280,7 +1305,7 @@ mod tests { assert_success!(o_prot.write_field_stop()); assert_success!(o_prot.write_struct_end()); - #[cfg_attr(rustfmt, rustfmt::skip)] + #[rustfmt::skip] let expected: [u8; 7] = [ 0x16, /* field delta (1) | field type */ 0x85, /* field delta (8) | field type */ @@ -1294,6 +1319,7 @@ mod tests { assert_eq_written_bytes!(o_prot, expected); } + #[allow(clippy::cognitive_complexity)] #[test] fn must_round_trip_nested_structs_0() { // last field of the containing struct is a delta @@ -1453,7 +1479,7 @@ mod tests { assert_success!(o_prot.write_field_stop()); assert_success!(o_prot.write_struct_end()); - #[cfg_attr(rustfmt, rustfmt::skip)] + #[rustfmt::skip] let expected: [u8; 7] = [ 0x16, /* field delta (1) | field type */ 0x85, /* field delta (8) | field type */ @@ -1467,6 +1493,7 @@ mod tests { assert_eq_written_bytes!(o_prot, expected); } + #[allow(clippy::cognitive_complexity)] #[test] fn must_round_trip_nested_structs_1() { // last field of the containing struct is a delta @@ -1626,7 +1653,7 @@ mod tests { assert_success!(o_prot.write_field_stop()); assert_success!(o_prot.write_struct_end()); - #[cfg_attr(rustfmt, rustfmt::skip)] + #[rustfmt::skip] let expected: [u8; 7] = [ 0x16, /* field delta (1) | field type */ 0x08, /* field type */ @@ -1640,6 +1667,7 @@ mod tests { assert_eq_written_bytes!(o_prot, expected); } + #[allow(clippy::cognitive_complexity)] #[test] fn must_round_trip_nested_structs_2() { let (mut i_prot, mut o_prot) = test_objects(); @@ -1796,7 +1824,7 @@ mod tests { assert_success!(o_prot.write_field_stop()); assert_success!(o_prot.write_struct_end()); - #[cfg_attr(rustfmt, rustfmt::skip)] + #[rustfmt::skip] let expected: [u8; 8] = [ 0x16, /* field delta (1) | field type */ 0x08, /* field type */ @@ -1811,6 +1839,7 @@ mod tests { assert_eq_written_bytes!(o_prot, expected); } + #[allow(clippy::cognitive_complexity)] #[test] fn must_round_trip_nested_structs_3() { // last field of the containing struct is a full write @@ -1962,7 +1991,7 @@ mod tests { assert_success!(o_prot.write_field_stop()); assert_success!(o_prot.write_struct_end()); - #[cfg_attr(rustfmt, rustfmt::skip)] + #[rustfmt::skip] let expected: [u8; 7] = [ 0x11, /* field delta (1) | true */ 0x82, /* field delta (8) | false */ @@ -1976,6 +2005,7 @@ mod tests { assert_eq_written_bytes!(o_prot, expected); } + #[allow(clippy::cognitive_complexity)] #[test] fn must_round_trip_bool_field() { let (mut i_prot, mut o_prot) = test_objects(); @@ -2221,7 +2251,7 @@ mod tests { fn must_round_trip_large_sized_set_begin() { let (mut i_prot, mut o_prot) = test_objects(); - let ident = TSetIdentifier::new(TType::Map, 3928429); + let ident = TSetIdentifier::new(TType::Map, 3_928_429); assert_success!(o_prot.write_set_begin(&ident)); @@ -2288,7 +2318,7 @@ mod tests { fn must_round_trip_map_begin() { let (mut i_prot, mut o_prot) = test_objects(); - let ident = TMapIdentifier::new(TType::Map, TType::List, 1928349); + let ident = TMapIdentifier::new(TType::Map, TType::List, 1_928_349); assert_success!(o_prot.write_map_begin(&ident)); @@ -2375,9 +2405,34 @@ mod tests { (i_prot, o_prot) } + #[test] + fn must_read_write_double() { + let (mut i_prot, mut o_prot) = test_objects(); + + #[allow(clippy::approx_constant)] + let double = 3.141_592_653_589_793; + o_prot.write_double(double).unwrap(); + copy_write_buffer_to_read_buffer!(o_prot); + + let read_double = i_prot.read_double().unwrap(); + assert!(read_double - double < std::f64::EPSILON); + } + + #[test] + fn must_encode_double_as_other_langs() { + let (_, mut o_prot) = test_objects(); + let expected = [24, 45, 68, 84, 251, 33, 9, 64]; + + #[allow(clippy::approx_constant)] + let double = 3.141_592_653_589_793; + o_prot.write_double(double).unwrap(); + + assert_eq_written_bytes!(o_prot, expected); + } + fn assert_no_write(mut write_fn: F) where - F: FnMut(&mut TCompactOutputProtocol>) -> ::Result<()>, + F: FnMut(&mut TCompactOutputProtocol>) -> crate::Result<()>, { let (_, mut o_prot) = test_objects(); assert!(write_fn(&mut o_prot).is_ok()); diff --git a/lib/rs/src/protocol/mod.rs b/lib/rs/src/protocol/mod.rs index 11c0289f954..f9c1f724c88 100644 --- a/lib/rs/src/protocol/mod.rs +++ b/lib/rs/src/protocol/mod.rs @@ -57,13 +57,12 @@ //! protocol.write_field_end().unwrap(); //! ``` -use std::convert::From; +use std::convert::{From, TryFrom}; use std::fmt; use std::fmt::{Display, Formatter}; -use try_from::TryFrom; -use transport::{TReadTransport, TWriteTransport}; -use {ProtocolError, ProtocolErrorKind}; +use crate::transport::{TReadTransport, TWriteTransport}; +use crate::{ProtocolError, ProtocolErrorKind}; #[cfg(test)] macro_rules! assert_eq_written_bytes { @@ -139,54 +138,54 @@ const MAXIMUM_SKIP_DEPTH: i8 = 64; /// ``` pub trait TInputProtocol { /// Read the beginning of a Thrift message. - fn read_message_begin(&mut self) -> ::Result; + fn read_message_begin(&mut self) -> crate::Result; /// Read the end of a Thrift message. - fn read_message_end(&mut self) -> ::Result<()>; + fn read_message_end(&mut self) -> crate::Result<()>; /// Read the beginning of a Thrift struct. - fn read_struct_begin(&mut self) -> ::Result>; + fn read_struct_begin(&mut self) -> crate::Result>; /// Read the end of a Thrift struct. - fn read_struct_end(&mut self) -> ::Result<()>; + fn read_struct_end(&mut self) -> crate::Result<()>; /// Read the beginning of a Thrift struct field. - fn read_field_begin(&mut self) -> ::Result; + fn read_field_begin(&mut self) -> crate::Result; /// Read the end of a Thrift struct field. - fn read_field_end(&mut self) -> ::Result<()>; + fn read_field_end(&mut self) -> crate::Result<()>; /// Read a bool. - fn read_bool(&mut self) -> ::Result; + fn read_bool(&mut self) -> crate::Result; /// Read a fixed-length byte array. - fn read_bytes(&mut self) -> ::Result>; + fn read_bytes(&mut self) -> crate::Result>; /// Read a word. - fn read_i8(&mut self) -> ::Result; + fn read_i8(&mut self) -> crate::Result; /// Read a 16-bit signed integer. - fn read_i16(&mut self) -> ::Result; + fn read_i16(&mut self) -> crate::Result; /// Read a 32-bit signed integer. - fn read_i32(&mut self) -> ::Result; + fn read_i32(&mut self) -> crate::Result; /// Read a 64-bit signed integer. - fn read_i64(&mut self) -> ::Result; + fn read_i64(&mut self) -> crate::Result; /// Read a 64-bit float. - fn read_double(&mut self) -> ::Result; + fn read_double(&mut self) -> crate::Result; /// Read a fixed-length string (not null terminated). - fn read_string(&mut self) -> ::Result; + fn read_string(&mut self) -> crate::Result; /// Read the beginning of a list. - fn read_list_begin(&mut self) -> ::Result; + fn read_list_begin(&mut self) -> crate::Result; /// Read the end of a list. - fn read_list_end(&mut self) -> ::Result<()>; + fn read_list_end(&mut self) -> crate::Result<()>; /// Read the beginning of a set. - fn read_set_begin(&mut self) -> ::Result; + fn read_set_begin(&mut self) -> crate::Result; /// Read the end of a set. - fn read_set_end(&mut self) -> ::Result<()>; + fn read_set_end(&mut self) -> crate::Result<()>; /// Read the beginning of a map. - fn read_map_begin(&mut self) -> ::Result; + fn read_map_begin(&mut self) -> crate::Result; /// Read the end of a map. - fn read_map_end(&mut self) -> ::Result<()>; + fn read_map_end(&mut self) -> crate::Result<()>; /// Skip a field with type `field_type` recursively until the default /// maximum skip depth is reached. - fn skip(&mut self, field_type: TType) -> ::Result<()> { + fn skip(&mut self, field_type: TType) -> crate::Result<()> { self.skip_till_depth(field_type, MAXIMUM_SKIP_DEPTH) } /// Skip a field with type `field_type` recursively up to `depth` levels. - fn skip_till_depth(&mut self, field_type: TType, depth: i8) -> ::Result<()> { + fn skip_till_depth(&mut self, field_type: TType, depth: i8) -> crate::Result<()> { if depth == 0 { - return Err(::Error::Protocol(ProtocolError { + return Err(crate::Error::Protocol(ProtocolError { kind: ProtocolErrorKind::DepthLimit, message: format!("cannot parse past {:?}", field_type), })); @@ -239,7 +238,7 @@ pub trait TInputProtocol { } self.read_map_end() } - u => Err(::Error::Protocol(ProtocolError { + u => Err(crate::Error::Protocol(ProtocolError { kind: ProtocolErrorKind::Unknown, message: format!("cannot skip field type {:?}", &u), })), @@ -252,7 +251,7 @@ pub trait TInputProtocol { /// Read an unsigned byte. /// /// This method should **never** be used in generated code. - fn read_byte(&mut self) -> ::Result; + fn read_byte(&mut self) -> crate::Result; } /// Converts Thrift identifiers, primitives, containers or structs into a @@ -288,50 +287,50 @@ pub trait TInputProtocol { /// ``` pub trait TOutputProtocol { /// Write the beginning of a Thrift message. - fn write_message_begin(&mut self, identifier: &TMessageIdentifier) -> ::Result<()>; + fn write_message_begin(&mut self, identifier: &TMessageIdentifier) -> crate::Result<()>; /// Write the end of a Thrift message. - fn write_message_end(&mut self) -> ::Result<()>; + fn write_message_end(&mut self) -> crate::Result<()>; /// Write the beginning of a Thrift struct. - fn write_struct_begin(&mut self, identifier: &TStructIdentifier) -> ::Result<()>; + fn write_struct_begin(&mut self, identifier: &TStructIdentifier) -> crate::Result<()>; /// Write the end of a Thrift struct. - fn write_struct_end(&mut self) -> ::Result<()>; + fn write_struct_end(&mut self) -> crate::Result<()>; /// Write the beginning of a Thrift field. - fn write_field_begin(&mut self, identifier: &TFieldIdentifier) -> ::Result<()>; + fn write_field_begin(&mut self, identifier: &TFieldIdentifier) -> crate::Result<()>; /// Write the end of a Thrift field. - fn write_field_end(&mut self) -> ::Result<()>; + fn write_field_end(&mut self) -> crate::Result<()>; /// Write a STOP field indicating that all the fields in a struct have been /// written. - fn write_field_stop(&mut self) -> ::Result<()>; + fn write_field_stop(&mut self) -> crate::Result<()>; /// Write a bool. - fn write_bool(&mut self, b: bool) -> ::Result<()>; + fn write_bool(&mut self, b: bool) -> crate::Result<()>; /// Write a fixed-length byte array. - fn write_bytes(&mut self, b: &[u8]) -> ::Result<()>; + fn write_bytes(&mut self, b: &[u8]) -> crate::Result<()>; /// Write an 8-bit signed integer. - fn write_i8(&mut self, i: i8) -> ::Result<()>; + fn write_i8(&mut self, i: i8) -> crate::Result<()>; /// Write a 16-bit signed integer. - fn write_i16(&mut self, i: i16) -> ::Result<()>; + fn write_i16(&mut self, i: i16) -> crate::Result<()>; /// Write a 32-bit signed integer. - fn write_i32(&mut self, i: i32) -> ::Result<()>; + fn write_i32(&mut self, i: i32) -> crate::Result<()>; /// Write a 64-bit signed integer. - fn write_i64(&mut self, i: i64) -> ::Result<()>; + fn write_i64(&mut self, i: i64) -> crate::Result<()>; /// Write a 64-bit float. - fn write_double(&mut self, d: f64) -> ::Result<()>; + fn write_double(&mut self, d: f64) -> crate::Result<()>; /// Write a fixed-length string. - fn write_string(&mut self, s: &str) -> ::Result<()>; + fn write_string(&mut self, s: &str) -> crate::Result<()>; /// Write the beginning of a list. - fn write_list_begin(&mut self, identifier: &TListIdentifier) -> ::Result<()>; + fn write_list_begin(&mut self, identifier: &TListIdentifier) -> crate::Result<()>; /// Write the end of a list. - fn write_list_end(&mut self) -> ::Result<()>; + fn write_list_end(&mut self) -> crate::Result<()>; /// Write the beginning of a set. - fn write_set_begin(&mut self, identifier: &TSetIdentifier) -> ::Result<()>; + fn write_set_begin(&mut self, identifier: &TSetIdentifier) -> crate::Result<()>; /// Write the end of a set. - fn write_set_end(&mut self) -> ::Result<()>; + fn write_set_end(&mut self) -> crate::Result<()>; /// Write the beginning of a map. - fn write_map_begin(&mut self, identifier: &TMapIdentifier) -> ::Result<()>; + fn write_map_begin(&mut self, identifier: &TMapIdentifier) -> crate::Result<()>; /// Write the end of a map. - fn write_map_end(&mut self) -> ::Result<()>; + fn write_map_end(&mut self) -> crate::Result<()>; /// Flush buffered bytes to the underlying transport. - fn flush(&mut self) -> ::Result<()>; + fn flush(&mut self) -> crate::Result<()>; // utility (DO NOT USE IN GENERATED CODE!!!!) // @@ -339,94 +338,94 @@ pub trait TOutputProtocol { /// Write an unsigned byte. /// /// This method should **never** be used in generated code. - fn write_byte(&mut self, b: u8) -> ::Result<()>; // FIXME: REMOVE + fn write_byte(&mut self, b: u8) -> crate::Result<()>; // FIXME: REMOVE } impl

TInputProtocol for Box

where P: TInputProtocol + ?Sized, { - fn read_message_begin(&mut self) -> ::Result { + fn read_message_begin(&mut self) -> crate::Result { (**self).read_message_begin() } - fn read_message_end(&mut self) -> ::Result<()> { + fn read_message_end(&mut self) -> crate::Result<()> { (**self).read_message_end() } - fn read_struct_begin(&mut self) -> ::Result> { + fn read_struct_begin(&mut self) -> crate::Result> { (**self).read_struct_begin() } - fn read_struct_end(&mut self) -> ::Result<()> { + fn read_struct_end(&mut self) -> crate::Result<()> { (**self).read_struct_end() } - fn read_field_begin(&mut self) -> ::Result { + fn read_field_begin(&mut self) -> crate::Result { (**self).read_field_begin() } - fn read_field_end(&mut self) -> ::Result<()> { + fn read_field_end(&mut self) -> crate::Result<()> { (**self).read_field_end() } - fn read_bool(&mut self) -> ::Result { + fn read_bool(&mut self) -> crate::Result { (**self).read_bool() } - fn read_bytes(&mut self) -> ::Result> { + fn read_bytes(&mut self) -> crate::Result> { (**self).read_bytes() } - fn read_i8(&mut self) -> ::Result { + fn read_i8(&mut self) -> crate::Result { (**self).read_i8() } - fn read_i16(&mut self) -> ::Result { + fn read_i16(&mut self) -> crate::Result { (**self).read_i16() } - fn read_i32(&mut self) -> ::Result { + fn read_i32(&mut self) -> crate::Result { (**self).read_i32() } - fn read_i64(&mut self) -> ::Result { + fn read_i64(&mut self) -> crate::Result { (**self).read_i64() } - fn read_double(&mut self) -> ::Result { + fn read_double(&mut self) -> crate::Result { (**self).read_double() } - fn read_string(&mut self) -> ::Result { + fn read_string(&mut self) -> crate::Result { (**self).read_string() } - fn read_list_begin(&mut self) -> ::Result { + fn read_list_begin(&mut self) -> crate::Result { (**self).read_list_begin() } - fn read_list_end(&mut self) -> ::Result<()> { + fn read_list_end(&mut self) -> crate::Result<()> { (**self).read_list_end() } - fn read_set_begin(&mut self) -> ::Result { + fn read_set_begin(&mut self) -> crate::Result { (**self).read_set_begin() } - fn read_set_end(&mut self) -> ::Result<()> { + fn read_set_end(&mut self) -> crate::Result<()> { (**self).read_set_end() } - fn read_map_begin(&mut self) -> ::Result { + fn read_map_begin(&mut self) -> crate::Result { (**self).read_map_begin() } - fn read_map_end(&mut self) -> ::Result<()> { + fn read_map_end(&mut self) -> crate::Result<()> { (**self).read_map_end() } - fn read_byte(&mut self) -> ::Result { + fn read_byte(&mut self) -> crate::Result { (**self).read_byte() } } @@ -435,95 +434,95 @@ impl

TOutputProtocol for Box

where P: TOutputProtocol + ?Sized, { - fn write_message_begin(&mut self, identifier: &TMessageIdentifier) -> ::Result<()> { + fn write_message_begin(&mut self, identifier: &TMessageIdentifier) -> crate::Result<()> { (**self).write_message_begin(identifier) } - fn write_message_end(&mut self) -> ::Result<()> { + fn write_message_end(&mut self) -> crate::Result<()> { (**self).write_message_end() } - fn write_struct_begin(&mut self, identifier: &TStructIdentifier) -> ::Result<()> { + fn write_struct_begin(&mut self, identifier: &TStructIdentifier) -> crate::Result<()> { (**self).write_struct_begin(identifier) } - fn write_struct_end(&mut self) -> ::Result<()> { + fn write_struct_end(&mut self) -> crate::Result<()> { (**self).write_struct_end() } - fn write_field_begin(&mut self, identifier: &TFieldIdentifier) -> ::Result<()> { + fn write_field_begin(&mut self, identifier: &TFieldIdentifier) -> crate::Result<()> { (**self).write_field_begin(identifier) } - fn write_field_end(&mut self) -> ::Result<()> { + fn write_field_end(&mut self) -> crate::Result<()> { (**self).write_field_end() } - fn write_field_stop(&mut self) -> ::Result<()> { + fn write_field_stop(&mut self) -> crate::Result<()> { (**self).write_field_stop() } - fn write_bool(&mut self, b: bool) -> ::Result<()> { + fn write_bool(&mut self, b: bool) -> crate::Result<()> { (**self).write_bool(b) } - fn write_bytes(&mut self, b: &[u8]) -> ::Result<()> { + fn write_bytes(&mut self, b: &[u8]) -> crate::Result<()> { (**self).write_bytes(b) } - fn write_i8(&mut self, i: i8) -> ::Result<()> { + fn write_i8(&mut self, i: i8) -> crate::Result<()> { (**self).write_i8(i) } - fn write_i16(&mut self, i: i16) -> ::Result<()> { + fn write_i16(&mut self, i: i16) -> crate::Result<()> { (**self).write_i16(i) } - fn write_i32(&mut self, i: i32) -> ::Result<()> { + fn write_i32(&mut self, i: i32) -> crate::Result<()> { (**self).write_i32(i) } - fn write_i64(&mut self, i: i64) -> ::Result<()> { + fn write_i64(&mut self, i: i64) -> crate::Result<()> { (**self).write_i64(i) } - fn write_double(&mut self, d: f64) -> ::Result<()> { + fn write_double(&mut self, d: f64) -> crate::Result<()> { (**self).write_double(d) } - fn write_string(&mut self, s: &str) -> ::Result<()> { + fn write_string(&mut self, s: &str) -> crate::Result<()> { (**self).write_string(s) } - fn write_list_begin(&mut self, identifier: &TListIdentifier) -> ::Result<()> { + fn write_list_begin(&mut self, identifier: &TListIdentifier) -> crate::Result<()> { (**self).write_list_begin(identifier) } - fn write_list_end(&mut self) -> ::Result<()> { + fn write_list_end(&mut self) -> crate::Result<()> { (**self).write_list_end() } - fn write_set_begin(&mut self, identifier: &TSetIdentifier) -> ::Result<()> { + fn write_set_begin(&mut self, identifier: &TSetIdentifier) -> crate::Result<()> { (**self).write_set_begin(identifier) } - fn write_set_end(&mut self) -> ::Result<()> { + fn write_set_end(&mut self) -> crate::Result<()> { (**self).write_set_end() } - fn write_map_begin(&mut self, identifier: &TMapIdentifier) -> ::Result<()> { + fn write_map_begin(&mut self, identifier: &TMapIdentifier) -> crate::Result<()> { (**self).write_map_begin(identifier) } - fn write_map_end(&mut self) -> ::Result<()> { + fn write_map_end(&mut self) -> crate::Result<()> { (**self).write_map_end() } - fn flush(&mut self) -> ::Result<()> { + fn flush(&mut self) -> crate::Result<()> { (**self).flush() } - fn write_byte(&mut self, b: u8) -> ::Result<()> { + fn write_byte(&mut self, b: u8) -> crate::Result<()> { (**self).write_byte(b) } } @@ -547,14 +546,14 @@ where /// ``` pub trait TInputProtocolFactory { // Create a `TInputProtocol` that reads bytes from `transport`. - fn create(&self, transport: Box) -> Box; + fn create(&self, transport: Box) -> Box; } impl TInputProtocolFactory for Box where T: TInputProtocolFactory + ?Sized, { - fn create(&self, transport: Box) -> Box { + fn create(&self, transport: Box) -> Box { (**self).create(transport) } } @@ -578,14 +577,14 @@ where /// ``` pub trait TOutputProtocolFactory { /// Create a `TOutputProtocol` that writes bytes to `transport`. - fn create(&self, transport: Box) -> Box; + fn create(&self, transport: Box) -> Box; } impl TOutputProtocolFactory for Box where T: TOutputProtocolFactory + ?Sized, { - fn create(&self, transport: Box) -> Box { + fn create(&self, transport: Box) -> Box { (**self).create(transport) } } @@ -611,8 +610,8 @@ impl TMessageIdentifier { ) -> TMessageIdentifier { TMessageIdentifier { name: name.into(), - message_type: message_type, - sequence_number: sequence_number, + message_type, + sequence_number, } } } @@ -661,7 +660,7 @@ impl TFieldIdentifier { { TFieldIdentifier { name: name.into().map(|n| n.into()), - field_type: field_type, + field_type, id: id.into(), } } @@ -681,8 +680,8 @@ impl TListIdentifier { /// `element_type`. pub fn new(element_type: TType, size: i32) -> TListIdentifier { TListIdentifier { - element_type: element_type, - size: size, + element_type, + size, } } } @@ -701,8 +700,8 @@ impl TSetIdentifier { /// `element_type`. pub fn new(element_type: TType, size: i32) -> TSetIdentifier { TSetIdentifier { - element_type: element_type, - size: size, + element_type, + size, } } } @@ -729,7 +728,7 @@ impl TMapIdentifier { TMapIdentifier { key_type: key_type.into(), value_type: value_type.into(), - size: size, + size, } } } @@ -748,7 +747,7 @@ pub enum TMessageType { } impl Display for TMessageType { - fn fmt(&self, f: &mut Formatter) -> fmt::Result { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { match *self { TMessageType::Call => write!(f, "Call"), TMessageType::Reply => write!(f, "Reply"), @@ -770,14 +769,14 @@ impl From for u8 { } impl TryFrom for TMessageType { - type Err = ::Error; - fn try_from(b: u8) -> ::Result { + type Error = crate::Error; + fn try_from(b: u8) -> Result { match b { 0x01 => Ok(TMessageType::Call), 0x02 => Ok(TMessageType::Reply), 0x03 => Ok(TMessageType::Exception), 0x04 => Ok(TMessageType::OneWay), - unkn => Err(::Error::Protocol(ProtocolError { + unkn => Err(crate::Error::Protocol(ProtocolError { kind: ProtocolErrorKind::InvalidData, message: format!("cannot convert {} to TMessageType", unkn), })), @@ -823,7 +822,7 @@ pub enum TType { } impl Display for TType { - fn fmt(&self, f: &mut Formatter) -> fmt::Result { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { match *self { TType::Stop => write!(f, "STOP"), TType::Void => write!(f, "void"), @@ -849,12 +848,12 @@ impl Display for TType { /// message sequence number `actual`. /// /// Return `()` if `actual == expected`, `Err` otherwise. -pub fn verify_expected_sequence_number(expected: i32, actual: i32) -> ::Result<()> { +pub fn verify_expected_sequence_number(expected: i32, actual: i32) -> crate::Result<()> { if expected == actual { Ok(()) } else { - Err(::Error::Application(::ApplicationError { - kind: ::ApplicationErrorKind::BadSequenceId, + Err(crate::Error::Application(crate::ApplicationError { + kind: crate::ApplicationErrorKind::BadSequenceId, message: format!("expected {} got {}", expected, actual), })) } @@ -864,12 +863,12 @@ pub fn verify_expected_sequence_number(expected: i32, actual: i32) -> ::Result<( /// service-call name `actual`. /// /// Return `()` if `actual == expected`, `Err` otherwise. -pub fn verify_expected_service_call(expected: &str, actual: &str) -> ::Result<()> { +pub fn verify_expected_service_call(expected: &str, actual: &str) -> crate::Result<()> { if expected == actual { Ok(()) } else { - Err(::Error::Application(::ApplicationError { - kind: ::ApplicationErrorKind::WrongMethodName, + Err(crate::Error::Application(crate::ApplicationError { + kind: crate::ApplicationErrorKind::WrongMethodName, message: format!("expected {} got {}", expected, actual), })) } @@ -879,12 +878,12 @@ pub fn verify_expected_service_call(expected: &str, actual: &str) -> ::Result<() /// `actual`. /// /// Return `()` if `actual == expected`, `Err` otherwise. -pub fn verify_expected_message_type(expected: TMessageType, actual: TMessageType) -> ::Result<()> { +pub fn verify_expected_message_type(expected: TMessageType, actual: TMessageType) -> crate::Result<()> { if expected == actual { Ok(()) } else { - Err(::Error::Application(::ApplicationError { - kind: ::ApplicationErrorKind::InvalidMessageType, + Err(crate::Error::Application(crate::ApplicationError { + kind: crate::ApplicationErrorKind::InvalidMessageType, message: format!("expected {} got {}", expected, actual), })) } @@ -893,11 +892,11 @@ pub fn verify_expected_message_type(expected: TMessageType, actual: TMessageType /// Check if a required Thrift struct field exists. /// /// Return `()` if it does, `Err` otherwise. -pub fn verify_required_field_exists(field_name: &str, field: &Option) -> ::Result<()> { +pub fn verify_required_field_exists(field_name: &str, field: &Option) -> crate::Result<()> { match *field { Some(_) => Ok(()), - None => Err(::Error::Protocol(::ProtocolError { - kind: ::ProtocolErrorKind::Unknown, + None => Err(crate::Error::Protocol(crate::ProtocolError { + kind: crate::ProtocolErrorKind::Unknown, message: format!("missing required field {}", field_name), })), } @@ -908,10 +907,10 @@ pub fn verify_required_field_exists(field_name: &str, field: &Option) -> : /// `field_ident` must *not* have `TFieldIdentifier.field_type` of type `TType::Stop`. /// /// Return `TFieldIdentifier.id` if an id exists, `Err` otherwise. -pub fn field_id(field_ident: &TFieldIdentifier) -> ::Result { +pub fn field_id(field_ident: &TFieldIdentifier) -> crate::Result { field_ident.id.ok_or_else(|| { - ::Error::Protocol(::ProtocolError { - kind: ::ProtocolErrorKind::Unknown, + crate::Error::Protocol(crate::ProtocolError { + kind: crate::ProtocolErrorKind::Unknown, message: format!("missing field in in {:?}", field_ident), }) }) @@ -923,33 +922,33 @@ mod tests { use std::io::Cursor; use super::*; - use transport::{TReadTransport, TWriteTransport}; + use crate::transport::{TReadTransport, TWriteTransport}; #[test] fn must_create_usable_input_protocol_from_concrete_input_protocol() { - let r: Box = Box::new(Cursor::new([0, 1, 2])); + let r: Box = Box::new(Cursor::new([0, 1, 2])); let mut t = TCompactInputProtocol::new(r); takes_input_protocol(&mut t) } #[test] fn must_create_usable_input_protocol_from_boxed_input() { - let r: Box = Box::new(Cursor::new([0, 1, 2])); - let mut t: Box = Box::new(TCompactInputProtocol::new(r)); + let r: Box = Box::new(Cursor::new([0, 1, 2])); + let mut t: Box = Box::new(TCompactInputProtocol::new(r)); takes_input_protocol(&mut t) } #[test] fn must_create_usable_output_protocol_from_concrete_output_protocol() { - let w: Box = Box::new(vec![0u8; 10]); + let w: Box = Box::new(vec![0u8; 10]); let mut t = TCompactOutputProtocol::new(w); takes_output_protocol(&mut t) } #[test] fn must_create_usable_output_protocol_from_boxed_output() { - let w: Box = Box::new(vec![0u8; 10]); - let mut t: Box = Box::new(TCompactOutputProtocol::new(w)); + let w: Box = Box::new(vec![0u8; 10]); + let mut t: Box = Box::new(TCompactOutputProtocol::new(w)); takes_output_protocol(&mut t) } diff --git a/lib/rs/src/protocol/multiplexed.rs b/lib/rs/src/protocol/multiplexed.rs index aaee44f73eb..83498fbdd0c 100644 --- a/lib/rs/src/protocol/multiplexed.rs +++ b/lib/rs/src/protocol/multiplexed.rs @@ -82,7 +82,7 @@ impl

TOutputProtocol for TMultiplexedOutputProtocol

where P: TOutputProtocol, { - fn write_message_begin(&mut self, identifier: &TMessageIdentifier) -> ::Result<()> { + fn write_message_begin(&mut self, identifier: &TMessageIdentifier) -> crate::Result<()> { match identifier.message_type { // FIXME: is there a better way to override identifier here? TMessageType::Call | TMessageType::OneWay => { @@ -96,94 +96,94 @@ where } } - fn write_message_end(&mut self) -> ::Result<()> { + fn write_message_end(&mut self) -> crate::Result<()> { self.inner.write_message_end() } - fn write_struct_begin(&mut self, identifier: &TStructIdentifier) -> ::Result<()> { + fn write_struct_begin(&mut self, identifier: &TStructIdentifier) -> crate::Result<()> { self.inner.write_struct_begin(identifier) } - fn write_struct_end(&mut self) -> ::Result<()> { + fn write_struct_end(&mut self) -> crate::Result<()> { self.inner.write_struct_end() } - fn write_field_begin(&mut self, identifier: &TFieldIdentifier) -> ::Result<()> { + fn write_field_begin(&mut self, identifier: &TFieldIdentifier) -> crate::Result<()> { self.inner.write_field_begin(identifier) } - fn write_field_end(&mut self) -> ::Result<()> { + fn write_field_end(&mut self) -> crate::Result<()> { self.inner.write_field_end() } - fn write_field_stop(&mut self) -> ::Result<()> { + fn write_field_stop(&mut self) -> crate::Result<()> { self.inner.write_field_stop() } - fn write_bytes(&mut self, b: &[u8]) -> ::Result<()> { + fn write_bytes(&mut self, b: &[u8]) -> crate::Result<()> { self.inner.write_bytes(b) } - fn write_bool(&mut self, b: bool) -> ::Result<()> { + fn write_bool(&mut self, b: bool) -> crate::Result<()> { self.inner.write_bool(b) } - fn write_i8(&mut self, i: i8) -> ::Result<()> { + fn write_i8(&mut self, i: i8) -> crate::Result<()> { self.inner.write_i8(i) } - fn write_i16(&mut self, i: i16) -> ::Result<()> { + fn write_i16(&mut self, i: i16) -> crate::Result<()> { self.inner.write_i16(i) } - fn write_i32(&mut self, i: i32) -> ::Result<()> { + fn write_i32(&mut self, i: i32) -> crate::Result<()> { self.inner.write_i32(i) } - fn write_i64(&mut self, i: i64) -> ::Result<()> { + fn write_i64(&mut self, i: i64) -> crate::Result<()> { self.inner.write_i64(i) } - fn write_double(&mut self, d: f64) -> ::Result<()> { + fn write_double(&mut self, d: f64) -> crate::Result<()> { self.inner.write_double(d) } - fn write_string(&mut self, s: &str) -> ::Result<()> { + fn write_string(&mut self, s: &str) -> crate::Result<()> { self.inner.write_string(s) } - fn write_list_begin(&mut self, identifier: &TListIdentifier) -> ::Result<()> { + fn write_list_begin(&mut self, identifier: &TListIdentifier) -> crate::Result<()> { self.inner.write_list_begin(identifier) } - fn write_list_end(&mut self) -> ::Result<()> { + fn write_list_end(&mut self) -> crate::Result<()> { self.inner.write_list_end() } - fn write_set_begin(&mut self, identifier: &TSetIdentifier) -> ::Result<()> { + fn write_set_begin(&mut self, identifier: &TSetIdentifier) -> crate::Result<()> { self.inner.write_set_begin(identifier) } - fn write_set_end(&mut self) -> ::Result<()> { + fn write_set_end(&mut self) -> crate::Result<()> { self.inner.write_set_end() } - fn write_map_begin(&mut self, identifier: &TMapIdentifier) -> ::Result<()> { + fn write_map_begin(&mut self, identifier: &TMapIdentifier) -> crate::Result<()> { self.inner.write_map_begin(identifier) } - fn write_map_end(&mut self) -> ::Result<()> { + fn write_map_end(&mut self) -> crate::Result<()> { self.inner.write_map_end() } - fn flush(&mut self) -> ::Result<()> { + fn flush(&mut self) -> crate::Result<()> { self.inner.flush() } // utility // - fn write_byte(&mut self, b: u8) -> ::Result<()> { + fn write_byte(&mut self, b: u8) -> crate::Result<()> { self.inner.write_byte(b) } } @@ -191,8 +191,8 @@ where #[cfg(test)] mod tests { - use protocol::{TBinaryOutputProtocol, TMessageIdentifier, TMessageType, TOutputProtocol}; - use transport::{TBufferChannel, TIoChannel, WriteHalf}; + use crate::protocol::{TBinaryOutputProtocol, TMessageIdentifier, TMessageType, TOutputProtocol}; + use crate::transport::{TBufferChannel, TIoChannel, WriteHalf}; use super::*; @@ -203,7 +203,7 @@ mod tests { let ident = TMessageIdentifier::new("bar", TMessageType::Call, 2); assert_success!(o_prot.write_message_begin(&ident)); - #[cfg_attr(rustfmt, rustfmt::skip)] + #[rustfmt::skip] let expected: [u8; 19] = [ 0x80, 0x01, /* protocol identifier */ diff --git a/lib/rs/src/protocol/stored.rs b/lib/rs/src/protocol/stored.rs index 8c559788777..179ae07b00d 100644 --- a/lib/rs/src/protocol/stored.rs +++ b/lib/rs/src/protocol/stored.rs @@ -21,7 +21,7 @@ use super::{ TFieldIdentifier, TInputProtocol, TListIdentifier, TMapIdentifier, TMessageIdentifier, TSetIdentifier, TStructIdentifier, }; -use ProtocolErrorKind; +use crate::ProtocolErrorKind; /// `TInputProtocol` required to use a `TMultiplexedProcessor`. /// @@ -42,7 +42,6 @@ use ProtocolErrorKind; /// Create and use a `TStoredInputProtocol`. /// /// ```no_run -/// use thrift; /// use thrift::protocol::{TInputProtocol, TMessageIdentifier, TMessageType, TOutputProtocol}; /// use thrift::protocol::{TBinaryInputProtocol, TBinaryOutputProtocol, TStoredInputProtocol}; /// use thrift::server::TProcessor; @@ -53,8 +52,8 @@ use ProtocolErrorKind; /// impl TProcessor for ActualProcessor { /// fn process( /// &self, -/// _: &mut TInputProtocol, -/// _: &mut TOutputProtocol +/// _: &mut dyn TInputProtocol, +/// _: &mut dyn TOutputProtocol /// ) -> thrift::Result<()> { /// unimplemented!() /// } @@ -80,7 +79,7 @@ use ProtocolErrorKind; /// ``` // FIXME: implement Debug pub struct TStoredInputProtocol<'a> { - inner: &'a mut TInputProtocol, + inner: &'a mut dyn TInputProtocol, message_ident: Option, } @@ -91,9 +90,9 @@ impl<'a> TStoredInputProtocol<'a> { /// with service name stripped - that will be passed to /// `wrapped.read_message_begin(...)`. pub fn new( - wrapped: &mut TInputProtocol, + wrapped: &mut dyn TInputProtocol, message_ident: TMessageIdentifier, - ) -> TStoredInputProtocol { + ) -> TStoredInputProtocol<'_> { TStoredInputProtocol { inner: wrapped, message_ident: message_ident.into(), @@ -102,95 +101,95 @@ impl<'a> TStoredInputProtocol<'a> { } impl<'a> TInputProtocol for TStoredInputProtocol<'a> { - fn read_message_begin(&mut self) -> ::Result { + fn read_message_begin(&mut self) -> crate::Result { self.message_ident.take().ok_or_else(|| { - ::errors::new_protocol_error( + crate::errors::new_protocol_error( ProtocolErrorKind::Unknown, "message identifier already read", ) }) } - fn read_message_end(&mut self) -> ::Result<()> { + fn read_message_end(&mut self) -> crate::Result<()> { self.inner.read_message_end() } - fn read_struct_begin(&mut self) -> ::Result> { + fn read_struct_begin(&mut self) -> crate::Result> { self.inner.read_struct_begin() } - fn read_struct_end(&mut self) -> ::Result<()> { + fn read_struct_end(&mut self) -> crate::Result<()> { self.inner.read_struct_end() } - fn read_field_begin(&mut self) -> ::Result { + fn read_field_begin(&mut self) -> crate::Result { self.inner.read_field_begin() } - fn read_field_end(&mut self) -> ::Result<()> { + fn read_field_end(&mut self) -> crate::Result<()> { self.inner.read_field_end() } - fn read_bytes(&mut self) -> ::Result> { + fn read_bytes(&mut self) -> crate::Result> { self.inner.read_bytes() } - fn read_bool(&mut self) -> ::Result { + fn read_bool(&mut self) -> crate::Result { self.inner.read_bool() } - fn read_i8(&mut self) -> ::Result { + fn read_i8(&mut self) -> crate::Result { self.inner.read_i8() } - fn read_i16(&mut self) -> ::Result { + fn read_i16(&mut self) -> crate::Result { self.inner.read_i16() } - fn read_i32(&mut self) -> ::Result { + fn read_i32(&mut self) -> crate::Result { self.inner.read_i32() } - fn read_i64(&mut self) -> ::Result { + fn read_i64(&mut self) -> crate::Result { self.inner.read_i64() } - fn read_double(&mut self) -> ::Result { + fn read_double(&mut self) -> crate::Result { self.inner.read_double() } - fn read_string(&mut self) -> ::Result { + fn read_string(&mut self) -> crate::Result { self.inner.read_string() } - fn read_list_begin(&mut self) -> ::Result { + fn read_list_begin(&mut self) -> crate::Result { self.inner.read_list_begin() } - fn read_list_end(&mut self) -> ::Result<()> { + fn read_list_end(&mut self) -> crate::Result<()> { self.inner.read_list_end() } - fn read_set_begin(&mut self) -> ::Result { + fn read_set_begin(&mut self) -> crate::Result { self.inner.read_set_begin() } - fn read_set_end(&mut self) -> ::Result<()> { + fn read_set_end(&mut self) -> crate::Result<()> { self.inner.read_set_end() } - fn read_map_begin(&mut self) -> ::Result { + fn read_map_begin(&mut self) -> crate::Result { self.inner.read_map_begin() } - fn read_map_end(&mut self) -> ::Result<()> { + fn read_map_end(&mut self) -> crate::Result<()> { self.inner.read_map_end() } // utility // - fn read_byte(&mut self) -> ::Result { + fn read_byte(&mut self) -> crate::Result { self.inner.read_byte() } } diff --git a/lib/rs/src/server/mod.rs b/lib/rs/src/server/mod.rs index 70b381ac928..050feeec7da 100644 --- a/lib/rs/src/server/mod.rs +++ b/lib/rs/src/server/mod.rs @@ -17,8 +17,8 @@ //! Types used to implement a Thrift server. -use protocol::{TInputProtocol, TMessageIdentifier, TMessageType, TOutputProtocol}; -use {ApplicationError, ApplicationErrorKind}; +use crate::protocol::{TInputProtocol, TMessageIdentifier, TMessageType, TOutputProtocol}; +use crate::{ApplicationError, ApplicationErrorKind}; mod multiplexed; mod threaded; @@ -39,7 +39,6 @@ pub use self::threaded::TServer; /// a Thrift service `SimpleService`. /// /// ```no_run -/// use thrift; /// use thrift::protocol::{TInputProtocol, TOutputProtocol}; /// use thrift::server::TProcessor; /// @@ -57,7 +56,7 @@ pub use self::threaded::TServer; /// /// // `TProcessor` implementation for `SimpleService` /// impl TProcessor for SimpleServiceSyncProcessor { -/// fn process(&self, i: &mut TInputProtocol, o: &mut TOutputProtocol) -> thrift::Result<()> { +/// fn process(&self, i: &mut dyn TInputProtocol, o: &mut dyn TOutputProtocol) -> thrift::Result<()> { /// unimplemented!(); /// } /// } @@ -92,19 +91,19 @@ pub trait TProcessor { /// the response to `o`. /// /// Returns `()` if the handler was executed; `Err` otherwise. - fn process(&self, i: &mut TInputProtocol, o: &mut TOutputProtocol) -> ::Result<()>; + fn process(&self, i: &mut dyn TInputProtocol, o: &mut dyn TOutputProtocol) -> crate::Result<()>; } /// Convenience function used in generated `TProcessor` implementations to /// return an `ApplicationError` if thrift message processing failed. pub fn handle_process_result( msg_ident: &TMessageIdentifier, - res: ::Result<()>, - o_prot: &mut TOutputProtocol, -) -> ::Result<()> { + res: crate::Result<()>, + o_prot: &mut dyn TOutputProtocol, +) -> crate::Result<()> { if let Err(e) = res { let e = match e { - ::Error::Application(a) => a, + crate::Error::Application(a) => a, _ => ApplicationError::new(ApplicationErrorKind::Unknown, format!("{:?}", e)), }; @@ -115,7 +114,7 @@ pub fn handle_process_result( ); o_prot.write_message_begin(&ident)?; - ::Error::write_application_error_to_out_protocol(&e, o_prot)?; + crate::Error::write_application_error_to_out_protocol(&e, o_prot)?; o_prot.write_message_end()?; o_prot.flush() } else { diff --git a/lib/rs/src/server/multiplexed.rs b/lib/rs/src/server/multiplexed.rs index e433794a552..4f41f244784 100644 --- a/lib/rs/src/server/multiplexed.rs +++ b/lib/rs/src/server/multiplexed.rs @@ -15,19 +15,21 @@ // specific language governing permissions and limitations // under the License. +use log::debug; + use std::collections::HashMap; use std::convert::Into; use std::fmt; use std::fmt::{Debug, Formatter}; use std::sync::{Arc, Mutex}; -use protocol::{TInputProtocol, TMessageIdentifier, TOutputProtocol, TStoredInputProtocol}; +use crate::protocol::{TInputProtocol, TMessageIdentifier, TOutputProtocol, TStoredInputProtocol}; use super::{handle_process_result, TProcessor}; -const MISSING_SEPARATOR_AND_NO_DEFAULT: &'static str = +const MISSING_SEPARATOR_AND_NO_DEFAULT: &str = "missing service separator and no default processor set"; -type ThreadSafeProcessor = Box; +type ThreadSafeProcessor = Box; /// A `TProcessor` that can demux service calls to multiple underlying /// Thrift services. @@ -70,13 +72,13 @@ impl TMultiplexedProcessor { /// Returns success if a new entry was inserted. Returns an error if: /// * A processor exists for `service_name` /// * You attempt to register a processor as default, and an existing default exists - #[cfg_attr(feature = "cargo-clippy", allow(map_entry))] + #[allow(clippy::map_entry)] pub fn register>( &mut self, service_name: S, - processor: Box, + processor: Box, as_default: bool, - ) -> ::Result<()> { + ) -> crate::Result<()> { let mut stored = self.stored.lock().unwrap(); let name = service_name.into(); @@ -103,9 +105,9 @@ impl TMultiplexedProcessor { fn process_message( &self, msg_ident: &TMessageIdentifier, - i_prot: &mut TInputProtocol, - o_prot: &mut TOutputProtocol, - ) -> ::Result<()> { + i_prot: &mut dyn TInputProtocol, + o_prot: &mut dyn TOutputProtocol, + ) -> crate::Result<()> { let (svc_name, svc_call) = split_ident_name(&msg_ident.name); debug!("routing svc_name {:?} svc_call {}", &svc_name, &svc_call); @@ -134,7 +136,7 @@ impl TMultiplexedProcessor { } impl TProcessor for TMultiplexedProcessor { - fn process(&self, i_prot: &mut TInputProtocol, o_prot: &mut TOutputProtocol) -> ::Result<()> { + fn process(&self, i_prot: &mut dyn TInputProtocol, o_prot: &mut dyn TOutputProtocol) -> crate::Result<()> { let msg_ident = i_prot.read_message_begin()?; debug!("process incoming msg id:{:?}", &msg_ident); @@ -145,7 +147,7 @@ impl TProcessor for TMultiplexedProcessor { } impl Debug for TMultiplexedProcessor { - fn fmt(&self, f: &mut Formatter) -> fmt::Result { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { let stored = self.stored.lock().unwrap(); write!( f, @@ -181,9 +183,9 @@ mod tests { use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::Arc; - use protocol::{TBinaryInputProtocol, TBinaryOutputProtocol, TMessageIdentifier, TMessageType}; - use transport::{ReadHalf, TBufferChannel, TIoChannel, WriteHalf}; - use {ApplicationError, ApplicationErrorKind}; + use crate::protocol::{TBinaryInputProtocol, TBinaryOutputProtocol, TMessageIdentifier, TMessageType}; + use crate::transport::{ReadHalf, TBufferChannel, TIoChannel, WriteHalf}; + use crate::{ApplicationError, ApplicationErrorKind}; use super::*; @@ -220,7 +222,7 @@ mod tests { let rcvd_ident = i.read_message_begin().unwrap(); let expected_ident = TMessageIdentifier::new("foo", TMessageType::Exception, 10); assert_eq!(rcvd_ident, expected_ident); - let rcvd_err = ::Error::read_application_error_from_in_protocol(&mut i).unwrap(); + let rcvd_err = crate::Error::read_application_error_from_in_protocol(&mut i).unwrap(); let expected_err = ApplicationError::new( ApplicationErrorKind::Unknown, MISSING_SEPARATOR_AND_NO_DEFAULT, @@ -245,7 +247,7 @@ mod tests { let rcvd_ident = i.read_message_begin().unwrap(); let expected_ident = TMessageIdentifier::new("missing:call", TMessageType::Exception, 10); assert_eq!(rcvd_ident, expected_ident); - let rcvd_err = ::Error::read_application_error_from_in_protocol(&mut i).unwrap(); + let rcvd_err = crate::Error::read_application_error_from_in_protocol(&mut i).unwrap(); let expected_err = ApplicationError::new( ApplicationErrorKind::Unknown, missing_processor_message(Some("missing")), @@ -259,7 +261,7 @@ mod tests { } impl TProcessor for Service { - fn process(&self, _: &mut TInputProtocol, _: &mut TOutputProtocol) -> ::Result<()> { + fn process(&self, _: &mut dyn TInputProtocol, _: &mut dyn TOutputProtocol) -> crate::Result<()> { let res = self .invoked .compare_and_swap(false, true, Ordering::Relaxed); diff --git a/lib/rs/src/server/threaded.rs b/lib/rs/src/server/threaded.rs index 8139a4e2789..64bf8bbe5e1 100644 --- a/lib/rs/src/server/threaded.rs +++ b/lib/rs/src/server/threaded.rs @@ -15,15 +15,18 @@ // specific language governing permissions and limitations // under the License. -use std::net::{TcpListener, TcpStream}; +use log::warn; + +use std::net::{TcpListener, TcpStream, ToSocketAddrs}; use std::sync::Arc; use threadpool::ThreadPool; -use protocol::{TInputProtocol, TInputProtocolFactory, TOutputProtocol, TOutputProtocolFactory}; -use transport::{TIoChannel, TReadTransportFactory, TTcpChannel, TWriteTransportFactory}; -use {ApplicationError, ApplicationErrorKind}; +use crate::protocol::{TInputProtocol, TInputProtocolFactory, TOutputProtocol, TOutputProtocolFactory}; +use crate::transport::{TIoChannel, TReadTransportFactory, TTcpChannel, TWriteTransportFactory}; +use crate::{ApplicationError, ApplicationErrorKind}; use super::TProcessor; +use crate::TransportErrorKind; /// Fixed-size thread-pool blocking Thrift server. /// @@ -43,7 +46,6 @@ use super::TProcessor; /// service code. /// /// ```no_run -/// use thrift; /// use thrift::protocol::{TInputProtocolFactory, TOutputProtocolFactory}; /// use thrift::protocol::{TBinaryInputProtocolFactory, TBinaryOutputProtocolFactory}; /// use thrift::protocol::{TInputProtocol, TOutputProtocol}; @@ -65,7 +67,7 @@ use super::TProcessor; /// /// // `TProcessor` implementation for `SimpleService` /// impl TProcessor for SimpleServiceSyncProcessor { -/// fn process(&self, i: &mut TInputProtocol, o: &mut TOutputProtocol) -> thrift::Result<()> { +/// fn process(&self, i: &mut dyn TInputProtocol, o: &mut dyn TOutputProtocol) -> thrift::Result<()> { /// unimplemented!(); /// } /// } @@ -91,10 +93,10 @@ use super::TProcessor; /// let processor = SimpleServiceSyncProcessor::new(SimpleServiceHandlerImpl {}); /// /// // instantiate the server -/// let i_tr_fact: Box = Box::new(TBufferedReadTransportFactory::new()); -/// let i_pr_fact: Box = Box::new(TBinaryInputProtocolFactory::new()); -/// let o_tr_fact: Box = Box::new(TBufferedWriteTransportFactory::new()); -/// let o_pr_fact: Box = Box::new(TBinaryOutputProtocolFactory::new()); +/// let i_tr_fact: Box = Box::new(TBufferedReadTransportFactory::new()); +/// let i_pr_fact: Box = Box::new(TBinaryInputProtocolFactory::new()); +/// let o_tr_fact: Box = Box::new(TBufferedWriteTransportFactory::new()); +/// let o_pr_fact: Box = Box::new(TBinaryOutputProtocolFactory::new()); /// /// let mut server = TServer::new( /// i_tr_fact, @@ -163,14 +165,13 @@ where /// Listen for incoming connections on `listen_address`. /// - /// `listen_address` should be in the form `host:port`, - /// for example: `127.0.0.1:8080`. + /// `listen_address` should implement `ToSocketAddrs` trait. /// /// Return `()` if successful. /// /// Return `Err` when the server cannot bind to `listen_address` or there /// is an unrecoverable error. - pub fn listen(&mut self, listen_address: &str) -> ::Result<()> { + pub fn listen(&mut self, listen_address: A) -> crate::Result<()> { let listener = TcpListener::bind(listen_address)?; for stream in listener.incoming() { match stream { @@ -186,7 +187,7 @@ where } } - Err(::Error::Application(ApplicationError { + Err(crate::Error::Application(ApplicationError { kind: ApplicationErrorKind::Unknown, message: "aborted listen loop".into(), })) @@ -195,7 +196,7 @@ where fn new_protocols_for_connection( &mut self, stream: TcpStream, - ) -> ::Result<(Box, Box)> { + ) -> crate::Result<(Box, Box)> { // create the shared tcp stream let channel = TTcpChannel::with_stream(stream); @@ -217,18 +218,23 @@ where fn handle_incoming_connection( processor: Arc, - i_prot: Box, - o_prot: Box, + i_prot: Box, + o_prot: Box, ) where PRC: TProcessor, { let mut i_prot = i_prot; let mut o_prot = o_prot; loop { - let r = processor.process(&mut *i_prot, &mut *o_prot); - if let Err(e) = r { - warn!("processor completed with error: {:?}", e); - break; + match processor.process(&mut *i_prot, &mut *o_prot) { + Ok(()) => {}, + Err(err) => { + match err { + crate::Error::Transport(ref transport_err) if transport_err.kind == TransportErrorKind::EndOfFile => {}, + other => warn!("processor completed with error: {:?}", other), + } + break; + } } } } diff --git a/lib/rs/src/transport/buffered.rs b/lib/rs/src/transport/buffered.rs index 87cfeff960e..ebdcdc29218 100644 --- a/lib/rs/src/transport/buffered.rs +++ b/lib/rs/src/transport/buffered.rs @@ -139,7 +139,7 @@ impl TBufferedReadTransportFactory { impl TReadTransportFactory for TBufferedReadTransportFactory { /// Create a `TBufferedReadTransport`. - fn create(&self, channel: Box) -> Box { + fn create(&self, channel: Box) -> Box { Box::new(TBufferedReadTransport::new(channel)) } } @@ -200,7 +200,7 @@ where TBufferedWriteTransport { buf: Vec::with_capacity(write_capacity), cap: write_capacity, - channel: channel, + channel, } } } @@ -254,7 +254,7 @@ impl TBufferedWriteTransportFactory { impl TWriteTransportFactory for TBufferedWriteTransportFactory { /// Create a `TBufferedWriteTransport`. - fn create(&self, channel: Box) -> Box { + fn create(&self, channel: Box) -> Box { Box::new(TBufferedWriteTransport::new(channel)) } } @@ -264,7 +264,7 @@ mod tests { use std::io::{Read, Write}; use super::*; - use transport::TBufferChannel; + use crate::transport::TBufferChannel; #[test] fn must_return_zero_if_read_buffer_is_empty() { @@ -347,8 +347,8 @@ mod tests { // fill the underlying transport's byte buffer let mut readable_bytes = [0u8; 10]; - for i in 0..10 { - readable_bytes[i] = i as u8; + for (i, b) in readable_bytes.iter_mut().enumerate() { + *b = i as u8; } t.chan.set_readable_bytes(&readable_bytes); @@ -365,8 +365,8 @@ mod tests { assert_eq!(&buf, &[0, 1, 2, 3, 4, 5, 6, 7]); // let's clear out the buffer and try read again - for i in 0..8 { - buf[i] = 0; + for b in &mut buf{ + *b = 0; } let read_result = t.read(&mut buf); diff --git a/lib/rs/src/transport/framed.rs b/lib/rs/src/transport/framed.rs index a00930778c9..c30ccd9ed62 100644 --- a/lib/rs/src/transport/framed.rs +++ b/lib/rs/src/transport/framed.rs @@ -121,7 +121,7 @@ impl TFramedReadTransportFactory { impl TReadTransportFactory for TFramedReadTransportFactory { /// Create a `TFramedReadTransport`. - fn create(&self, channel: Box) -> Box { + fn create(&self, channel: Box) -> Box { Box::new(TFramedReadTransport::new(channel)) } } @@ -231,7 +231,7 @@ impl TFramedWriteTransportFactory { impl TWriteTransportFactory for TFramedWriteTransportFactory { /// Create a `TFramedWriteTransport`. - fn create(&self, channel: Box) -> Box { + fn create(&self, channel: Box) -> Box { Box::new(TFramedWriteTransport::new(channel)) } } @@ -239,7 +239,7 @@ impl TWriteTransportFactory for TFramedWriteTransportFactory { #[cfg(test)] mod tests { use super::*; - use transport::mem::TBufferChannel; + use crate::transport::mem::TBufferChannel; // FIXME: test a forced reserve diff --git a/lib/rs/src/transport/mem.rs b/lib/rs/src/transport/mem.rs index 82c4b579f60..fe1c5434699 100644 --- a/lib/rs/src/transport/mem.rs +++ b/lib/rs/src/transport/mem.rs @@ -31,7 +31,7 @@ use super::{ReadHalf, TIoChannel, WriteHalf}; /// `set_readable_bytes(...)`. Callers can then read until the buffer is /// depleted. No further reads are accepted until the internal read buffer is /// replenished again. -#[derive(Debug)] +#[derive(Clone, Debug)] pub struct TBufferChannel { read: Arc>, write: Arc>, @@ -139,7 +139,7 @@ impl TBufferChannel { } impl TIoChannel for TBufferChannel { - fn split(self) -> ::Result<(ReadHalf, WriteHalf)> + fn split(self) -> crate::Result<(ReadHalf, WriteHalf)> where Self: Sized, { @@ -153,7 +153,9 @@ impl TIoChannel for TBufferChannel { WriteHalf { handle: TBufferChannel { read: self.read.clone(), - write: self.write.clone(), + // NOTE: not cloning here, since this is the last statement + // in this method and `write` can take ownership of `self.write` + write: self.write, }, }, )) diff --git a/lib/rs/src/transport/mod.rs b/lib/rs/src/transport/mod.rs index a623350103e..d02a87c64e3 100644 --- a/lib/rs/src/transport/mod.rs +++ b/lib/rs/src/transport/mod.rs @@ -64,7 +64,7 @@ pub trait TReadTransport: Read {} /// accepted client connections. pub trait TReadTransportFactory { /// Create a `TTransport` that wraps a channel over which bytes are to be read. - fn create(&self, channel: Box) -> Box; + fn create(&self, channel: Box) -> Box; } /// Identifies a transport used by `TOutputProtocol` to send bytes. @@ -74,7 +74,7 @@ pub trait TWriteTransport: Write {} /// accepted client connections. pub trait TWriteTransportFactory { /// Create a `TTransport` that wraps a channel over which bytes are to be sent. - fn create(&self, channel: Box) -> Box; + fn create(&self, channel: Box) -> Box; } impl TReadTransport for T where T: Read {} @@ -87,7 +87,7 @@ impl TReadTransportFactory for Box where T: TReadTransportFactory + ?Sized, { - fn create(&self, channel: Box) -> Box { + fn create(&self, channel: Box) -> Box { (**self).create(channel) } } @@ -96,7 +96,7 @@ impl TWriteTransportFactory for Box where T: TWriteTransportFactory + ?Sized, { - fn create(&self, channel: Box) -> Box { + fn create(&self, channel: Box) -> Box { (**self).create(channel) } } @@ -111,7 +111,7 @@ pub trait TIoChannel: Read + Write { /// Returned halves may share the underlying OS channel or buffer resources. /// Implementations **should ensure** that these two halves can be safely /// used independently by concurrent threads. - fn split(self) -> ::Result<(::transport::ReadHalf, ::transport::WriteHalf)> + fn split(self) -> crate::Result<(crate::transport::ReadHalf, crate::transport::WriteHalf)> where Self: Sized; } @@ -231,7 +231,7 @@ mod tests { #[test] fn must_create_usable_read_channel_from_boxed_read() { - let r: Box = Box::new(Cursor::new([0, 1, 2])); + let r: Box = Box::new(Cursor::new([0, 1, 2])); let _ = TBufferedReadTransport::new(r); } @@ -243,7 +243,7 @@ mod tests { #[test] fn must_create_usable_write_channel_from_boxed_write() { - let w: Box = Box::new(vec![0u8; 10]); + let w: Box = Box::new(vec![0u8; 10]); let _ = TBufferedWriteTransport::new(w); } @@ -257,7 +257,7 @@ mod tests { #[test] fn must_create_usable_read_transport_from_boxed_read() { let r = Cursor::new([0, 1, 2]); - let mut t: Box = Box::new(TBufferedReadTransport::new(r)); + let mut t: Box = Box::new(TBufferedReadTransport::new(r)); takes_read_transport(&mut t) } @@ -271,7 +271,7 @@ mod tests { #[test] fn must_create_usable_write_transport_from_boxed_write() { let w = vec![0u8; 10]; - let mut t: Box = Box::new(TBufferedWriteTransport::new(w)); + let mut t: Box = Box::new(TBufferedWriteTransport::new(w)); takes_write_transport(&mut t) } diff --git a/lib/rs/src/transport/socket.rs b/lib/rs/src/transport/socket.rs index 0bef67bed00..275bcd45982 100644 --- a/lib/rs/src/transport/socket.rs +++ b/lib/rs/src/transport/socket.rs @@ -18,10 +18,10 @@ use std::convert::From; use std::io; use std::io::{ErrorKind, Read, Write}; -use std::net::{Shutdown, TcpStream}; +use std::net::{Shutdown, TcpStream, ToSocketAddrs}; use super::{ReadHalf, TIoChannel, WriteHalf}; -use {new_transport_error, TransportErrorKind}; +use crate::{new_transport_error, TransportErrorKind}; /// Bidirectional TCP/IP channel. /// @@ -81,8 +81,8 @@ impl TTcpChannel { } } - /// Connect to `remote_address`, which should have the form `host:port`. - pub fn open(&mut self, remote_address: &str) -> ::Result<()> { + /// Connect to `remote_address`, which should implement `ToSocketAddrs` trait. + pub fn open(&mut self, remote_address: A) -> crate::Result<()> { if self.stream.is_some() { Err(new_transport_error( TransportErrorKind::AlreadyOpen, @@ -103,7 +103,7 @@ impl TTcpChannel { /// /// Both send and receive halves are closed, and this instance can no /// longer be used to communicate with another endpoint. - pub fn close(&mut self) -> ::Result<()> { + pub fn close(&mut self) -> crate::Result<()> { self.if_set(|s| s.shutdown(Shutdown::Both)) .map_err(From::from) } @@ -124,7 +124,7 @@ impl TTcpChannel { } impl TIoChannel for TTcpChannel { - fn split(self) -> ::Result<(ReadHalf, WriteHalf)> + fn split(self) -> crate::Result<(ReadHalf, WriteHalf)> where Self: Sized, { diff --git a/lib/rs/test/Cargo.toml b/lib/rs/test/Cargo.toml index 50dec1972c2..9341bd56383 100644 --- a/lib/rs/test/Cargo.toml +++ b/lib/rs/test/Cargo.toml @@ -1,14 +1,13 @@ [package] name = "kitchen-sink" version = "0.1.0" +edition = "2018" license = "Apache-2.0" authors = ["Apache Thrift Developers "] publish = false [dependencies] -clap = "<2.28.0" -ordered-float = "0.3.0" -try_from = "0.2.0" +clap = "2.33" [dependencies.thrift] path = "../" diff --git a/lib/rs/test/Makefile.am b/lib/rs/test/Makefile.am index 8edd756bfa3..5dc4f56ead0 100644 --- a/lib/rs/test/Makefile.am +++ b/lib/rs/test/Makefile.am @@ -25,6 +25,7 @@ stubs: thrifts/Base_One.thrift thrifts/Base_Two.thrift thrifts/Midlayer.thrift t $(THRIFT) -I ./thrifts -out src --gen rs thrifts/Midlayer.thrift $(THRIFT) -I ./thrifts -out src --gen rs thrifts/Ultimate.thrift $(THRIFT) -out src --gen rs $(top_builddir)/test/Recursive.thrift + $(THRIFT) -out src --gen rs $(top_builddir)/test/Identifiers.thrift #THRIFT-4953 check: stubs $(CARGO) build @@ -40,6 +41,8 @@ clean-local: -$(RM) src/base_two.rs -$(RM) src/midlayer.rs -$(RM) src/ultimate.rs + -$(RM) src/recursive.rs + -$(RM) src/identifiers.rs -$(RM) -r bin EXTRA_DIST = \ diff --git a/lib/rs/test/src/bin/kitchen_sink_client.rs b/lib/rs/test/src/bin/kitchen_sink_client.rs index d295c8870d6..74197de7f0b 100644 --- a/lib/rs/test/src/bin/kitchen_sink_client.rs +++ b/lib/rs/test/src/bin/kitchen_sink_client.rs @@ -15,11 +15,7 @@ // specific language governing permissions and limitations // under the License. -#[macro_use] -extern crate clap; - -extern crate kitchen_sink; -extern crate thrift; +use clap::{clap_app, value_t}; use std::convert::Into; @@ -28,6 +24,8 @@ use kitchen_sink::midlayer::{MealServiceSyncClient, TMealServiceSyncClient}; use kitchen_sink::recursive; use kitchen_sink::recursive::{CoRec, CoRec2, RecList, RecTree, TTestServiceSyncClient}; use kitchen_sink::ultimate::{FullMealServiceSyncClient, TFullMealServiceSyncClient}; + +use thrift; use thrift::protocol::{ TBinaryInputProtocol, TBinaryOutputProtocol, TCompactInputProtocol, TCompactOutputProtocol, TInputProtocol, TOutputProtocol, @@ -69,7 +67,7 @@ fn run() -> thrift::Result<()> { TFramedWriteTransport::new(o_chan), ); - let (i_prot, o_prot): (Box, Box) = match protocol { + let (i_prot, o_prot): (Box, Box) = match protocol { "binary" => ( Box::new(TBinaryInputProtocol::new(i_tran, true)), Box::new(TBinaryOutputProtocol::new(o_tran, true)), @@ -86,8 +84,8 @@ fn run() -> thrift::Result<()> { fn run_client( service: &str, - i_prot: Box, - o_prot: Box, + i_prot: Box, + o_prot: Box, ) -> thrift::Result<()> { match service { "full" => exec_full_meal_client(i_prot, o_prot), @@ -110,8 +108,8 @@ fn tcp_channel( } fn exec_meal_client( - i_prot: Box, - o_prot: Box, + i_prot: Box, + o_prot: Box, ) -> thrift::Result<()> { let mut client = MealServiceSyncClient::new(i_prot, o_prot); @@ -127,8 +125,8 @@ fn exec_meal_client( } fn exec_full_meal_client( - i_prot: Box, - o_prot: Box, + i_prot: Box, + o_prot: Box, ) -> thrift::Result<()> { let mut client = FullMealServiceSyncClient::new(i_prot, o_prot); @@ -141,8 +139,8 @@ fn exec_full_meal_client( } fn exec_recursive_client( - i_prot: Box, - o_prot: Box, + i_prot: Box, + o_prot: Box, ) -> thrift::Result<()> { let mut client = recursive::TestServiceSyncClient::new(i_prot, o_prot); diff --git a/lib/rs/test/src/bin/kitchen_sink_server.rs b/lib/rs/test/src/bin/kitchen_sink_server.rs index 73801eaf892..6df39e4bc6c 100644 --- a/lib/rs/test/src/bin/kitchen_sink_server.rs +++ b/lib/rs/test/src/bin/kitchen_sink_server.rs @@ -15,11 +15,9 @@ // specific language governing permissions and limitations // under the License. -#[macro_use] -extern crate clap; -extern crate kitchen_sink; -extern crate thrift; +use clap::{clap_app, value_t}; +use thrift; use thrift::protocol::{ TBinaryInputProtocolFactory, TBinaryOutputProtocolFactory, TCompactInputProtocolFactory, TCompactOutputProtocolFactory, TInputProtocolFactory, TOutputProtocolFactory, @@ -76,8 +74,8 @@ fn run() -> thrift::Result<()> { let w_transport_factory = TFramedWriteTransportFactory::new(); let (i_protocol_factory, o_protocol_factory): ( - Box, - Box, + Box, + Box, ) = match &*protocol { "binary" => ( Box::new(TBinaryInputProtocolFactory::new()), diff --git a/lib/rs/test/src/lib.rs b/lib/rs/test/src/lib.rs index e5e176e14b8..91fd0272848 100644 --- a/lib/rs/test/src/lib.rs +++ b/lib/rs/test/src/lib.rs @@ -15,10 +15,6 @@ // specific language governing permissions and limitations // under the License. -extern crate ordered_float; -extern crate thrift; -extern crate try_from; - pub mod base_one; pub mod base_two; pub mod midlayer; diff --git a/lib/st/package.xml b/lib/st/package.xml index 7f6b2343de6..eab405225e2 100644 --- a/lib/st/package.xml +++ b/lib/st/package.xml @@ -17,7 +17,7 @@ specific language governing permissions and limitations under the License. --> - + libthrift-st thrift.st diff --git a/lib/netcore/Makefile.am b/lib/swift/Makefile.am similarity index 73% rename from lib/netcore/Makefile.am rename to lib/swift/Makefile.am index caf3f34558b..6b88b06a713 100644 --- a/lib/netcore/Makefile.am +++ b/lib/swift/Makefile.am @@ -17,25 +17,30 @@ # under the License. # -SUBDIRS = . +SUBDIRS = . all-local: - $(DOTNETCORE) build + swift build --configuration release -check-local: - $(DOTNETCORE) test Tests/Thrift.Tests/Thrift.Tests.csproj - ${DOTNETCORE} test Tests/Thrift.IntegrationTests/Thrift.IntegrationTests.csproj +install-exec-hook: + swift install clean-local: - $(RM) -r Thrift/bin - $(RM) -r Thrift/obj + swift package clean + rm -rf .build + +precross: + swift + +check-local: + swift test EXTRA_DIST = \ - README.md \ + Package.swift \ + Sources \ Tests \ - Thrift \ - Thrift.sln \ - build.cmd \ - build.sh \ - runtests.cmd \ - runtests.sh + README.md + +MAINTAINERCLEANFILES = \ + Makefile \ + Makefile.in diff --git a/lib/swift/Package.swift b/lib/swift/Package.swift index b533f6086ad..1bae5c11a3d 100644 --- a/lib/swift/Package.swift +++ b/lib/swift/Package.swift @@ -1,3 +1,4 @@ +// swift-tools-version:5.1 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file @@ -20,5 +21,12 @@ import PackageDescription let package = Package( - name: "Thrift" + name: "Thrift", + products: [ + .library(name: "Thrift", targets: ["Thrift"]) + ], + targets: [ + .target(name: "Thrift", path: "Sources"), + .testTarget(name: "ThriftTests", dependencies: ["Thrift"]) + ] ) diff --git a/lib/swift/README.md b/lib/swift/README.md index 4fdeacfa824..6f109618424 100644 --- a/lib/swift/README.md +++ b/lib/swift/README.md @@ -22,7 +22,6 @@ under the License. ## Build - swift build ## Test diff --git a/lib/swift/Sources/TApplicationError.swift b/lib/swift/Sources/TApplicationError.swift index bc393968005..cfaf285d8ff 100644 --- a/lib/swift/Sources/TApplicationError.swift +++ b/lib/swift/Sources/TApplicationError.swift @@ -146,9 +146,12 @@ extension TApplicationError : TSerializable { try proto.writeFieldStop() try proto.writeStructEnd() } - - public var hashValue: Int { - return error.thriftErrorCode &+ (message?.hashValue ?? 0) +} + +extension TApplicationError: Hashable { + public func hash(into hasher: inout Hasher) { + hasher.combine(error.thriftErrorCode) + hasher.combine(message) } } diff --git a/lib/swift/Sources/TBinaryProtocol.swift b/lib/swift/Sources/TBinaryProtocol.swift index a97249abc9e..85acce0455f 100644 --- a/lib/swift/Sources/TBinaryProtocol.swift +++ b/lib/swift/Sources/TBinaryProtocol.swift @@ -325,7 +325,7 @@ public class TBinaryProtocol: TProtocol { } public func write(_ value: UInt8) throws { - let buff = Data(bytes: [value]) + let buff = Data([value]) try ProtocolTransportTry(error: TProtocolError(message: "Transport write failed")) { try self.transport.write(data: buff) @@ -334,8 +334,8 @@ public class TBinaryProtocol: TProtocol { public func write(_ value: Int16) throws { var buff = Data() - buff.append(Data(bytes: [UInt8(0xff & (value >> 8))])) - buff.append(Data(bytes: [UInt8(0xff & (value))])) + buff.append(Data([UInt8(0xff & (value >> 8))])) + buff.append(Data([UInt8(0xff & (value))])) try ProtocolTransportTry(error: TProtocolError(message: "Transport write failed")) { try self.transport.write(data: buff) } @@ -343,10 +343,10 @@ public class TBinaryProtocol: TProtocol { public func write(_ value: Int32) throws { var buff = Data() - buff.append(Data(bytes: [UInt8(0xff & (value >> 24))])) - buff.append(Data(bytes: [UInt8(0xff & (value >> 16))])) - buff.append(Data(bytes: [UInt8(0xff & (value >> 8))])) - buff.append(Data(bytes: [UInt8(0xff & (value))])) + buff.append(Data([UInt8(0xff & (value >> 24))])) + buff.append(Data([UInt8(0xff & (value >> 16))])) + buff.append(Data([UInt8(0xff & (value >> 8))])) + buff.append(Data([UInt8(0xff & (value))])) try ProtocolTransportTry(error: TProtocolError(message: "Transport write failed")) { try self.transport.write(data: buff) @@ -355,14 +355,14 @@ public class TBinaryProtocol: TProtocol { public func write(_ value: Int64) throws { var buff = Data() - buff.append(Data(bytes: [UInt8(0xff & (value >> 56))])) - buff.append(Data(bytes: [UInt8(0xff & (value >> 48))])) - buff.append(Data(bytes: [UInt8(0xff & (value >> 40))])) - buff.append(Data(bytes: [UInt8(0xff & (value >> 32))])) - buff.append(Data(bytes: [UInt8(0xff & (value >> 24))])) - buff.append(Data(bytes: [UInt8(0xff & (value >> 16))])) - buff.append(Data(bytes: [UInt8(0xff & (value >> 8))])) - buff.append(Data(bytes: [UInt8(0xff & (value))])) + buff.append(Data([UInt8(0xff & (value >> 56))])) + buff.append(Data([UInt8(0xff & (value >> 48))])) + buff.append(Data([UInt8(0xff & (value >> 40))])) + buff.append(Data([UInt8(0xff & (value >> 32))])) + buff.append(Data([UInt8(0xff & (value >> 24))])) + buff.append(Data([UInt8(0xff & (value >> 16))])) + buff.append(Data([UInt8(0xff & (value >> 8))])) + buff.append(Data([UInt8(0xff & (value))])) try ProtocolTransportTry(error: TProtocolError(message: "Transport write failed")) { try self.transport.write(data: buff) diff --git a/lib/swift/Sources/TClient.swift b/lib/swift/Sources/TClient.swift index cc3288a8c05..049027f7726 100644 --- a/lib/swift/Sources/TClient.swift +++ b/lib/swift/Sources/TClient.swift @@ -40,16 +40,3 @@ open class TAsyncClient { self.factory = factory } } - - -public enum TAsyncResult { - case success(T) - case error(Swift.Error) - - public func value() throws -> T { - switch self { - case .success(let t): return t - case .error(let e): throw e - } - } -} diff --git a/lib/swift/Sources/TCompactProtocol.swift b/lib/swift/Sources/TCompactProtocol.swift index 59773c34af3..81a51f55114 100644 --- a/lib/swift/Sources/TCompactProtocol.swift +++ b/lib/swift/Sources/TCompactProtocol.swift @@ -67,7 +67,7 @@ public class TCompactProtocol: TProtocol { /// Mark: - TCompactProtocol helpers func writebyteDirect(_ byte: UInt8) throws { - let byte = Data(bytes: [byte]) + let byte = Data([byte]) try ProtocolTransportTry(error: TProtocolError(message: "Transport Write Failed")) { try self.transport.write(data: byte) } @@ -90,7 +90,7 @@ public class TCompactProtocol: TProtocol { } try ProtocolTransportTry(error: TProtocolError(message: "Transport Write Failed")) { - try self.transport.write(data: Data(bytes: i32buf[0..> UInt8(TCType.typeShiftAmount)) & TCType.typeBits @@ -374,10 +371,7 @@ public class TCompactProtocol: TProtocol { try ProtocolTransportTry(error: TProtocolError(message: "Transport Read Failed")) { buff = try self.transport.readAll(size: 8) } - - let i64: UInt64 = buff.withUnsafeBytes { (ptr: UnsafePointer) -> UInt64 in - return UnsafePointer(OpaquePointer(ptr)).pointee - } + let i64: UInt64 = buff.withUnsafeBytes { $0.load(as: UInt64.self) } let bits = CFSwapInt64LittleToHost(i64) return Double(bitPattern: bits) } @@ -485,7 +479,6 @@ public class TCompactProtocol: TProtocol { } lastFieldId = UInt8(fieldID) - } public func writeFieldStop() throws { diff --git a/lib/swift/Sources/TEnum.swift b/lib/swift/Sources/TEnum.swift index fedfdb12489..2e91f636002 100644 --- a/lib/swift/Sources/TEnum.swift +++ b/lib/swift/Sources/TEnum.swift @@ -24,7 +24,10 @@ public protocol TEnum : TSerializable, Hashable { extension TEnum { public static var thriftType: TType { return .i32 } - public var hashValue: Int { return rawValue.hashValue } + + public func hash(into hasher: inout Hasher) { + hasher.combine(rawValue) + } public func write(to proto: TProtocol) throws { try proto.write(rawValue) diff --git a/lib/swift/Sources/TFileTransport.swift b/lib/swift/Sources/TFileTransport.swift index fe2253da4c6..75eba4042d3 100644 --- a/lib/swift/Sources/TFileTransport.swift +++ b/lib/swift/Sources/TFileTransport.swift @@ -88,7 +88,7 @@ public class TFileTransport: TTransport { public func write(data: Data) throws { let bytesWritten = data.withUnsafeBytes { - fwrite($0, 1, data.count, self.fileHandle) + fwrite($0.baseAddress!, 1, data.count, self.fileHandle) } if bytesWritten != data.count { throw TTransportError(error: .unknown) diff --git a/lib/swift/Sources/TFramedTransport.swift b/lib/swift/Sources/TFramedTransport.swift index ca385d6d3ce..9f75b5efc8a 100644 --- a/lib/swift/Sources/TFramedTransport.swift +++ b/lib/swift/Sources/TFramedTransport.swift @@ -65,7 +65,9 @@ public class TFramedTransport: TTransport { throw TTransportError(error: .sizeLimit(limit: maxSize, got: toRead)) } - return try transport.readAll(size: toRead) + let data = try transport.readAll(size: toRead) + remainingBytes -= data.count + return data } public func flush() throws { @@ -73,10 +75,6 @@ public class TFramedTransport: TTransport { let buff = writeBuffer writeBuffer = Data() - if buff.count - TFramedTransport.headerSize < 0 { - throw TTransportError(error: .unknown) - } - let frameSize = encodeFrameSize(size: UInt32(buff.count)) try transport.write(data: frameSize) @@ -92,10 +90,10 @@ public class TFramedTransport: TTransport { private func encodeFrameSize(size: UInt32) -> Data { var data = Data() - data.append(Data(bytes: [UInt8(0xff & (size >> 24))])) - data.append(Data(bytes: [UInt8(0xff & (size >> 16))])) - data.append(Data(bytes: [UInt8(0xff & (size >> 8))])) - data.append(Data(bytes: [UInt8(0xff & (size))])) + data.append(Data([UInt8(0xff & (size >> 24))])) + data.append(Data([UInt8(0xff & (size >> 16))])) + data.append(Data([UInt8(0xff & (size >> 8))])) + data.append(Data([UInt8(0xff & (size))])) return data } diff --git a/lib/swift/Sources/THTTPSessionTransport.swift b/lib/swift/Sources/THTTPSessionTransport.swift index 3c0af8eb890..f66da922e81 100644 --- a/lib/swift/Sources/THTTPSessionTransport.swift +++ b/lib/swift/Sources/THTTPSessionTransport.swift @@ -18,8 +18,15 @@ */ import Foundation -import Dispatch +// Conditional import for URLRequest +// It was moved from Foundation to FoundationNetworking in 5.1, but +// not on Darwin. See https://stackoverflow.com/a/58606520 +#if canImport(FoundationNetworking) +import FoundationNetworking +#endif + +import Dispatch public class THTTPSessionTransport: TAsyncTransport { public class Factory : TAsyncTransportFactory { diff --git a/lib/swift/Sources/TList.swift b/lib/swift/Sources/TList.swift index c239d10c555..1508d90b8d7 100644 --- a/lib/swift/Sources/TList.swift +++ b/lib/swift/Sources/TList.swift @@ -17,7 +17,7 @@ * under the License. */ -public struct TList : RandomAccessCollection, MutableCollection, ExpressibleByArrayLiteral, TSerializable, Hashable { +public struct TList : RandomAccessCollection, MutableCollection, ExpressibleByArrayLiteral, TSerializable, Hashable { public typealias Storage = Array public typealias Indices = Storage.Indices @@ -31,13 +31,8 @@ public struct TList : RandomAccessCollection, MutableCo } /// Mark: Hashable - public var hashValue : Int { - let prime = 31 - var result = 1 - for element in storage { - result = prime &* result &+ element.hashValue - } - return result + public func hash(into hasher: inout Hasher) { + hasher.combine(storage) } /// Mark: TSerializable diff --git a/lib/swift/Sources/TMap.swift b/lib/swift/Sources/TMap.swift index 89803773949..8f520673b4c 100644 --- a/lib/swift/Sources/TMap.swift +++ b/lib/swift/Sources/TMap.swift @@ -17,11 +17,11 @@ * under the License. */ -public struct TMap: Collection, ExpressibleByDictionaryLiteral, Hashable, TSerializable { +public struct TMap: Collection, ExpressibleByDictionaryLiteral, Hashable, TSerializable { public typealias Storage = Dictionary public typealias Element = Storage.Element public typealias Index = Storage.Index - public typealias IndexDistance = Storage.IndexDistance + public typealias IndexDistance = Int public typealias Indices = Storage.Indices public typealias SubSequence = Storage.SubSequence internal var storage = Storage() @@ -33,11 +33,7 @@ public struct TMap: Colle } public mutating func updateValue(_ value: Value, forKey key: Key) -> Value? { - return updateValue(value, forKey: key) - } - - public mutating func removeAtIndex(_ index: DictionaryIndex) -> (Key, Value) { - return removeAtIndex(index) + return storage.updateValue(value, forKey: key) } public mutating func removeValueForKey(_ key: Key) -> Value? { @@ -115,15 +111,9 @@ public struct TMap: Colle } /// Mark: Hashable - - public var hashValue: Int { - let prime = 31 - var result = 1 - for (key, value) in storage { - result = prime &* result &+ key.hashValue - result = prime &* result &+ value.hashValue - } - return result + + public func hash(into hasher: inout Hasher) { + hasher.combine(storage) } /// Mark: TSerializable diff --git a/lib/swift/Sources/TMultiplexedProcessor.swift b/lib/swift/Sources/TMultiplexedProcessor.swift new file mode 100644 index 00000000000..7f4080816b2 --- /dev/null +++ b/lib/swift/Sources/TMultiplexedProcessor.swift @@ -0,0 +1,91 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +/** + `TMultiplexedProcessor` is a `TProcessor` allowing + a single `TServer` to provide multiple services. + + To do so, you instantiate the processor and then register additional + processors with it, as shown in the following example: + + let processor = MultiplexedProcessor() + + processor.register(CalculatorProcessor(service: CalculatorService()), for: "Calculator") + processor.register(WeatherProcessor(service: CalculatorService()), for: "Weather") + + let server = TPerfectServer(port: 9090, processor: processor, TCompactProtocol.self, TCompactProtocol.self) + try server.start() + + */ +public class MultiplexedProcessor: TProcessor { + + enum Error: Swift.Error { + case incompatibleMessageType(TMessageType) + case missingProcessor(String) + case missingDefaultProcessor + } + + private var processors = [String: TProcessor]() + private var defaultProcessor: TProcessor? + + public init(defaultProcessor: TProcessor? = nil) { + self.defaultProcessor = defaultProcessor + } + + public func register(defaultProcessor processor: TProcessor) { + defaultProcessor = processor + } + + public func register(processor: TProcessor, for service: String) { + processors[service] = processor + } + + public func process(on inProtocol: TProtocol, outProtocol: TProtocol) throws { + let message = try inProtocol.readMessageBegin() + guard message.1 == .call || message.1 == .oneway else { throw Error.incompatibleMessageType(message.1) } + if let separatorIndex = message.0.firstIndex(of: Character(.multiplexSeparator)) { + let serviceName = String(message.0.prefix(upTo: separatorIndex)) + let messageName = String(message.0.suffix(from: message.0.index(after: separatorIndex))) + guard let processor = processors[serviceName] else { throw Error.missingProcessor(serviceName)} + let storedMessage = StoredMessage(message: (messageName, message.1, message.2), proto: inProtocol) + try processor.process(on: storedMessage, outProtocol: outProtocol) + } else { + guard let processor = defaultProcessor else { throw Error.missingDefaultProcessor } + try processor.process(on: inProtocol, outProtocol: outProtocol) + } + } +} + +private final class StoredMessage: TProtocolDecorator { + + private let message: (String, TMessageType, Int32) + + init(message: (String, TMessageType, Int32), proto: TProtocol) { + self.message = message + super.init(proto: proto) + } + + required init(on transport: TTransport) { + fatalError("init(on:) has not been implemented") + } + + override func readMessageBegin() throws -> (String, TMessageType, Int32) { + return message + } +} diff --git a/lib/swift/Sources/TMultiplexedProtocol.swift b/lib/swift/Sources/TMultiplexedProtocol.swift index 73a8d51ab9e..83f0ddd13e7 100644 --- a/lib/swift/Sources/TMultiplexedProtocol.swift +++ b/lib/swift/Sources/TMultiplexedProtocol.swift @@ -17,8 +17,19 @@ * under the License. */ +extension String { + static let multiplexSeparator = ":" +} + +/** + `TMultiplexedProtocol` is a protocol-independent concrete decorator + that allows a Thrift client to communicate with a multiplexing Thrift server, + by prepending the service name to the function name during function calls. + + - Note: THIS IS NOT USED BY SERVERS. On the server, use `TMultiplexedProcessor` to handle request + from a multiplexing client. + */ public class TMultiplexedProtocol: TWrappedProtocol { - public let separator = ":" public var serviceName = "" @@ -33,7 +44,7 @@ public class TMultiplexedProtocol: TWrappedProtocol = (Int, TProtocol, TProtocol, T) -> Void - public protocol TProcessor { - associatedtype Service - var service: Service { get set } func process(on inProtocol: TProtocol, outProtocol: TProtocol) throws - init(service: Service) } - diff --git a/lib/swift/Sources/TProtocol.swift b/lib/swift/Sources/TProtocol.swift index a4e4a20fa91..b4e5dbe73b3 100644 --- a/lib/swift/Sources/TProtocol.swift +++ b/lib/swift/Sources/TProtocol.swift @@ -18,7 +18,6 @@ */ import Foundation -// public enum TMessageType: Int32 { case call = 1 @@ -99,19 +98,19 @@ public protocol TProtocol { } public extension TProtocol { - public func writeFieldValue(_ value: TSerializable, name: String, type: TType, id: Int32) throws { + func writeFieldValue(_ value: TSerializable, name: String, type: TType, id: Int32) throws { try writeFieldBegin(name: name, type: type, fieldID: id) try value.write(to: self) try writeFieldEnd() } - public func validateValue(_ value: Any?, named name: String) throws { + func validateValue(_ value: Any?, named name: String) throws { if value == nil { throw TProtocolError(error: .unknown, message: "Missing required value for field: \(name)") } } - public func readResultMessageBegin() throws { + func readResultMessageBegin() throws { let (_, type, _) = try readMessageBegin(); if type == .exception { let x = try readException() @@ -120,17 +119,17 @@ public extension TProtocol { return } - public func readException() throws -> TApplicationError { + func readException() throws -> TApplicationError { return try TApplicationError.read(from: self) } - public func writeException(messageName name: String, sequenceID: Int32, ex: TApplicationError) throws { + func writeException(messageName name: String, sequenceID: Int32, ex: TApplicationError) throws { try writeMessageBegin(name: name, type: .exception, sequenceID: sequenceID) try ex.write(to: self) try writeMessageEnd() } - public func skip(type: TType) throws { + func skip(type: TType) throws { switch type { case .bool: _ = try read() as Bool case .i8: _ = try read() as UInt8 @@ -175,8 +174,9 @@ public extension TProtocol { try skip(type: elemType) } try readListEnd() + default: - return + throw TProtocolError(error: .invalidData, message: "Invalid data") } } } diff --git a/lib/swift/Sources/TProtocolDecorator.swift b/lib/swift/Sources/TProtocolDecorator.swift new file mode 100644 index 00000000000..b1b1480db03 --- /dev/null +++ b/lib/swift/Sources/TProtocolDecorator.swift @@ -0,0 +1,199 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import Foundation + +class TProtocolDecorator: TProtocol { + + private let proto: TProtocol + var transport: TTransport + + init(proto: TProtocol) { + self.proto = proto + self.transport = proto.transport + } + + required init(on transport: TTransport) { + fatalError("init(on:) has not been implemented") + } + + func readMessageBegin() throws -> (String, TMessageType, Int32) { + return try proto.readMessageBegin() + } + + func readMessageEnd() throws { + try proto.readMessageEnd() + } + + func readStructBegin() throws -> String { + return try proto.readStructBegin() + } + + func readStructEnd() throws { + try proto.readStructEnd() + } + + func readFieldBegin() throws -> (String, TType, Int32) { + return try proto.readFieldBegin() + } + + func readFieldEnd() throws { + try proto.readFieldEnd() + } + + func readMapBegin() throws -> (TType, TType, Int32) { + return try proto.readMapBegin() + } + + func readMapEnd() throws { + try proto.readMapEnd() + } + + func readSetBegin() throws -> (TType, Int32) { + return try proto.readSetBegin() + } + + func readSetEnd() throws { + try proto.readSetEnd() + } + + func readListBegin() throws -> (TType, Int32) { + return try proto.readListBegin() + } + + func readListEnd() throws { + try proto.readListEnd() + } + + func read() throws -> String { + return try proto.read() + } + + func read() throws -> Bool { + return try proto.read() + } + + func read() throws -> UInt8 { + return try proto.read() + } + + func read() throws -> Int16 { + return try proto.read() + } + + func read() throws -> Int32 { + return try proto.read() + } + + func read() throws -> Int64 { + return try proto.read() + } + + func read() throws -> Double { + return try proto.read() + } + + func read() throws -> Data { + return try proto.read() + } + + func writeMessageBegin(name: String, type messageType: TMessageType, sequenceID: Int32) throws { + try proto.writeMessageBegin(name: name, type: messageType, sequenceID: sequenceID) + } + + func writeMessageEnd() throws { + try proto.writeMessageEnd() + } + + func writeStructBegin(name: String) throws { + try proto.writeStructBegin(name: name) + } + + func writeStructEnd() throws { + try proto.writeStructEnd() + } + + func writeFieldBegin(name: String, type fieldType: TType, fieldID: Int32) throws { + try proto.writeFieldBegin(name: name, type: fieldType, fieldID: fieldID) + } + + func writeFieldStop() throws { + try proto.writeFieldStop() + } + + func writeFieldEnd() throws { + try proto.writeFieldEnd() + } + + func writeMapBegin(keyType: TType, valueType: TType, size: Int32) throws { + try proto.writeMapBegin(keyType: keyType, valueType: valueType, size: size) + } + + func writeMapEnd() throws { + try proto.writeMapEnd() + } + + func writeSetBegin(elementType: TType, size: Int32) throws { + try proto.writeSetBegin(elementType: elementType, size: size) + } + + func writeSetEnd() throws { + try proto.writeSetEnd() + } + + func writeListBegin(elementType: TType, size: Int32) throws { + try proto.writeListBegin(elementType: elementType, size: size) + } + + func writeListEnd() throws { + try proto.writeListEnd() + } + + func write(_ value: String) throws { + try proto.write(value) + } + + func write(_ value: Bool) throws { + try proto.write(value) + } + + func write(_ value: UInt8) throws { + try proto.write(value) + } + + func write(_ value: Int16) throws { + try proto.write(value) + } + + func write(_ value: Int32) throws { + try proto.write(value) + } + + func write(_ value: Int64) throws { + try proto.write(value) + } + + func write(_ value: Double) throws { + try proto.write(value) + } + + func write(_ value: Data) throws { + try proto.write(value) + } +} diff --git a/lib/swift/Sources/TSSLSocketTransport.swift b/lib/swift/Sources/TSSLSocketTransport.swift index c2b590266a8..d350f821ee7 100644 --- a/lib/swift/Sources/TSSLSocketTransport.swift +++ b/lib/swift/Sources/TSSLSocketTransport.swift @@ -20,6 +20,13 @@ import Foundation import CoreFoundation +#if !swift(>=4.2) +// Swift 3/4 compatibility +fileprivate extension RunLoopMode { + static let `default` = defaultRunLoopMode +} +#endif + #if os(Linux) public class TSSLSocketTransport { init(hostname: String, port: UInt16) { @@ -98,18 +105,18 @@ public class TSSLSocketTransport: TStreamTransport { CFReadStreamSetProperty(readStream?.takeRetainedValue(), .SSLSettings, - settings as CFTypeRef!) + settings as CFTypeRef) CFWriteStreamSetProperty(writeStream?.takeRetainedValue(), .SSLSettings, - settings as CFTypeRef!) + settings as CFTypeRef) inputStream = readStream!.takeRetainedValue() - inputStream?.schedule(in: .current, forMode: .defaultRunLoopMode) + inputStream?.schedule(in: .current, forMode: .default) inputStream?.open() outputStream = writeStream!.takeRetainedValue() - outputStream?.schedule(in: .current, forMode: .defaultRunLoopMode) + outputStream?.schedule(in: .current, forMode: .default) outputStream?.open() readStream?.release() diff --git a/lib/swift/Sources/TSerializable.swift b/lib/swift/Sources/TSerializable.swift index b45096b69e8..1374700ca81 100644 --- a/lib/swift/Sources/TSerializable.swift +++ b/lib/swift/Sources/TSerializable.swift @@ -21,7 +21,6 @@ import Foundation public protocol TSerializable { - var hashValue: Int { get } /// TType for instance static var thriftType: TType { get } @@ -43,10 +42,6 @@ extension TSerializable { public var thriftType: TType { return Self.thriftType } } -public func ==(lhs: T, rhs: T) -> Bool where T : TSerializable { - return lhs.hashValue == rhs.hashValue -} - /// Default read/write for primitave Thrift types: /// Bool, Int8 (byte), Int16, Int32, Int64, Double, String diff --git a/lib/swift/Sources/TSet.swift b/lib/swift/Sources/TSet.swift index 1ecd170a571..9d89d14a92e 100644 --- a/lib/swift/Sources/TSet.swift +++ b/lib/swift/Sources/TSet.swift @@ -30,9 +30,10 @@ public struct TSet : SetAlgebra, Hashable, C /// Mark: Collection + public typealias Element = Storage.Element public typealias Indices = Storage.Indices public typealias Index = Storage.Index - public typealias IndexDistance = Storage.IndexDistance + public typealias IndexDistance = Int public typealias SubSequence = Storage.SubSequence @@ -126,13 +127,8 @@ public struct TSet : SetAlgebra, Hashable, C /// Mark: Hashable - public var hashValue : Int { - let prime = 31 - var result = 1 - for element in storage { - result = prime &* result &+ element.hashValue - } - return result + public func hash(into hasher: inout Hasher) { + hasher.combine(storage) } /// Mark: TSerializable diff --git a/lib/swift/Sources/TSocketServer.swift b/lib/swift/Sources/TSocketServer.swift index 0224e67b5e9..d70020ec31b 100644 --- a/lib/swift/Sources/TSocketServer.swift +++ b/lib/swift/Sources/TSocketServer.swift @@ -31,20 +31,18 @@ public let TSocketServerClientConnectionFinished = "TSocketServerClientConnectio public let TSocketServerProcessorKey = "TSocketServerProcessor" public let TSocketServerTransportKey = "TSocketServerTransport" -class TSocketServer where Processor.Service == Service { +open class TSocketServer { var socketFileHandle: FileHandle var processingQueue = DispatchQueue(label: "TSocketServer.processing", qos: .background, attributes: .concurrent) - var serviceHandler: Service + let processor: Processor public init(port: Int, - service: Service, inProtocol: InProtocol.Type, outProtocol: OutProtocol.Type, - processor: Processor.Type) throws { - // set service handler - self.serviceHandler = service + processor: Processor) throws { + self.processor = processor // create a socket var fd: Int32 = -1 @@ -54,21 +52,21 @@ class TSocketServer.size)) - + let inPort = in_port_t(UInt16(truncatingIfNeeded: port).bigEndian) #if os(Linux) var addr = sockaddr_in(sin_family: sa_family_t(AF_INET), - sin_port: in_port_t(port.bigEndian), + sin_port: inPort, sin_addr: in_addr(s_addr: in_addr_t(0)), sin_zero: (0, 0, 0, 0, 0, 0, 0, 0)) #else var addr = sockaddr_in(sin_len: UInt8(MemoryLayout.size), sin_family: sa_family_t(AF_INET), - sin_port: in_port_t(port.bigEndian), + sin_port: inPort, sin_addr: in_addr(s_addr: in_addr_t(0)), sin_zero: (0, 0, 0, 0, 0, 0, 0, 0)) #endif @@ -80,7 +78,7 @@ class TSocketServer.size) let cfaddr = address.withUnsafeBytes { - CFDataCreateWithBytesNoCopy(kCFAllocatorDefault, $0, address.count, nil) + CFDataCreateWithBytesNoCopy(kCFAllocatorDefault, $0.bindMemory(to: UInt8.self).baseAddress!, address.count, kCFAllocatorNull) } if CFSocketSetAddress(sock, cfaddr) != CFSocketError.success { //kCFSocketSuccess { CFSocketInvalidate(sock) @@ -104,8 +102,8 @@ class TSocketServer TTransport { + return TFileHandleTransport(fileHandle: fileHandle) + } + func handleClientConnection(_ clientSocket: FileHandle) { + let transport = createTransport(fileHandle: clientSocket) let inProtocol = InProtocol(on: transport) let outProtocol = OutProtocol(on: transport) do { - try processor.process(on: inProtocol, outProtocol: outProtocol) + while true { + try processor.process(on: inProtocol, outProtocol: outProtocol) + } } catch let error { print("Error processign request: \(error)") } @@ -142,8 +146,14 @@ class TSocketServer: TSocketServer { + open override func createTransport(fileHandle: FileHandle) -> TTransport { + return TFramedTransport(transport: super.createTransport(fileHandle: fileHandle)) + } +} diff --git a/lib/swift/Sources/TSocketTransport.swift b/lib/swift/Sources/TSocketTransport.swift index 0316e37d7e4..640612b7349 100644 --- a/lib/swift/Sources/TSocketTransport.swift +++ b/lib/swift/Sources/TSocketTransport.swift @@ -29,6 +29,13 @@ import Foundation import CoreFoundation +#if !swift(>=4.2) +// Swift 3/4 compatibility +fileprivate extension RunLoopMode { + static let `default` = defaultRunLoopMode +} +#endif + private struct Sys { #if os(Linux) static let read = Glibc.read @@ -72,7 +79,7 @@ public class TCFSocketTransport: TStreamTransport { var readStream: Unmanaged? var writeStream: Unmanaged? CFStreamCreatePairWithSocketToHost(kCFAllocatorDefault, - hostname as CFString!, + hostname as CFString, UInt32(port), &readStream, &writeStream) @@ -88,11 +95,11 @@ public class TCFSocketTransport: TStreamTransport { } inputStream = readStream as InputStream - inputStream.schedule(in: .current, forMode: .defaultRunLoopMode) + inputStream.schedule(in: .current, forMode: .default) inputStream.open() outputStream = writeStream as OutputStream - outputStream.schedule(in: .current, forMode: .defaultRunLoopMode) + outputStream.schedule(in: .current, forMode: .default) outputStream.open() } else { @@ -140,7 +147,6 @@ public class TSocketTransport : TTransport { } - #if os(Linux) let sock = socket(AF_INET, Int32(SOCK_STREAM.rawValue), 0) var addr = sockaddr_in(sin_family: sa_family_t(AF_INET), @@ -153,7 +159,7 @@ public class TSocketTransport : TTransport { var addr = sockaddr_in(sin_len: UInt8(MemoryLayout.size), sin_family: sa_family_t(AF_INET), sin_port: in_port_t(htons(UInt16(port))), - sin_addr: in_addr(s_addr: in_addr_t(0)), + sin_addr: hostAddr, sin_zero: (0, 0, 0, 0, 0, 0, 0, 0)) #endif @@ -184,7 +190,7 @@ public class TSocketTransport : TTransport { var buff = Array.init(repeating: 0, count: size) let readBytes = Sys.read(socketDescriptor, &buff, size) - return Data(bytes: buff[0.. 0 { let written = writeBuffer.withUnsafeBytes { - Sys.write(socketDescriptor, $0, writeBuffer.count) + Sys.write(socketDescriptor, $0.baseAddress!, writeBuffer.count) } writeBuffer = writeBuffer.subdata(in: written ..< writeBuffer.count) bytesToWrite -= written diff --git a/lib/swift/Sources/TStreamTransport.swift b/lib/swift/Sources/TStreamTransport.swift index 26bc1b8061d..5dd7dbac973 100644 --- a/lib/swift/Sources/TStreamTransport.swift +++ b/lib/swift/Sources/TStreamTransport.swift @@ -20,6 +20,13 @@ import Foundation import CoreFoundation +#if !swift(>=4.2) +// Swift 3/4 compatibility +fileprivate extension RunLoopMode { + static let `default` = defaultRunLoopMode +} +#endif + #if os(Linux) /// Currently unavailable in Linux /// Remove comments and build to fix @@ -64,7 +71,7 @@ import CoreFoundation if bytesRead <= 0 { throw TTransportError(error: .notOpen) } - read.append(Data(bytes: buffer)) + read.append(Data(buffer)) } return read } @@ -85,7 +92,7 @@ import CoreFoundation break } - read.append(Data(bytes: buffer)) + read.append(Data(buffer)) } return read } @@ -97,10 +104,7 @@ import CoreFoundation var bytesWritten = 0 while bytesWritten < data.count { - bytesWritten = data.withUnsafeBytes { - return output.write($0, maxLength: data.count) - } - + bytesWritten = data.withUnsafeBytes { output.write($0.bindMemory(to: UInt8.self).baseAddress!, maxLength: data.count) } if bytesWritten == -1 { throw TTransportError(error: .notOpen) } else if bytesWritten == 0 { @@ -124,7 +128,7 @@ import CoreFoundation input?.delegate = nil input?.close() - input?.remove(from: .current, forMode: .defaultRunLoopMode) + input?.remove(from: .current, forMode: .default) input = nil } @@ -135,7 +139,7 @@ import CoreFoundation } output?.delegate = nil output?.close() - output?.remove(from: .current, forMode: .defaultRunLoopMode) + output?.remove(from: .current, forMode: .default) output = nil } } diff --git a/lib/swift/Sources/TStruct.swift b/lib/swift/Sources/TStruct.swift index f172a32d125..d0a1a4bcd21 100644 --- a/lib/swift/Sources/TStruct.swift +++ b/lib/swift/Sources/TStruct.swift @@ -29,10 +29,10 @@ public protocol TStruct : TSerializable { } public extension TStruct { - public static var fieldIds: [String: (id: Int32, type: TType)] { return [:] } - public static var thriftType: TType { return .struct } + static var fieldIds: [String: (id: Int32, type: TType)] { return [:] } + static var thriftType: TType { return .struct } - public func write(to proto: TProtocol) throws { + func write(to proto: TProtocol) throws { // Write struct name first try proto.writeStructBegin(name: Self.structName) @@ -45,15 +45,6 @@ public extension TStruct { try proto.writeStructEnd() } - public var hashValue: Int { - let prime = 31 - var result = 1 - self.forEach { _, value, _ in - result = prime &* result &+ (value.hashValue) - } - return result - } - /// Provides a block for handling each (available) thrift property using reflection /// Caveat: Skips over optional values @@ -71,7 +62,7 @@ public extension TStruct { for (propName, propValue) in mirror.children { guard let propName = propName else { continue } - if let tval = unwrap(any: propValue) as? TSerializable, let id = Self.fieldIds[propName] { + if let tval = unwrap(any: propValue, parent: mirror) as? TSerializable, let id = Self.fieldIds[propName] { try block(propName, tval, id) } } @@ -87,10 +78,10 @@ public extension TStruct { /// - parameter any: Any instance to attempt to unwrap /// /// - returns: Unwrapped Any as Optional - private func unwrap(any: Any) -> Any? { + private func unwrap(any: Any, parent: Mirror) -> Any? { let mi = Mirror(reflecting: any) - if mi.displayStyle != .optional { return any } + if parent.displayStyle != .enum && mi.displayStyle != .optional { return any } if mi.children.count == 0 { return nil } let (_, some) = mi.children.first! diff --git a/lib/swift/Sources/Thrift.swift b/lib/swift/Sources/Thrift.swift index 5bd1758090f..789aae9e7dd 100644 --- a/lib/swift/Sources/Thrift.swift +++ b/lib/swift/Sources/Thrift.swift @@ -1,3 +1,3 @@ class Thrift { - let version = "1.1.0" + let version = "0.14.0" } diff --git a/lib/swift/Tests/ThriftTests/TBinaryProtocolTests.swift b/lib/swift/Tests/ThriftTests/TBinaryProtocolTests.swift index 56a557261ab..a50db4e54b7 100644 --- a/lib/swift/Tests/ThriftTests/TBinaryProtocolTests.swift +++ b/lib/swift/Tests/ThriftTests/TBinaryProtocolTests.swift @@ -116,7 +116,6 @@ class TBinaryProtocolTests: XCTestCase { do { try writeVal.write(to: proto) try? transport.flush() - } catch let error { XCTAssertFalse(true, "Caught Error attempting to write \(error)") } @@ -129,6 +128,7 @@ class TBinaryProtocolTests: XCTestCase { XCTAssertFalse(true, "Caught Error attempting to read \(error)") } } + func testUnsafeBitcastUpdate() { let value: Double = 3.14159 let val: Int64 = 31415926 @@ -162,7 +162,8 @@ class TBinaryProtocolTests: XCTestCase { ("testBoolWriteRead", testBoolWriteRead), ("testStringWriteRead", testStringWriteRead), ("testDataWriteRead", testDataWriteRead), - ("testStructWriteRead", testStructWriteRead) + ("testStructWriteRead", testStructWriteRead), + ("testUnsafeBitcastUpdate", testUnsafeBitcastUpdate) ] } } diff --git a/lib/swift/Tests/ThriftTests/TFramedTransportTests.swift b/lib/swift/Tests/ThriftTests/TFramedTransportTests.swift new file mode 100644 index 00000000000..d830cee9378 --- /dev/null +++ b/lib/swift/Tests/ThriftTests/TFramedTransportTests.swift @@ -0,0 +1,179 @@ +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +import XCTest +import Foundation +@testable import Thrift + +/// Testig TFramedTransport +/// +class TFramedTransportTests: XCTestCase { + var underlyingTransport: TMemoryBufferTransport = TMemoryBufferTransport(flushHandler: { + $0.reset(readBuffer: $1) + }) + + var proto: TBinaryProtocol! + var transport: TFramedTransport! + + override func setUp() { + super.setUp() + transport = TFramedTransport(transport:underlyingTransport) + proto = TBinaryProtocol(on: transport) + underlyingTransport.reset() + } + + override func tearDown() { + super.tearDown() + underlyingTransport.reset() + } + func testInt8WriteRead() { + let writeVal: UInt8 = 250 + try? proto.write(writeVal) + try? transport.flush() + + let readVal: UInt8 = (try? proto.read()) ?? 0 + XCTAssertEqual(writeVal, readVal, "Error with UInt8, wrote \(writeVal) but read \(readVal)") + } + + func testInt16WriteRead() { + + let writeVal: Int16 = 12312 + try? proto.write(writeVal) + try? transport.flush() + + let readVal: Int16 = (try? proto.read()) ?? 0 + XCTAssertEqual(writeVal, readVal, "Error with Int16, wrote \(writeVal) but read \(readVal)") + } + + func testInt32WriteRead() { + let writeVal: Int32 = 2029234 + try? proto.write(writeVal) + try? transport.flush() + + let readVal: Int32 = (try? proto.read()) ?? 0 + XCTAssertEqual(writeVal, readVal, "Error with Int32, wrote \(writeVal) but read \(readVal)") + } + + func testInt64WriteRead() { + let writeVal: Int64 = 234234981374134 + try? proto.write(writeVal) + try? transport.flush() + + let readVal: Int64 = (try? proto.read()) ?? 0 + XCTAssertEqual(writeVal, readVal, "Error with Int64, wrote \(writeVal) but read \(readVal)") + } + + func testDoubleWriteRead() { + let writeVal: Double = 3.1415926 + try? proto.write(writeVal) + try? transport.flush() + + let readVal: Double = (try? proto.read()) ?? 0.0 + XCTAssertEqual(writeVal, readVal, "Error with Double, wrote \(writeVal) but read \(readVal)") + } + + func testBoolWriteRead() { + let writeVal: Bool = true + try? proto.write(writeVal) + try? transport.flush() + + let readVal: Bool = (try? proto.read()) ?? false + XCTAssertEqual(writeVal, readVal, "Error with Bool, wrote \(writeVal) but read \(readVal)") + } + + func testStringWriteRead() { + let writeVal: String = "Hello World" + try? proto.write(writeVal) + try? transport.flush() + + let readVal: String! + do { + readVal = try proto.read() + } catch let error { + XCTAssertFalse(true, "Error reading \(error)") + return + } + + XCTAssertEqual(writeVal, readVal, "Error with String, wrote \(writeVal) but read \(readVal)") + } + + func testDataWriteRead() { + let writeVal: Data = "Data World".data(using: .utf8)! + try? proto.write(writeVal) + try? transport.flush() + + let readVal: Data = (try? proto.read()) ?? "Goodbye World".data(using: .utf8)! + XCTAssertEqual(writeVal, readVal, "Error with Data, wrote \(writeVal) but read \(readVal)") + } + + func testStructWriteRead() { + let msg = "Test Protocol Error" + let writeVal = TApplicationError(error: .protocolError, message: msg) + do { + try writeVal.write(to: proto) + try? transport.flush() + } catch let error { + XCTAssertFalse(true, "Caught Error attempting to write \(error)") + } + + do { + let readVal = try TApplicationError.read(from: proto) + XCTAssertEqual(readVal.error.thriftErrorCode, writeVal.error.thriftErrorCode, "Error case mismatch, expected \(readVal.error) got \(writeVal.error)") + XCTAssertEqual(readVal.message, writeVal.message, "Error message mismatch, expected \(readVal.message) got \(writeVal.message)") + } catch let error { + XCTAssertFalse(true, "Caught Error attempting to read \(error)") + } + } + + func testUnsafeBitcastUpdate() { + let value: Double = 3.14159 + let val: Int64 = 31415926 + let uval: UInt64 = 31415926 + + let i64 = Int64(bitPattern: value.bitPattern) + let ubc = unsafeBitCast(value, to: Int64.self) + + XCTAssertEqual(i64, ubc, "Bitcast Double-> i64 Values don't match") + + let dbl = Double(bitPattern: UInt64(val)) + let ubdb = unsafeBitCast(val, to: Double.self) + + XCTAssertEqual(dbl, ubdb, "Bitcast i64 -> Double Values don't match") + + let db2 = Double(bitPattern: uval) + let usbc2 = unsafeBitCast(uval, to: Double.self) + + XCTAssertEqual(db2, usbc2, "Bitcast u64 -> Double Values don't match") + } + + static var allTests : [(String, (TFramedTransportTests) -> () throws -> Void)] { + return [ + ("testInt8WriteRead", testInt8WriteRead), + ("testInt16WriteRead", testInt16WriteRead), + ("testInt32WriteRead", testInt32WriteRead), + ("testInt64WriteRead", testInt64WriteRead), + ("testDoubleWriteRead", testDoubleWriteRead), + ("testBoolWriteRead", testBoolWriteRead), + ("testStringWriteRead", testStringWriteRead), + ("testDataWriteRead", testDataWriteRead), + ("testStructWriteRead", testStructWriteRead), + ("testUnsafeBitcastUpdate", testUnsafeBitcastUpdate) + ] + } +} diff --git a/lib/swift/Tests/ThriftTests/TMultiplexedProcessorTests.swift b/lib/swift/Tests/ThriftTests/TMultiplexedProcessorTests.swift new file mode 100644 index 00000000000..a68c081eb4d --- /dev/null +++ b/lib/swift/Tests/ThriftTests/TMultiplexedProcessorTests.swift @@ -0,0 +1,154 @@ +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +import XCTest +import Foundation +@testable import Thrift + +private protocol CalculatorService { } + +private class Calculator: CalculatorService { } + +private class CalculatorProcessor: TProcessor { + private let service: CalculatorService + + init(service: CalculatorService) { + self.service = service + } + + var processCalled = false + func process(on inProtocol: TProtocol, outProtocol: TProtocol) throws { + processCalled = true + } +} + +class TMultiplexedProcessorTests: XCTestCase { + + let sut = MultiplexedProcessor() + var transport: TMemoryBufferTransport = TMemoryBufferTransport { $0.reset(readBuffer: $1) } + lazy var proto = TMultiplexedProtocol(on: transport) + + override func setUp() { + super.setUp() + transport.reset() + } + + override func tearDown() { + super.tearDown() + transport.reset() + } + + func testExceptionMessageThrowsError() throws { + try proto.writeMessageBegin(name: "message", type: .exception, sequenceID: 1) + try transport.flush() + XCTAssertThrowsError(try sut.process(on: proto, outProtocol: proto)) { error in + guard case MultiplexedProcessor.Error.incompatibleMessageType(let type) = error else { + XCTFail() + return + } + XCTAssertEqual(type, .exception) + } + } + + func testReplyMessageThrowsError() throws { + try proto.writeMessageBegin(name: "message", type: .reply, sequenceID: 1) + try transport.flush() + XCTAssertThrowsError(try sut.process(on: proto, outProtocol: proto)) { error in + guard case MultiplexedProcessor.Error.incompatibleMessageType(let type) = error else { + XCTFail() + return + } + XCTAssertEqual(type, .reply) + } + } + + func testMissingDefaultProcessorThrowsError() throws { + try proto.writeMessageBegin(name: "message", type: .call, sequenceID: 1) + try transport.flush() + XCTAssertThrowsError(try sut.process(on: proto, outProtocol: proto)) { error in + guard case MultiplexedProcessor.Error.missingDefaultProcessor = error else { + XCTFail() + return + } + } + } + + func testUsesDefaultProcessorForNonMultiplexedMessage() throws { + let calculator = Calculator() + let calculatorProcessor = CalculatorProcessor(service: calculator) + sut.register(defaultProcessor: calculatorProcessor) + try proto.writeMessageBegin(name: "message", type: .call, sequenceID: 1) + try transport.flush() + try sut.process(on: proto, outProtocol: proto) + XCTAssertTrue(calculatorProcessor.processCalled) + } + + func testUsesProcessorForMultiplexedMessage() throws { + let calculator = Calculator() + let calculatorProcessor = CalculatorProcessor(service: calculator) + sut.register(processor: calculatorProcessor, for: "Calculator") + try proto.writeMessageBegin(name: "Calculator:message", type: .call, sequenceID: 1) + try transport.flush() + try sut.process(on: proto, outProtocol: proto) + XCTAssertTrue(calculatorProcessor.processCalled) + } + + func testMissingProcessorForMultiplexedMessageThrowsError() throws { + try proto.writeMessageBegin(name: "Calculator:message", type: .call, sequenceID: 1) + try transport.flush() + XCTAssertThrowsError(try sut.process(on: proto, outProtocol: proto)) { error in + guard case MultiplexedProcessor.Error.missingProcessor(let serviceName) = error else { + XCTFail() + return + } + XCTAssertEqual(serviceName, "Calculator") + } + } + + func testCallMessageDoesNotThrowError() throws { + let calculator = Calculator() + let calculatorProcessor = CalculatorProcessor(service: calculator) + sut.register(defaultProcessor: calculatorProcessor) + try proto.writeMessageBegin(name: "message", type: .call, sequenceID: 1) + try transport.flush() + try sut.process(on: proto, outProtocol: proto) + } + + func testOneWayMessageDoesNotThrowError() throws { + let calculator = Calculator() + let calculatorProcessor = CalculatorProcessor(service: calculator) + sut.register(defaultProcessor: calculatorProcessor) + try proto.writeMessageBegin(name: "message", type: .oneway, sequenceID: 1) + try transport.flush() + try sut.process(on: proto, outProtocol: proto) + } + + static var allTests : [(String, (TMultiplexedProcessorTests) -> () throws -> Void)] { + return [ + ("testExceptionMessageThrowsError", testExceptionMessageThrowsError), + ("testReplyMessageThrowsError", testReplyMessageThrowsError), + ("testMissingDefaultProcessorThrowsError", testMissingDefaultProcessorThrowsError), + ("testUsesDefaultProcessorForNonMultiplexedMessage", testUsesDefaultProcessorForNonMultiplexedMessage), + ("testUsesProcessorForMultiplexedMessage", testUsesProcessorForMultiplexedMessage), + ("testMissingProcessorForMultiplexedMessageThrowsError", testMissingProcessorForMultiplexedMessageThrowsError), + ("testCallMessageDoesNotThrowError", testCallMessageDoesNotThrowError), + ("testOneWayMessageDoesNotThrowError", testOneWayMessageDoesNotThrowError) + ] + } +} diff --git a/lib/swift/Tests/ThriftTests/TSocketServerTests.swift b/lib/swift/Tests/ThriftTests/TSocketServerTests.swift new file mode 100644 index 00000000000..a00ebbc4bcb --- /dev/null +++ b/lib/swift/Tests/ThriftTests/TSocketServerTests.swift @@ -0,0 +1,55 @@ +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +import XCTest +import Foundation +@testable import Thrift + +private protocol CalculatorService { } + +private class Calculator: CalculatorService { } + +private class CalculatorProcessor: TProcessor { + private let service: CalculatorService + + init(service: CalculatorService) { + self.service = service + } + + var processCalled = false + func process(on inProtocol: TProtocol, outProtocol: TProtocol) throws { + processCalled = true + } +} + +class TSocketServerTests: XCTestCase { + func testInit() throws { + let service: CalculatorService = Calculator() + let processor: CalculatorProcessor = CalculatorProcessor(service: service) + let _: TSocketServer = + try TSocketServer(port: 9090, inProtocol: TBinaryProtocol.self, outProtocol: TBinaryProtocol.self, processor: processor) + } + + static var allTests : [(String, (TSocketServerTests) -> () throws -> Void)] { + return [ + ("testInit", testInit), + ] + } + +} diff --git a/lib/swift/Tests/ThriftTests/ThriftTests.swift b/lib/swift/Tests/ThriftTests/ThriftTests.swift index 9316100180a..226862e2b08 100644 --- a/lib/swift/Tests/ThriftTests/ThriftTests.swift +++ b/lib/swift/Tests/ThriftTests/ThriftTests.swift @@ -3,11 +3,7 @@ import XCTest class ThriftTests: XCTestCase { func testVersion() { - XCTAssertEqual(Thrift().version, "1.1.0") - } - - func test_in_addr_extension() { - + XCTAssertEqual(Thrift().version, "0.14.0") } static var allTests : [(String, (ThriftTests) -> () throws -> Void)] { diff --git a/lib/ts/Gruntfile.js b/lib/ts/Gruntfile.js new file mode 100644 index 00000000000..fcd79f8e543 --- /dev/null +++ b/lib/ts/Gruntfile.js @@ -0,0 +1,163 @@ +//To build dist/thrift.js, dist/thrift.min.js and doc/* +//run grunt at the command line in this directory. +//Prerequisites: +// Node Setup - nodejs.org +// Grunt Setup - npm install //reads the ./package.json and installs project dependencies +// Run grunt - npx grunt // uses project-local installed version of grunt (from package.json) + +module.exports = function(grunt) { + 'use strict'; + + grunt.initConfig({ + pkg: grunt.file.readJSON('package.json'), + concat: { + options: { + separator: ';' + }, + dist: { + src: ['src/**/*.js'], + dest: 'dist/<%= pkg.name %>.js' + } + }, + shell: { + InstallThriftJS: { + command: 'mkdir -p test/build/ts/lib; cp ../js/src/thrift.js test/build/ts/thrift.js' + }, + InstallThriftNodeJSDep: { + command: 'cd ../..; npm install' + }, + InstallTestLibs: { + command: 'cd test; ant download_jslibs' + }, + ThriftGen: { + command: [ + 'mkdir -p test/gen-js', + '../../compiler/cpp/thrift -gen js:ts --out test/gen-js ../../test/ThriftTest.thrift', + 'mkdir -p test/gen-nodejs', + '../../compiler/cpp/thrift -gen js:node,ts --out test/gen-nodejs ../../test/ThriftTest.thrift', + ].join(' && ') + }, + ThriftBrowserifyNodeInt64: { + command: [ + './node_modules/browserify/bin/cmd.js ./node_modules/node-int64/Int64.js -s Int64 -o test/build/js/lib/Int64.js', + './node_modules/browserify/bin/cmd.js ../nodejs/lib/thrift/int64_util.js -s Int64Util -o test/build/js/lib/Int64Util.js', + './node_modules/browserify/bin/cmd.js ./node_modules/json-int64/index.js -s JSONInt64 -o test/build/js/lib/JSONInt64.js' + ].join(' && ') + }, + ThriftGenInt64: { + command: '../../compiler/cpp/thrift -gen js:ts -o test ../../test/Int64Test.thrift' + }, + ThriftTestServer: { + options: { + async: true, + execOptions: { + cwd: "./test", + env: {NODE_PATH: "../../nodejs/lib:../../../node_modules"} + } + }, + command: "node server_http.js", + }, + BuildTS: { + options: { + execOptions: { + cwd: "./test", + } + }, + command : "../node_modules/typescript/bin/tsc --listFiles --outDir build/ts" + }, + BrowserifyCompiledTS: { + command: [ + "./node_modules/browserify/bin/cmd.js test/build/ts/test.js -o test/build/ts/lib/test.js --standalone test", + "./node_modules/browserify/bin/cmd.js test/build/ts/test-int64.js -o test/build/ts/lib/test-int64.js --standalone testInt64", + ].join(" && ") + }, + InstallGeneratedCode: { + command: [ + "mkdir -p test/build/ts", + "cp -r test/gen-js test/build/ts" + ].join(" && ") + }, + }, + qunit: { + ThriftJS: { + options: { + urls: [ + 'http://localhost:8089/test.html' + ], + puppeteer: { + headless: true, + args: ['--no-sandbox'], + }, + } + }, + ThriftJS_Int64: { + options: { + urls: [ + 'http://localhost:8089/test-int64.html' + ], + puppeteer: { + headless: true, + args: ['--no-sandbox'], + ignoreHTTPSErrors: true, + }, + } + }, + }, + jshint: { + // The main Thrift library file. not es6 yet :( + lib: { + src: ['../js/src/**/*.js'], + }, + // The test files use es6 + test: { + src: ['Gruntfile.js', 'test/*.js'], + options: { + esversion: 6, + } + }, + gen_js_code: { + src: ['test/gen-js/*.js'], + }, + gen_node_code: { + src: ['test/gen-nodejs/*.js'], + options: { + node: true, + } + }, + }, + }); + + grunt.loadNpmTasks('grunt-contrib-jshint'); + grunt.loadNpmTasks('grunt-contrib-qunit'); + grunt.loadNpmTasks('grunt-shell-spawn'); + + grunt.registerTask('wait', 'Wait just one second for the server to start', function () { + var done = this.async(); + setTimeout(function() { + done(true); + }, 1000); + }); + + grunt.registerTask('installAndGenerate', [ + 'shell:InstallThriftJS', + 'shell:InstallThriftNodeJSDep', + 'shell:ThriftGen', + 'shell:ThriftBrowserifyNodeInt64', + 'shell:ThriftGenInt64', + 'shell:InstallTestLibs', + 'shell:BuildTS', + 'shell:InstallGeneratedCode', + 'shell:BrowserifyCompiledTS', + ]); + + grunt.registerTask('test', [ + 'installAndGenerate', + 'jshint', + 'shell:ThriftTestServer', + 'wait', + 'qunit:ThriftJS', + 'qunit:ThriftJS_Int64', + 'shell:ThriftTestServer:kill', + ]); + grunt.registerTask('default', ['test']); +}; diff --git a/compiler/cpp/test/plugin_stability_test.sh b/lib/ts/Makefile.am old mode 100755 new mode 100644 similarity index 53% rename from compiler/cpp/test/plugin_stability_test.sh rename to lib/ts/Makefile.am index eb7c93d6674..62ea2069f16 --- a/compiler/cpp/test/plugin_stability_test.sh +++ b/lib/ts/Makefile.am @@ -1,5 +1,3 @@ -#!/bin/sh - # # Licensed to the Apache Software Foundation (ASF) under one # or more contributor license agreements. See the NOTICE file @@ -19,14 +17,42 @@ # under the License. # -# this file is intended to be invoked by make. +# Make sure this doesn't fail if ant is not configured. +# We call install twice to work around npm issues # -# This file runs the compiler twice, using a plugin that just invokes -# /bin/cat, and compares the output. If GeneratorInput is -# nondeterminsitic, you'd expect the output to differ from run-to-run. -# So this tests that in fact, the output is stable from run-to-run. -set -e -mkdir -p gen-bincat -PATH=.:"$PATH" ../thrift -r -gen bincat ../../../test/Include.thrift > gen-bincat/1.ser -PATH=.:"$PATH" ../thrift -r -gen bincat ../../../test/Include.thrift > gen-bincat/2.ser -diff --binary gen-bincat/1.ser gen-bincat/2.ser +if HAVE_NPM + +prereq: + $(NPM) install || $(NPM) install + $(NPM) list + +check-local: prereq all + ./node_modules/.bin/grunt + +doc: prereq + ./node_modules/.bin/grunt jsdoc + +endif + +clean-local: + $(RM) -r dist + $(RM) -r doc + $(RM) -r node_modules + $(RM) -r test/build/ + $(RM) -r test/gen-*/ + +dist-hook: + $(RM) -r $(distdir)/dist/ + $(RM) -r $(distdir)/doc/ + $(RM) -r $(distdir)/node_modules/ + $(RM) -r $(distdir)/test/build/ + $(RM) -r $(distdir)/test/gen-*/ + +EXTRA_DIST = \ + coding_standards.md \ + Gruntfile.js \ + package.json \ + package-lock.json \ + thrift.d.ts \ + tsconfig.json + diff --git a/compiler/cpp/src/thrift/generate/thrift-t_php_generator.o-a60a38e9 b/lib/ts/dist/thrift.js similarity index 100% rename from compiler/cpp/src/thrift/generate/thrift-t_php_generator.o-a60a38e9 rename to lib/ts/dist/thrift.js diff --git a/lib/ts/dist/thrift.min.js b/lib/ts/dist/thrift.min.js new file mode 100644 index 00000000000..8e14f2d0716 --- /dev/null +++ b/lib/ts/dist/thrift.min.js @@ -0,0 +1 @@ +/*! thrift 07-01-2019 */ diff --git a/lib/ts/package-lock.json b/lib/ts/package-lock.json new file mode 100644 index 00000000000..49de90bf499 --- /dev/null +++ b/lib/ts/package-lock.json @@ -0,0 +1,4955 @@ +{ + "name": "thrift", + "version": "0.14.0", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "@types/node": { + "version": "10.12.18", + "resolved": "https://registry.npmjs.org/@types/node/-/node-10.12.18.tgz", + "integrity": "sha512-fh+pAqt4xRzPfqA6eh3Z2y6fyZavRIumvjhaCL753+TVkGKGhpPeyrJG2JftD0T9q4GF00KjefsQ+PQNDdWQaQ==", + "dev": true + }, + "@types/node-int64": { + "version": "0.4.29", + "resolved": "https://registry.npmjs.org/@types/node-int64/-/node-int64-0.4.29.tgz", + "integrity": "sha512-rHXvenLTj/CcsmNAebaBOhxQ2MqEGl3yXZZcZ21XYR+gzGTTcpOy2N4IxpvTCz48loyQNatHvfn6GhIbbZ1R3Q==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, + "@types/phantom": { + "version": "3.2.5", + "resolved": "https://registry.npmjs.org/@types/phantom/-/phantom-3.2.5.tgz", + "integrity": "sha512-7m36DoKSvZgBGWp0xiJ74eHnuotyrpDyQ6m+lers5iMvW4QX+RvBENn7PCjNix7OVqPWlBM+7AqzYVIQ7NrKrA==", + "dev": true + }, + "@types/qunit": { + "version": "2.5.4", + "resolved": "https://registry.npmjs.org/@types/qunit/-/qunit-2.5.4.tgz", + "integrity": "sha512-VHi2lEd4/zp8OOouf43JXGJJ5ZxHvdLL1dU0Yakp6Iy73SjpuXl7yjwAwmh1qhTv8krDgHteSwaySr++uXX9YQ==", + "dev": true + }, + "JSONStream": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/JSONStream/-/JSONStream-1.3.5.tgz", + "integrity": "sha512-E+iruNOY8VV9s4JEbe1aNEm6MiszPRr/UfcHMz0TQh1BXSxHK+ASV1R6W4HpjBhSeS+54PIsAMCBmwD06LLsqQ==", + "dev": true, + "requires": { + "jsonparse": "^1.2.0", + "through": ">=2.2.7 <3" + } + }, + "abbrev": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", + "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==" + }, + "acorn": { + "version": "6.0.4", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-6.0.4.tgz", + "integrity": "sha512-VY4i5EKSKkofY2I+6QLTbTTN/UvEQPCo6eiwzzSaSWfpaDhOmStMCMod6wmuPciNq+XS0faCglFu2lHZpdHUtg==", + "dev": true + }, + "acorn-dynamic-import": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/acorn-dynamic-import/-/acorn-dynamic-import-4.0.0.tgz", + "integrity": "sha512-d3OEjQV4ROpoflsnUA8HozoIR504TFxNivYEUi6uwz0IYhBkTDXGuWlNdMtybRt3nqVx/L6XqMt0FxkXuWKZhw==", + "dev": true + }, + "acorn-node": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/acorn-node/-/acorn-node-1.6.2.tgz", + "integrity": "sha512-rIhNEZuNI8ibQcL7ANm/mGyPukIaZsRNX9psFNQURyJW0nu6k8wjSDld20z6v2mDBWqX13pIEnk9gGZJHIlEXg==", + "dev": true, + "requires": { + "acorn": "^6.0.2", + "acorn-dynamic-import": "^4.0.0", + "acorn-walk": "^6.1.0", + "xtend": "^4.0.1" + } + }, + "acorn-walk": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-6.1.1.tgz", + "integrity": "sha512-OtUw6JUTgxA2QoqqmrmQ7F2NYqiBPi/L2jqHyFtllhOUvXYQXf0Z1CYUinIfyT4bTCGmrA7gX9FvHA81uzCoVw==", + "dev": true + }, + "agent-base": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-4.2.1.tgz", + "integrity": "sha512-JVwXMr9nHYTUXsBFKUqhJwvlcYU/blreOEUkhNR2eXZIvwd+c+o5V4MgDPKWnMS/56awN3TRzIP+KoPn+roQtg==", + "dev": true, + "requires": { + "es6-promisify": "^5.0.0" + } + }, + "ajv": { + "version": "6.7.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.7.0.tgz", + "integrity": "sha512-RZXPviBTtfmtka9n9sy1N5M5b82CbxWIR6HIis4s3WQTXDJamc/0gpCWNGz6EWdWp4DOfjzJfhz/AS9zVPjjWg==", + "dev": true, + "requires": { + "fast-deep-equal": "^2.0.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + }, + "align-text": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/align-text/-/align-text-0.1.4.tgz", + "integrity": "sha1-DNkKVhCT810KmSVsIrcGlDP60Rc=", + "dev": true, + "requires": { + "kind-of": "^3.0.2", + "longest": "^1.0.1", + "repeat-string": "^1.5.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", + "dev": true + }, + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "requires": { + "color-convert": "^1.9.0" + } + }, + "argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "requires": { + "sprintf-js": "~1.0.2" + }, + "dependencies": { + "sprintf-js": { + "version": "1.0.3", + "resolved": "http://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", + "dev": true + } + } + }, + "arr-diff": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", + "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=", + "dev": true + }, + "arr-flatten": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/arr-flatten/-/arr-flatten-1.1.0.tgz", + "integrity": "sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg==", + "dev": true + }, + "arr-union": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/arr-union/-/arr-union-3.1.0.tgz", + "integrity": "sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ=", + "dev": true + }, + "array-each": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/array-each/-/array-each-1.0.1.tgz", + "integrity": "sha1-p5SvDAWrF1KEbudTofIRoFugxE8=", + "dev": true + }, + "array-filter": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/array-filter/-/array-filter-0.0.1.tgz", + "integrity": "sha1-fajPLiZijtcygDWB/SH2fKzS7uw=", + "dev": true + }, + "array-find-index": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/array-find-index/-/array-find-index-1.0.2.tgz", + "integrity": "sha1-3wEKoSh+Fku9pvlyOwqWoexBh6E=", + "dev": true + }, + "array-map": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/array-map/-/array-map-0.0.0.tgz", + "integrity": "sha1-iKK6tz0c97zVwbEYoAP2b2ZfpmI=", + "dev": true + }, + "array-reduce": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/array-reduce/-/array-reduce-0.0.0.tgz", + "integrity": "sha1-FziZ0//Rx9k4PkR5Ul2+J4yrXys=", + "dev": true + }, + "array-slice": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/array-slice/-/array-slice-1.1.0.tgz", + "integrity": "sha512-B1qMD3RBP7O8o0H2KbrXDyB0IccejMF15+87Lvlor12ONPRHP6gTjXMNkt/d3ZuOGbAe66hFmaCfECI24Ufp6w==", + "dev": true + }, + "array-unique": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz", + "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=", + "dev": true + }, + "asn1": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz", + "integrity": "sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg==", + "dev": true, + "requires": { + "safer-buffer": "~2.1.0" + } + }, + "asn1.js": { + "version": "4.10.1", + "resolved": "https://registry.npmjs.org/asn1.js/-/asn1.js-4.10.1.tgz", + "integrity": "sha512-p32cOF5q0Zqs9uBiONKYLm6BClCoBCM5O9JfeUSlnQLBTxYdTK+pW+nXflm8UkKd2UYlEbYz5qEi0JuZR9ckSw==", + "dev": true, + "requires": { + "bn.js": "^4.0.0", + "inherits": "^2.0.1", + "minimalistic-assert": "^1.0.0" + } + }, + "assert": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/assert/-/assert-1.4.1.tgz", + "integrity": "sha1-mZEtWRg2tab1s0XA8H7vwI/GXZE=", + "dev": true, + "requires": { + "util": "0.10.3" + }, + "dependencies": { + "inherits": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz", + "integrity": "sha1-sX0I0ya0Qj5Wjv9xn5GwscvfafE=", + "dev": true + }, + "util": { + "version": "0.10.3", + "resolved": "http://registry.npmjs.org/util/-/util-0.10.3.tgz", + "integrity": "sha1-evsa/lCAUkZInj23/g7TeTNqwPk=", + "dev": true, + "requires": { + "inherits": "2.0.1" + } + } + } + }, + "assert-plus": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=", + "dev": true + }, + "assign-symbols": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assign-symbols/-/assign-symbols-1.0.0.tgz", + "integrity": "sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c=", + "dev": true + }, + "async": { + "version": "1.5.2", + "resolved": "http://registry.npmjs.org/async/-/async-1.5.2.tgz", + "integrity": "sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo=", + "dev": true + }, + "async-limiter": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.0.tgz", + "integrity": "sha512-jp/uFnooOiO+L211eZOoSyzpOITMXx1rBITauYykG3BRYPu8h0UcxsPNB04RR5vo4Tyz3+ay17tR6JVf9qzYWg==", + "dev": true + }, + "asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=", + "dev": true + }, + "atob": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz", + "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==", + "dev": true + }, + "aws-sign2": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", + "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=", + "dev": true + }, + "aws4": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.8.0.tgz", + "integrity": "sha512-ReZxvNHIOv88FlT7rxcXIIC0fPt4KZqZbOlivyWtXLt8ESx84zd3kMC6iK5jVeS2qt+g7ftS7ye4fi06X5rtRQ==", + "dev": true + }, + "babylon": { + "version": "7.0.0-beta.19", + "resolved": "https://registry.npmjs.org/babylon/-/babylon-7.0.0-beta.19.tgz", + "integrity": "sha512-Vg0C9s/REX6/WIXN37UKpv5ZhRi6A4pjHlpkE34+8/a6c2W1Q692n3hmc+SZG5lKRnaExLUbxtJ1SVT+KaCQ/A==" + }, + "balanced-match": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", + "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", + "dev": true + }, + "base": { + "version": "0.11.2", + "resolved": "https://registry.npmjs.org/base/-/base-0.11.2.tgz", + "integrity": "sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg==", + "dev": true, + "requires": { + "cache-base": "^1.0.1", + "class-utils": "^0.3.5", + "component-emitter": "^1.2.1", + "define-property": "^1.0.0", + "isobject": "^3.0.1", + "mixin-deep": "^1.2.0", + "pascalcase": "^0.1.1" + }, + "dependencies": { + "define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", + "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", + "dev": true, + "requires": { + "is-descriptor": "^1.0.0" + } + }, + "is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "dev": true, + "requires": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + } + } + } + }, + "base64-js": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.3.0.tgz", + "integrity": "sha512-ccav/yGvoa80BQDljCxsmmQ3Xvx60/UpBIij5QN21W3wBi/hhIC9OoO+KLpu9IJTS9j4DRVJ3aDDF9cMSoa2lw==", + "dev": true + }, + "bcrypt-pbkdf": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", + "integrity": "sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=", + "dev": true, + "requires": { + "tweetnacl": "^0.14.3" + } + }, + "bluebird": { + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.3.tgz", + "integrity": "sha512-/qKPUQlaW1OyR51WeCPBvRnAlnZFUJkCSG5HzGnuIqhgyJtF+T94lFnn33eiazjRm2LAHVy2guNnaq48X9SJuw==" + }, + "bn.js": { + "version": "4.11.8", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.8.tgz", + "integrity": "sha512-ItfYfPLkWHUjckQCk8xC+LwxgK8NYcXywGigJgSwOP8Y2iyWT4f2vsZnoOXTTbo+o5yXmIUJ4gn5538SO5S3gA==", + "dev": true + }, + "brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "braces": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", + "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", + "dev": true, + "requires": { + "arr-flatten": "^1.1.0", + "array-unique": "^0.3.2", + "extend-shallow": "^2.0.1", + "fill-range": "^4.0.0", + "isobject": "^3.0.1", + "repeat-element": "^1.1.2", + "snapdragon": "^0.8.1", + "snapdragon-node": "^2.0.1", + "split-string": "^3.0.2", + "to-regex": "^3.0.1" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "brorand": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz", + "integrity": "sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8=", + "dev": true + }, + "browser-pack": { + "version": "6.1.0", + "resolved": "http://registry.npmjs.org/browser-pack/-/browser-pack-6.1.0.tgz", + "integrity": "sha512-erYug8XoqzU3IfcU8fUgyHqyOXqIE4tUTTQ+7mqUjQlvnXkOO6OlT9c/ZoJVHYoAaqGxr09CN53G7XIsO4KtWA==", + "dev": true, + "requires": { + "JSONStream": "^1.0.3", + "combine-source-map": "~0.8.0", + "defined": "^1.0.0", + "safe-buffer": "^5.1.1", + "through2": "^2.0.0", + "umd": "^3.0.0" + } + }, + "browser-resolve": { + "version": "1.11.3", + "resolved": "https://registry.npmjs.org/browser-resolve/-/browser-resolve-1.11.3.tgz", + "integrity": "sha512-exDi1BYWB/6raKHmDTCicQfTkqwN5fioMFV4j8BsfMU4R2DK/QfZfK7kOVkmWCNANf0snkBzqGqAJBao9gZMdQ==", + "dev": true, + "requires": { + "resolve": "1.1.7" + }, + "dependencies": { + "resolve": { + "version": "1.1.7", + "resolved": "http://registry.npmjs.org/resolve/-/resolve-1.1.7.tgz", + "integrity": "sha1-IDEU2CrSxe2ejgQRs5ModeiJ6Xs=", + "dev": true + } + } + }, + "browserify": { + "version": "16.2.3", + "resolved": "https://registry.npmjs.org/browserify/-/browserify-16.2.3.tgz", + "integrity": "sha512-zQt/Gd1+W+IY+h/xX2NYMW4orQWhqSwyV+xsblycTtpOuB27h1fZhhNQuipJ4t79ohw4P4mMem0jp/ZkISQtjQ==", + "dev": true, + "requires": { + "JSONStream": "^1.0.3", + "assert": "^1.4.0", + "browser-pack": "^6.0.1", + "browser-resolve": "^1.11.0", + "browserify-zlib": "~0.2.0", + "buffer": "^5.0.2", + "cached-path-relative": "^1.0.0", + "concat-stream": "^1.6.0", + "console-browserify": "^1.1.0", + "constants-browserify": "~1.0.0", + "crypto-browserify": "^3.0.0", + "defined": "^1.0.0", + "deps-sort": "^2.0.0", + "domain-browser": "^1.2.0", + "duplexer2": "~0.1.2", + "events": "^2.0.0", + "glob": "^7.1.0", + "has": "^1.0.0", + "htmlescape": "^1.1.0", + "https-browserify": "^1.0.0", + "inherits": "~2.0.1", + "insert-module-globals": "^7.0.0", + "labeled-stream-splicer": "^2.0.0", + "mkdirp": "^0.5.0", + "module-deps": "^6.0.0", + "os-browserify": "~0.3.0", + "parents": "^1.0.1", + "path-browserify": "~0.0.0", + "process": "~0.11.0", + "punycode": "^1.3.2", + "querystring-es3": "~0.2.0", + "read-only-stream": "^2.0.0", + "readable-stream": "^2.0.2", + "resolve": "^1.1.4", + "shasum": "^1.0.0", + "shell-quote": "^1.6.1", + "stream-browserify": "^2.0.0", + "stream-http": "^2.0.0", + "string_decoder": "^1.1.1", + "subarg": "^1.0.0", + "syntax-error": "^1.1.1", + "through2": "^2.0.0", + "timers-browserify": "^1.0.1", + "tty-browserify": "0.0.1", + "url": "~0.11.0", + "util": "~0.10.1", + "vm-browserify": "^1.0.0", + "xtend": "^4.0.0" + } + }, + "browserify-aes": { + "version": "1.2.0", + "resolved": "http://registry.npmjs.org/browserify-aes/-/browserify-aes-1.2.0.tgz", + "integrity": "sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA==", + "dev": true, + "requires": { + "buffer-xor": "^1.0.3", + "cipher-base": "^1.0.0", + "create-hash": "^1.1.0", + "evp_bytestokey": "^1.0.3", + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "browserify-cipher": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/browserify-cipher/-/browserify-cipher-1.0.1.tgz", + "integrity": "sha512-sPhkz0ARKbf4rRQt2hTpAHqn47X3llLkUGn+xEJzLjwY8LRs2p0v7ljvI5EyoRO/mexrNunNECisZs+gw2zz1w==", + "dev": true, + "requires": { + "browserify-aes": "^1.0.4", + "browserify-des": "^1.0.0", + "evp_bytestokey": "^1.0.0" + } + }, + "browserify-des": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/browserify-des/-/browserify-des-1.0.2.tgz", + "integrity": "sha512-BioO1xf3hFwz4kc6iBhI3ieDFompMhrMlnDFC4/0/vd5MokpuAc3R+LYbwTA9A5Yc9pq9UYPqffKpW2ObuwX5A==", + "dev": true, + "requires": { + "cipher-base": "^1.0.1", + "des.js": "^1.0.0", + "inherits": "^2.0.1", + "safe-buffer": "^5.1.2" + } + }, + "browserify-rsa": { + "version": "4.0.1", + "resolved": "http://registry.npmjs.org/browserify-rsa/-/browserify-rsa-4.0.1.tgz", + "integrity": "sha1-IeCr+vbyApzy+vsTNWenAdQTVSQ=", + "dev": true, + "requires": { + "bn.js": "^4.1.0", + "randombytes": "^2.0.1" + } + }, + "browserify-sign": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/browserify-sign/-/browserify-sign-4.0.4.tgz", + "integrity": "sha1-qk62jl17ZYuqa/alfmMMvXqT0pg=", + "dev": true, + "requires": { + "bn.js": "^4.1.1", + "browserify-rsa": "^4.0.0", + "create-hash": "^1.1.0", + "create-hmac": "^1.1.2", + "elliptic": "^6.0.0", + "inherits": "^2.0.1", + "parse-asn1": "^5.0.0" + } + }, + "browserify-zlib": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/browserify-zlib/-/browserify-zlib-0.2.0.tgz", + "integrity": "sha512-Z942RysHXmJrhqk88FmKBVq/v5tqmSkDz7p54G/MGyjMnCFFnC79XWNbg+Vta8W6Wb2qtSZTSxIGkJrRpCFEiA==", + "dev": true, + "requires": { + "pako": "~1.0.5" + } + }, + "buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.2.1.tgz", + "integrity": "sha512-c+Ko0loDaFfuPWiL02ls9Xd3GO3cPVmUobQ6t3rXNUk304u6hGq+8N/kFi+QEIKhzK3uwolVhLzszmfLmMLnqg==", + "dev": true, + "requires": { + "base64-js": "^1.0.2", + "ieee754": "^1.1.4" + } + }, + "buffer-from": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz", + "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==", + "dev": true + }, + "buffer-shims": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/buffer-shims/-/buffer-shims-1.0.0.tgz", + "integrity": "sha1-mXjOMXOIxkmth5MCjDR37wRKi1E=", + "dev": true + }, + "buffer-xor": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/buffer-xor/-/buffer-xor-1.0.3.tgz", + "integrity": "sha1-JuYe0UIvtw3ULm42cp7VHYVf6Nk=", + "dev": true + }, + "bufferutil": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/bufferutil/-/bufferutil-4.0.1.tgz", + "integrity": "sha512-xowrxvpxojqkagPcWRQVXZl0YXhRhAtBEIq3VoER1NH5Mw1n1o0ojdspp+GS2J//2gCVyrzQDApQ4unGF+QOoA==", + "requires": { + "node-gyp-build": "~3.7.0" + } + }, + "builtin-modules": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-1.1.1.tgz", + "integrity": "sha1-Jw8HbFpywC9bZaR9+Uxf46J4iS8=", + "dev": true + }, + "builtin-status-codes": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/builtin-status-codes/-/builtin-status-codes-3.0.0.tgz", + "integrity": "sha1-hZgoeOIbmOHGZCXgPQF0eI9Wnug=", + "dev": true + }, + "cache-base": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/cache-base/-/cache-base-1.0.1.tgz", + "integrity": "sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ==", + "dev": true, + "requires": { + "collection-visit": "^1.0.0", + "component-emitter": "^1.2.1", + "get-value": "^2.0.6", + "has-value": "^1.0.0", + "isobject": "^3.0.1", + "set-value": "^2.0.0", + "to-object-path": "^0.3.0", + "union-value": "^1.0.0", + "unset-value": "^1.0.0" + } + }, + "cached-path-relative": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/cached-path-relative/-/cached-path-relative-1.0.2.tgz", + "integrity": "sha512-5r2GqsoEb4qMTTN9J+WzXfjov+hjxT+j3u5K+kIVNIwAd99DLCJE9pBIMP1qVeybV6JiijL385Oz0DcYxfbOIg==", + "dev": true + }, + "camelcase": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-2.1.1.tgz", + "integrity": "sha1-fB0W1nmhu+WcoCys7PsBHiAfWh8=", + "dev": true + }, + "camelcase-keys": { + "version": "2.1.0", + "resolved": "http://registry.npmjs.org/camelcase-keys/-/camelcase-keys-2.1.0.tgz", + "integrity": "sha1-MIvur/3ygRkFHvodkyITyRuPkuc=", + "dev": true, + "requires": { + "camelcase": "^2.0.0", + "map-obj": "^1.0.0" + } + }, + "caseless": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", + "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=", + "dev": true + }, + "catharsis": { + "version": "0.8.9", + "resolved": "https://registry.npmjs.org/catharsis/-/catharsis-0.8.9.tgz", + "integrity": "sha1-mMyJDKZS3S7w5ws3klMQ/56Q/Is=", + "requires": { + "underscore-contrib": "~0.3.0" + } + }, + "center-align": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/center-align/-/center-align-0.1.3.tgz", + "integrity": "sha1-qg0yYptu6XIgBBHL1EYckHvCt60=", + "dev": true, + "requires": { + "align-text": "^0.1.3", + "lazy-cache": "^1.0.3" + } + }, + "chalk": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.1.tgz", + "integrity": "sha512-ObN6h1v2fTJSmUXoS3nMQ92LbDK9be4TV+6G+omQlGJFdcUX5heKi1LZ1YnRMIgwTLEj3E24bT6tYni50rlCfQ==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + } + }, + "cipher-base": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.4.tgz", + "integrity": "sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q==", + "dev": true, + "requires": { + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "class-utils": { + "version": "0.3.6", + "resolved": "https://registry.npmjs.org/class-utils/-/class-utils-0.3.6.tgz", + "integrity": "sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg==", + "dev": true, + "requires": { + "arr-union": "^3.1.0", + "define-property": "^0.2.5", + "isobject": "^3.0.0", + "static-extend": "^0.1.1" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "requires": { + "is-descriptor": "^0.1.0" + } + } + } + }, + "cli": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/cli/-/cli-1.0.1.tgz", + "integrity": "sha1-IoF1NPJL+klQw01TLUjsvGIbjBQ=", + "dev": true, + "requires": { + "exit": "0.1.2", + "glob": "^7.1.1" + } + }, + "cliui": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-2.1.0.tgz", + "integrity": "sha1-S0dXYP+AJkx2LDoXGQMukcf+oNE=", + "dev": true, + "requires": { + "center-align": "^0.1.1", + "right-align": "^0.1.1", + "wordwrap": "0.0.2" + } + }, + "coffeescript": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/coffeescript/-/coffeescript-1.10.0.tgz", + "integrity": "sha1-56qDAZF+9iGzXYo580jc3R234z4=", + "dev": true + }, + "collection-visit": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/collection-visit/-/collection-visit-1.0.0.tgz", + "integrity": "sha1-S8A3PBZLwykbTTaMgpzxqApZ3KA=", + "dev": true, + "requires": { + "map-visit": "^1.0.0", + "object-visit": "^1.0.0" + } + }, + "color": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/color/-/color-3.0.0.tgz", + "integrity": "sha512-jCpd5+s0s0t7p3pHQKpnJ0TpQKKdleP71LWcA0aqiljpiuAkOSUFN/dyH8ZwF0hRmFlrIuRhufds1QyEP9EB+w==", + "dev": true, + "requires": { + "color-convert": "^1.9.1", + "color-string": "^1.5.2" + } + }, + "color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "requires": { + "color-name": "1.1.3" + } + }, + "color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", + "dev": true + }, + "color-string": { + "version": "1.5.3", + "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.5.3.tgz", + "integrity": "sha512-dC2C5qeWoYkxki5UAXapdjqO672AM4vZuPGRQfO8b5HKuKGBbKWpITyDYN7TOFKvRW7kOgAn3746clDBMDJyQw==", + "dev": true, + "requires": { + "color-name": "^1.0.0", + "simple-swizzle": "^0.2.2" + } + }, + "colornames": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/colornames/-/colornames-1.1.1.tgz", + "integrity": "sha1-+IiQMGhcfE/54qVZ9Qd+t2qBb5Y=", + "dev": true + }, + "colors": { + "version": "1.1.2", + "resolved": "http://registry.npmjs.org/colors/-/colors-1.1.2.tgz", + "integrity": "sha1-FopHAXVran9RoSzgyXv6KMCE7WM=", + "dev": true + }, + "colorspace": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/colorspace/-/colorspace-1.1.1.tgz", + "integrity": "sha512-pI3btWyiuz7Ken0BWh9Elzsmv2bM9AhA7psXib4anUXy/orfZ/E0MbQwhSOG/9L8hLlalqrU0UhOuqxW1YjmVw==", + "dev": true, + "requires": { + "color": "3.0.x", + "text-hex": "1.0.x" + } + }, + "combine-source-map": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/combine-source-map/-/combine-source-map-0.8.0.tgz", + "integrity": "sha1-pY0N8ELBhvz4IqjoAV9UUNLXmos=", + "dev": true, + "requires": { + "convert-source-map": "~1.1.0", + "inline-source-map": "~0.6.0", + "lodash.memoize": "~3.0.3", + "source-map": "~0.5.3" + } + }, + "combined-stream": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.7.tgz", + "integrity": "sha512-brWl9y6vOB1xYPZcpZde3N9zDByXTosAeMDo4p1wzo6UMOX4vumB+TP1RZ76sfE6Md68Q0NJSrE/gbezd4Ul+w==", + "dev": true, + "requires": { + "delayed-stream": "~1.0.0" + } + }, + "component-emitter": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.2.1.tgz", + "integrity": "sha1-E3kY1teCg/ffemt8WmPhQOaUJeY=", + "dev": true + }, + "concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", + "dev": true + }, + "concat-stream": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz", + "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==", + "dev": true, + "requires": { + "buffer-from": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^2.2.2", + "typedarray": "^0.0.6" + } + }, + "console-browserify": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/console-browserify/-/console-browserify-1.1.0.tgz", + "integrity": "sha1-8CQcRXMKn8YyOyBtvzjtx0HQuxA=", + "dev": true, + "requires": { + "date-now": "^0.1.4" + } + }, + "constants-browserify": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/constants-browserify/-/constants-browserify-1.0.0.tgz", + "integrity": "sha1-wguW2MYXdIqvHBYCF2DNJ/y4y3U=", + "dev": true + }, + "convert-source-map": { + "version": "1.1.3", + "resolved": "http://registry.npmjs.org/convert-source-map/-/convert-source-map-1.1.3.tgz", + "integrity": "sha1-SCnId+n+SbMWHzvzZziI4gRpmGA=", + "dev": true + }, + "copy-descriptor": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/copy-descriptor/-/copy-descriptor-0.1.1.tgz", + "integrity": "sha1-Z29us8OZl8LuGsOpJP1hJHSPV40=", + "dev": true + }, + "core-util-is": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", + "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=", + "dev": true + }, + "create-ecdh": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/create-ecdh/-/create-ecdh-4.0.3.tgz", + "integrity": "sha512-GbEHQPMOswGpKXM9kCWVrremUcBmjteUaQ01T9rkKCPDXfUHX0IoP9LpHYo2NPFampa4e+/pFDc3jQdxrxQLaw==", + "dev": true, + "requires": { + "bn.js": "^4.1.0", + "elliptic": "^6.0.0" + } + }, + "create-hash": { + "version": "1.2.0", + "resolved": "http://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz", + "integrity": "sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==", + "dev": true, + "requires": { + "cipher-base": "^1.0.1", + "inherits": "^2.0.1", + "md5.js": "^1.3.4", + "ripemd160": "^2.0.1", + "sha.js": "^2.4.0" + } + }, + "create-hmac": { + "version": "1.1.7", + "resolved": "http://registry.npmjs.org/create-hmac/-/create-hmac-1.1.7.tgz", + "integrity": "sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg==", + "dev": true, + "requires": { + "cipher-base": "^1.0.3", + "create-hash": "^1.1.0", + "inherits": "^2.0.1", + "ripemd160": "^2.0.0", + "safe-buffer": "^5.0.1", + "sha.js": "^2.4.8" + } + }, + "cross-spawn": { + "version": "6.0.5", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", + "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", + "dev": true, + "requires": { + "nice-try": "^1.0.4", + "path-key": "^2.0.1", + "semver": "^5.5.0", + "shebang-command": "^1.2.0", + "which": "^1.2.9" + } + }, + "crypto-browserify": { + "version": "3.12.0", + "resolved": "https://registry.npmjs.org/crypto-browserify/-/crypto-browserify-3.12.0.tgz", + "integrity": "sha512-fz4spIh+znjO2VjL+IdhEpRJ3YN6sMzITSBijk6FK2UvTqruSQW+/cCZTSNsMiZNvUeq0CqurF+dAbyiGOY6Wg==", + "dev": true, + "requires": { + "browserify-cipher": "^1.0.0", + "browserify-sign": "^4.0.0", + "create-ecdh": "^4.0.0", + "create-hash": "^1.1.0", + "create-hmac": "^1.1.0", + "diffie-hellman": "^5.0.0", + "inherits": "^2.0.1", + "pbkdf2": "^3.0.3", + "public-encrypt": "^4.0.0", + "randombytes": "^2.0.0", + "randomfill": "^1.0.3" + } + }, + "currently-unhandled": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/currently-unhandled/-/currently-unhandled-0.4.1.tgz", + "integrity": "sha1-mI3zP+qxke95mmE2nddsF635V+o=", + "dev": true, + "requires": { + "array-find-index": "^1.0.1" + } + }, + "dashdash": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", + "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=", + "dev": true, + "requires": { + "assert-plus": "^1.0.0" + } + }, + "date-now": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/date-now/-/date-now-0.1.4.tgz", + "integrity": "sha1-6vQ5/U1ISK105cx9vvIAZyueNFs=", + "dev": true + }, + "dateformat": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/dateformat/-/dateformat-1.0.12.tgz", + "integrity": "sha1-nxJLZ1lMk3/3BpMuSmQsyo27/uk=", + "dev": true, + "requires": { + "get-stdin": "^4.0.1", + "meow": "^3.3.0" + } + }, + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "decamelize": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=", + "dev": true + }, + "decode-uri-component": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz", + "integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=", + "dev": true + }, + "define-property": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-2.0.2.tgz", + "integrity": "sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ==", + "dev": true, + "requires": { + "is-descriptor": "^1.0.2", + "isobject": "^3.0.1" + }, + "dependencies": { + "is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "dev": true, + "requires": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + } + } + } + }, + "defined": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/defined/-/defined-1.0.0.tgz", + "integrity": "sha1-yY2bzvdWdBiOEQlpFRGZ45sfppM=", + "dev": true + }, + "delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=", + "dev": true + }, + "deps-sort": { + "version": "2.0.0", + "resolved": "http://registry.npmjs.org/deps-sort/-/deps-sort-2.0.0.tgz", + "integrity": "sha1-CRckkC6EZYJg65EHSMzNGvbiH7U=", + "dev": true, + "requires": { + "JSONStream": "^1.0.3", + "shasum": "^1.0.0", + "subarg": "^1.0.0", + "through2": "^2.0.0" + } + }, + "des.js": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/des.js/-/des.js-1.0.0.tgz", + "integrity": "sha1-wHTS4qpqipoH29YfmhXCzYPsjsw=", + "dev": true, + "requires": { + "inherits": "^2.0.1", + "minimalistic-assert": "^1.0.0" + } + }, + "detect-file": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/detect-file/-/detect-file-1.0.0.tgz", + "integrity": "sha1-8NZtA2cqglyxtzvbP+YjEMjlUrc=", + "dev": true + }, + "detective": { + "version": "5.1.0", + "resolved": "http://registry.npmjs.org/detective/-/detective-5.1.0.tgz", + "integrity": "sha512-TFHMqfOvxlgrfVzTEkNBSh9SvSNX/HfF4OFI2QFGCyPm02EsyILqnUeb5P6q7JZ3SFNTBL5t2sePRgrN4epUWQ==", + "dev": true, + "requires": { + "acorn-node": "^1.3.0", + "defined": "^1.0.0", + "minimist": "^1.1.1" + }, + "dependencies": { + "minimist": { + "version": "1.2.0", + "resolved": "http://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", + "dev": true + } + } + }, + "diagnostics": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/diagnostics/-/diagnostics-1.1.1.tgz", + "integrity": "sha512-8wn1PmdunLJ9Tqbx+Fx/ZEuHfJf4NKSN2ZBj7SJC/OWRWha843+WsTjqMe1B5E3p28jqBlp+mJ2fPVxPyNgYKQ==", + "dev": true, + "requires": { + "colorspace": "1.1.x", + "enabled": "1.0.x", + "kuler": "1.0.x" + } + }, + "diffie-hellman": { + "version": "5.0.3", + "resolved": "http://registry.npmjs.org/diffie-hellman/-/diffie-hellman-5.0.3.tgz", + "integrity": "sha512-kqag/Nl+f3GwyK25fhUMYj81BUOrZ9IuJsjIcDE5icNM9FJHAVm3VcUDxdLPoQtTuUylWm6ZIknYJwwaPxsUzg==", + "dev": true, + "requires": { + "bn.js": "^4.1.0", + "miller-rabin": "^4.0.0", + "randombytes": "^2.0.0" + } + }, + "dom-serializer": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.1.0.tgz", + "integrity": "sha1-BzxpdUbOB4DOI75KKOKT5AvDDII=", + "dev": true, + "requires": { + "domelementtype": "~1.1.1", + "entities": "~1.1.1" + }, + "dependencies": { + "domelementtype": { + "version": "1.1.3", + "resolved": "http://registry.npmjs.org/domelementtype/-/domelementtype-1.1.3.tgz", + "integrity": "sha1-vSh3PiZCiBrsUVRJJCmcXNgiGFs=", + "dev": true + }, + "entities": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/entities/-/entities-1.1.2.tgz", + "integrity": "sha512-f2LZMYl1Fzu7YSBKg+RoROelpOaNrcGmE9AZubeDfrCEia483oW4MI4VyFd5VNHIgQ/7qm1I0wUHK1eJnn2y2w==", + "dev": true + } + } + }, + "domain-browser": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/domain-browser/-/domain-browser-1.2.0.tgz", + "integrity": "sha512-jnjyiM6eRyZl2H+W8Q/zLMA481hzi0eszAaBUzIVnmYVDBbnLxVNnfu1HgEBvCbL+71FrxMl3E6lpKH7Ge3OXA==", + "dev": true + }, + "domelementtype": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.3.1.tgz", + "integrity": "sha512-BSKB+TSpMpFI/HOxCNr1O8aMOTZ8hT3pM3GQ0w/mWRmkhEDSFJkkyzz4XQsBV44BChwGkrDfMyjVD0eA2aFV3w==", + "dev": true + }, + "domhandler": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-2.3.0.tgz", + "integrity": "sha1-LeWaCCLVAn+r/28DLCsloqir5zg=", + "dev": true, + "requires": { + "domelementtype": "1" + } + }, + "domutils": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.5.1.tgz", + "integrity": "sha1-3NhIiib1Y9YQeeSMn3t+Mjc2gs8=", + "dev": true, + "requires": { + "dom-serializer": "0", + "domelementtype": "1" + } + }, + "duplexer2": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/duplexer2/-/duplexer2-0.1.4.tgz", + "integrity": "sha1-ixLauHjA1p4+eJEFFmKjL8a93ME=", + "dev": true, + "requires": { + "readable-stream": "^2.0.2" + } + }, + "ecc-jsbn": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", + "integrity": "sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=", + "dev": true, + "requires": { + "jsbn": "~0.1.0", + "safer-buffer": "^2.1.0" + } + }, + "elliptic": { + "version": "6.4.1", + "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.4.1.tgz", + "integrity": "sha512-BsXLz5sqX8OHcsh7CqBMztyXARmGQ3LWPtGjJi6DiJHq5C/qvi9P3OqgswKSDftbu8+IoI/QDTAm2fFnQ9SZSQ==", + "dev": true, + "requires": { + "bn.js": "^4.4.0", + "brorand": "^1.0.1", + "hash.js": "^1.0.0", + "hmac-drbg": "^1.0.0", + "inherits": "^2.0.1", + "minimalistic-assert": "^1.0.0", + "minimalistic-crypto-utils": "^1.0.0" + } + }, + "enabled": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/enabled/-/enabled-1.0.2.tgz", + "integrity": "sha1-ll9lE9LC0cX0ZStkouM5ZGf8L5M=", + "dev": true, + "requires": { + "env-variable": "0.0.x" + } + }, + "entities": { + "version": "1.0.0", + "resolved": "http://registry.npmjs.org/entities/-/entities-1.0.0.tgz", + "integrity": "sha1-sph6o4ITR/zeZCsk/fyeT7cSvyY=", + "dev": true + }, + "env-variable": { + "version": "0.0.5", + "resolved": "https://registry.npmjs.org/env-variable/-/env-variable-0.0.5.tgz", + "integrity": "sha512-zoB603vQReOFvTg5xMl9I1P2PnHsHQQKTEowsKKD7nseUfJq6UWzK+4YtlWUO1nhiQUxe6XMkk+JleSZD1NZFA==", + "dev": true + }, + "error-ex": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", + "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "dev": true, + "requires": { + "is-arrayish": "^0.2.1" + } + }, + "es6-promise": { + "version": "4.2.5", + "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-4.2.5.tgz", + "integrity": "sha512-n6wvpdE43VFtJq+lUDYDBFUwV8TZbuGXLV4D6wKafg13ldznKsyEvatubnmUe31zcvelSzOHF+XbaT+Bl9ObDg==", + "dev": true + }, + "es6-promisify": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/es6-promisify/-/es6-promisify-5.0.0.tgz", + "integrity": "sha1-UQnWLz5W6pZ8S2NQWu8IKRyKUgM=", + "dev": true, + "requires": { + "es6-promise": "^4.0.3" + } + }, + "escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=" + }, + "esprima": { + "version": "2.7.3", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-2.7.3.tgz", + "integrity": "sha1-luO3DVd59q1JzQMmc9HDEnZ7pYE=", + "dev": true + }, + "eventemitter2": { + "version": "0.4.14", + "resolved": "http://registry.npmjs.org/eventemitter2/-/eventemitter2-0.4.14.tgz", + "integrity": "sha1-j2G3XN4BKy6esoTUVFWDtWQ7Yas=", + "dev": true + }, + "events": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/events/-/events-2.1.0.tgz", + "integrity": "sha512-3Zmiobend8P9DjmKAty0Era4jV8oJ0yGYe2nJJAxgymF9+N8F2m0hhZiMoWtcfepExzNKZumFU3ksdQbInGWCg==", + "dev": true + }, + "evp_bytestokey": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz", + "integrity": "sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA==", + "dev": true, + "requires": { + "md5.js": "^1.3.4", + "safe-buffer": "^5.1.1" + } + }, + "exit": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", + "integrity": "sha1-BjJjj42HfMghB9MKD/8aF8uhzQw=", + "dev": true + }, + "expand-brackets": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz", + "integrity": "sha1-t3c14xXOMPa27/D4OwQVGiJEliI=", + "dev": true, + "requires": { + "debug": "^2.3.3", + "define-property": "^0.2.5", + "extend-shallow": "^2.0.1", + "posix-character-classes": "^0.1.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "requires": { + "is-descriptor": "^0.1.0" + } + }, + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "expand-tilde": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/expand-tilde/-/expand-tilde-2.0.2.tgz", + "integrity": "sha1-l+gBqgUt8CRU3kawK/YhZCzchQI=", + "dev": true, + "requires": { + "homedir-polyfill": "^1.0.1" + } + }, + "extend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", + "dev": true + }, + "extend-shallow": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", + "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=", + "dev": true, + "requires": { + "assign-symbols": "^1.0.0", + "is-extendable": "^1.0.1" + }, + "dependencies": { + "is-extendable": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", + "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", + "dev": true, + "requires": { + "is-plain-object": "^2.0.4" + } + } + } + }, + "extglob": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz", + "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==", + "dev": true, + "requires": { + "array-unique": "^0.3.2", + "define-property": "^1.0.0", + "expand-brackets": "^2.1.4", + "extend-shallow": "^2.0.1", + "fragment-cache": "^0.2.1", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" + }, + "dependencies": { + "define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", + "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", + "dev": true, + "requires": { + "is-descriptor": "^1.0.0" + } + }, + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + }, + "is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "dev": true, + "requires": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + } + } + } + }, + "extract-zip": { + "version": "1.6.7", + "resolved": "https://registry.npmjs.org/extract-zip/-/extract-zip-1.6.7.tgz", + "integrity": "sha1-qEC0uK9kAyZMjbV/Txp0Mz74H+k=", + "dev": true, + "requires": { + "concat-stream": "1.6.2", + "debug": "2.6.9", + "mkdirp": "0.5.1", + "yauzl": "2.4.1" + } + }, + "extsprintf": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", + "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=", + "dev": true + }, + "fast-deep-equal": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz", + "integrity": "sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk=", + "dev": true + }, + "fast-json-stable-stringify": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz", + "integrity": "sha1-1RQsDK7msRifh9OnYREGT4bIu/I=", + "dev": true + }, + "fast-safe-stringify": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-2.0.6.tgz", + "integrity": "sha512-q8BZ89jjc+mz08rSxROs8VsrBBcn1SIw1kq9NjolL509tkABRk9io01RAjSaEv1Xb2uFLt8VtRiZbGp5H8iDtg==", + "dev": true + }, + "fd-slicer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.0.1.tgz", + "integrity": "sha1-i1vL2ewyfFBBv5qwI/1nUPEXfmU=", + "dev": true, + "requires": { + "pend": "~1.2.0" + } + }, + "fecha": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fecha/-/fecha-2.3.3.tgz", + "integrity": "sha512-lUGBnIamTAwk4znq5BcqsDaxSmZ9nDVJaij6NvRt/Tg4R69gERA+otPKbS86ROw9nxVMw2/mp1fnaiWqbs6Sdg==", + "dev": true + }, + "figures": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-1.7.0.tgz", + "integrity": "sha1-y+Hjr/zxzUS4DK3+0o3Hk6lwHS4=", + "dev": true, + "requires": { + "escape-string-regexp": "^1.0.5", + "object-assign": "^4.1.0" + } + }, + "fill-range": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", + "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", + "dev": true, + "requires": { + "extend-shallow": "^2.0.1", + "is-number": "^3.0.0", + "repeat-string": "^1.6.1", + "to-regex-range": "^2.1.0" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "find-up": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-1.1.2.tgz", + "integrity": "sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8=", + "dev": true, + "requires": { + "path-exists": "^2.0.0", + "pinkie-promise": "^2.0.0" + } + }, + "findup-sync": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/findup-sync/-/findup-sync-0.3.0.tgz", + "integrity": "sha1-N5MKpdgWt3fANEXhlmzGeQpMCxY=", + "dev": true, + "requires": { + "glob": "~5.0.0" + }, + "dependencies": { + "glob": { + "version": "5.0.15", + "resolved": "https://registry.npmjs.org/glob/-/glob-5.0.15.tgz", + "integrity": "sha1-G8k2ueAvSmA/zCIuz3Yz0wuLk7E=", + "dev": true, + "requires": { + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "2 || 3", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + } + } + }, + "fined": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/fined/-/fined-1.1.1.tgz", + "integrity": "sha512-jQp949ZmEbiYHk3gkbdtpJ0G1+kgtLQBNdP5edFP7Fh+WAYceLQz6yO1SBj72Xkg8GVyTB3bBzAYrHJVh5Xd5g==", + "dev": true, + "requires": { + "expand-tilde": "^2.0.2", + "is-plain-object": "^2.0.3", + "object.defaults": "^1.1.0", + "object.pick": "^1.2.0", + "parse-filepath": "^1.0.1" + } + }, + "flagged-respawn": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/flagged-respawn/-/flagged-respawn-1.0.1.tgz", + "integrity": "sha512-lNaHNVymajmk0OJMBn8fVUAU1BtDeKIqKoVhk4xAALB57aALg6b4W0MfJ/cUE0g9YBXy5XhSlPIpYIJ7HaY/3Q==", + "dev": true + }, + "for-in": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz", + "integrity": "sha1-gQaNKVqBQuwKxybG4iAMMPttXoA=", + "dev": true + }, + "for-own": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/for-own/-/for-own-1.0.0.tgz", + "integrity": "sha1-xjMy9BXO3EsE2/5wz4NklMU8tEs=", + "dev": true, + "requires": { + "for-in": "^1.0.1" + } + }, + "forever-agent": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", + "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=", + "dev": true + }, + "form-data": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", + "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", + "dev": true, + "requires": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.6", + "mime-types": "^2.1.12" + } + }, + "fragment-cache": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/fragment-cache/-/fragment-cache-0.2.1.tgz", + "integrity": "sha1-QpD60n8T6Jvn8zeZxrxaCr//DRk=", + "dev": true, + "requires": { + "map-cache": "^0.2.2" + } + }, + "fs-extra": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-1.0.0.tgz", + "integrity": "sha1-zTzl9+fLYUWIP8rjGR6Yd/hYeVA=", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "jsonfile": "^2.1.0", + "klaw": "^1.0.0" + }, + "dependencies": { + "klaw": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/klaw/-/klaw-1.3.1.tgz", + "integrity": "sha1-QIhDO0azsbolnXh4XY6W9zugJDk=", + "dev": true, + "requires": { + "graceful-fs": "^4.1.9" + } + } + } + }, + "fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", + "dev": true + }, + "function-bind": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", + "dev": true + }, + "get-assigned-identifiers": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/get-assigned-identifiers/-/get-assigned-identifiers-1.2.0.tgz", + "integrity": "sha512-mBBwmeGTrxEMO4pMaaf/uUEFHnYtwr8FTe8Y/mer4rcV/bye0qGm6pw1bGZFGStxC5O76c5ZAVBGnqHmOaJpdQ==", + "dev": true + }, + "get-stdin": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-4.0.1.tgz", + "integrity": "sha1-uWjGsKBDhDJJAui/Gl3zJXmkUP4=", + "dev": true + }, + "get-value": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/get-value/-/get-value-2.0.6.tgz", + "integrity": "sha1-3BXKHGcjh8p2vTesCjlbogQqLCg=", + "dev": true + }, + "getobject": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/getobject/-/getobject-0.1.0.tgz", + "integrity": "sha1-BHpEl4n6Fg0Bj1SG7ZEyC27HiFw=", + "dev": true + }, + "getpass": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", + "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=", + "dev": true, + "requires": { + "assert-plus": "^1.0.0" + } + }, + "glob": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.3.tgz", + "integrity": "sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "global-modules": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/global-modules/-/global-modules-1.0.0.tgz", + "integrity": "sha512-sKzpEkf11GpOFuw0Zzjzmt4B4UZwjOcG757PPvrfhxcLFbq0wpsgpOqxpxtxFiCG4DtG93M6XRVbF2oGdev7bg==", + "dev": true, + "requires": { + "global-prefix": "^1.0.1", + "is-windows": "^1.0.1", + "resolve-dir": "^1.0.0" + } + }, + "global-prefix": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/global-prefix/-/global-prefix-1.0.2.tgz", + "integrity": "sha1-2/dDxsFJklk8ZVVoy2btMsASLr4=", + "dev": true, + "requires": { + "expand-tilde": "^2.0.2", + "homedir-polyfill": "^1.0.1", + "ini": "^1.3.4", + "is-windows": "^1.0.1", + "which": "^1.2.14" + } + }, + "graceful-fs": { + "version": "4.1.15", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.15.tgz", + "integrity": "sha512-6uHUhOPEBgQ24HM+r6b/QwWfZq+yiFcipKFrOFiBEnWdy5sdzYoi+pJeQaPI5qOLRFqWmAXUPQNsielzdLoecA==" + }, + "grunt": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/grunt/-/grunt-1.0.3.tgz", + "integrity": "sha512-/JzmZNPfKorlCrrmxWqQO4JVodO+DVd5XX4DkocL/1WlLlKVLE9+SdEIempOAxDhWPysLle6afvn/hg7Ck2k9g==", + "dev": true, + "requires": { + "coffeescript": "~1.10.0", + "dateformat": "~1.0.12", + "eventemitter2": "~0.4.13", + "exit": "~0.1.1", + "findup-sync": "~0.3.0", + "glob": "~7.0.0", + "grunt-cli": "~1.2.0", + "grunt-known-options": "~1.1.0", + "grunt-legacy-log": "~2.0.0", + "grunt-legacy-util": "~1.1.1", + "iconv-lite": "~0.4.13", + "js-yaml": "~3.5.2", + "minimatch": "~3.0.2", + "mkdirp": "~0.5.1", + "nopt": "~3.0.6", + "path-is-absolute": "~1.0.0", + "rimraf": "~2.6.2" + }, + "dependencies": { + "glob": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.0.6.tgz", + "integrity": "sha1-IRuvr0nlJbjNkyYNFKsTYVKz9Xo=", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.2", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "grunt-cli": { + "version": "1.2.0", + "resolved": "http://registry.npmjs.org/grunt-cli/-/grunt-cli-1.2.0.tgz", + "integrity": "sha1-VisRnrsGndtGSs4oRVAb6Xs1tqg=", + "dev": true, + "requires": { + "findup-sync": "~0.3.0", + "grunt-known-options": "~1.1.0", + "nopt": "~3.0.6", + "resolve": "~1.1.0" + } + }, + "nopt": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-3.0.6.tgz", + "integrity": "sha1-xkZdvwirzU2zWTF/eaxopkayj/k=", + "dev": true, + "requires": { + "abbrev": "1" + } + }, + "resolve": { + "version": "1.1.7", + "resolved": "http://registry.npmjs.org/resolve/-/resolve-1.1.7.tgz", + "integrity": "sha1-IDEU2CrSxe2ejgQRs5ModeiJ6Xs=", + "dev": true + } + } + }, + "grunt-cli": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/grunt-cli/-/grunt-cli-1.3.2.tgz", + "integrity": "sha512-8OHDiZZkcptxVXtMfDxJvmN7MVJNE8L/yIcPb4HB7TlyFD1kDvjHrb62uhySsU14wJx9ORMnTuhRMQ40lH/orQ==", + "dev": true, + "requires": { + "grunt-known-options": "~1.1.0", + "interpret": "~1.1.0", + "liftoff": "~2.5.0", + "nopt": "~4.0.1", + "v8flags": "~3.1.1" + } + }, + "grunt-contrib-concat": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/grunt-contrib-concat/-/grunt-contrib-concat-1.0.1.tgz", + "integrity": "sha1-YVCYYwhOhx1+ht5IwBUlntl3Rb0=", + "dev": true, + "requires": { + "chalk": "^1.0.0", + "source-map": "^0.5.3" + }, + "dependencies": { + "ansi-styles": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", + "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", + "dev": true + }, + "chalk": { + "version": "1.1.3", + "resolved": "http://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", + "dev": true, + "requires": { + "ansi-styles": "^2.2.1", + "escape-string-regexp": "^1.0.2", + "has-ansi": "^2.0.0", + "strip-ansi": "^3.0.0", + "supports-color": "^2.0.0" + } + }, + "supports-color": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", + "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", + "dev": true + } + } + }, + "grunt-contrib-jshint": { + "version": "1.1.0", + "resolved": "http://registry.npmjs.org/grunt-contrib-jshint/-/grunt-contrib-jshint-1.1.0.tgz", + "integrity": "sha1-Np2QmyWTxA6L55lAshNAhQx5Oaw=", + "dev": true, + "requires": { + "chalk": "^1.1.1", + "hooker": "^0.2.3", + "jshint": "~2.9.4" + }, + "dependencies": { + "ansi-styles": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", + "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", + "dev": true + }, + "chalk": { + "version": "1.1.3", + "resolved": "http://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", + "dev": true, + "requires": { + "ansi-styles": "^2.2.1", + "escape-string-regexp": "^1.0.2", + "has-ansi": "^2.0.0", + "strip-ansi": "^3.0.0", + "supports-color": "^2.0.0" + } + }, + "supports-color": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", + "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", + "dev": true + } + } + }, + "grunt-contrib-qunit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/grunt-contrib-qunit/-/grunt-contrib-qunit-3.1.0.tgz", + "integrity": "sha512-mdk8UltH6mxCD63E0hTXMAts42DOi4z4bBBrY7qnuHiShflMF7IueSMYe0zWaZ2dO8mgujh57Zfny2EbigJhRg==", + "dev": true, + "requires": { + "eventemitter2": "^5.0.1", + "p-each-series": "^1.0.0", + "puppeteer": "^1.11.0" + }, + "dependencies": { + "eventemitter2": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/eventemitter2/-/eventemitter2-5.0.1.tgz", + "integrity": "sha1-YZegldX7a1folC9v1+qtY6CclFI=", + "dev": true + } + } + }, + "grunt-contrib-uglify": { + "version": "1.0.2", + "resolved": "http://registry.npmjs.org/grunt-contrib-uglify/-/grunt-contrib-uglify-1.0.2.tgz", + "integrity": "sha1-rmekb5FT7dTLEYE6Vetpxw19svs=", + "dev": true, + "requires": { + "chalk": "^1.0.0", + "lodash": "^4.0.1", + "maxmin": "^1.1.0", + "uglify-js": "~2.6.2", + "uri-path": "^1.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", + "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", + "dev": true + }, + "chalk": { + "version": "1.1.3", + "resolved": "http://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", + "dev": true, + "requires": { + "ansi-styles": "^2.2.1", + "escape-string-regexp": "^1.0.2", + "has-ansi": "^2.0.0", + "strip-ansi": "^3.0.0", + "supports-color": "^2.0.0" + } + }, + "supports-color": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", + "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", + "dev": true + } + } + }, + "grunt-jsdoc": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/grunt-jsdoc/-/grunt-jsdoc-2.3.0.tgz", + "integrity": "sha512-gC66TCRXeQMj3HIyqVSBJm8zdUz43e5vaG/PLO/627A1edbJnzxhJV7nF0KqLwMM0RDNu1istC6fvfnYqFKi3w==", + "dev": true, + "requires": { + "cross-spawn": "^6.0.5", + "jsdoc": "~3.5.5", + "marked": "^0.5.0" + }, + "dependencies": { + "marked": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/marked/-/marked-0.5.2.tgz", + "integrity": "sha512-fdZvBa7/vSQIZCi4uuwo2N3q+7jJURpMVCcbaX0S1Mg65WZ5ilXvC67MviJAsdjqqgD+CEq4RKo5AYGgINkVAA==", + "dev": true + } + } + }, + "grunt-known-options": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/grunt-known-options/-/grunt-known-options-1.1.1.tgz", + "integrity": "sha512-cHwsLqoighpu7TuYj5RonnEuxGVFnztcUqTqp5rXFGYL4OuPFofwC4Ycg7n9fYwvK6F5WbYgeVOwph9Crs2fsQ==", + "dev": true + }, + "grunt-legacy-log": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/grunt-legacy-log/-/grunt-legacy-log-2.0.0.tgz", + "integrity": "sha512-1m3+5QvDYfR1ltr8hjiaiNjddxGdQWcH0rw1iKKiQnF0+xtgTazirSTGu68RchPyh1OBng1bBUjLmX8q9NpoCw==", + "dev": true, + "requires": { + "colors": "~1.1.2", + "grunt-legacy-log-utils": "~2.0.0", + "hooker": "~0.2.3", + "lodash": "~4.17.5" + } + }, + "grunt-legacy-log-utils": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/grunt-legacy-log-utils/-/grunt-legacy-log-utils-2.0.1.tgz", + "integrity": "sha512-o7uHyO/J+i2tXG8r2bZNlVk20vlIFJ9IEYyHMCQGfWYru8Jv3wTqKZzvV30YW9rWEjq0eP3cflQ1qWojIe9VFA==", + "dev": true, + "requires": { + "chalk": "~2.4.1", + "lodash": "~4.17.10" + } + }, + "grunt-legacy-util": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/grunt-legacy-util/-/grunt-legacy-util-1.1.1.tgz", + "integrity": "sha512-9zyA29w/fBe6BIfjGENndwoe1Uy31BIXxTH3s8mga0Z5Bz2Sp4UCjkeyv2tI449ymkx3x26B+46FV4fXEddl5A==", + "dev": true, + "requires": { + "async": "~1.5.2", + "exit": "~0.1.1", + "getobject": "~0.1.0", + "hooker": "~0.2.3", + "lodash": "~4.17.10", + "underscore.string": "~3.3.4", + "which": "~1.3.0" + } + }, + "grunt-shell-spawn": { + "version": "0.3.12", + "resolved": "https://registry.npmjs.org/grunt-shell-spawn/-/grunt-shell-spawn-0.3.12.tgz", + "integrity": "sha512-TprZct92sQ4M2Q92piaeLsCrx4+gq/ageuxjZsRG6cglKt7x7rGA3YHt8D30+G789v+/pw4l0tDjEyrkMXx2tA==", + "dev": true, + "requires": { + "grunt": ">=0.4.x" + } + }, + "gzip-size": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/gzip-size/-/gzip-size-1.0.0.tgz", + "integrity": "sha1-Zs+LEBBHInuVus5uodoMF37Vwi8=", + "dev": true, + "requires": { + "browserify-zlib": "^0.1.4", + "concat-stream": "^1.4.1" + }, + "dependencies": { + "browserify-zlib": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/browserify-zlib/-/browserify-zlib-0.1.4.tgz", + "integrity": "sha1-uzX4pRn2AOD6a4SFJByXnQFB+y0=", + "dev": true, + "requires": { + "pako": "~0.2.0" + } + }, + "pako": { + "version": "0.2.9", + "resolved": "http://registry.npmjs.org/pako/-/pako-0.2.9.tgz", + "integrity": "sha1-8/dSL073gjSNqBYbrZ7P1Rv4OnU=", + "dev": true + } + } + }, + "har-schema": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", + "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=", + "dev": true + }, + "har-validator": { + "version": "5.1.3", + "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.3.tgz", + "integrity": "sha512-sNvOCzEQNr/qrvJgc3UG/kD4QtlHycrzwS+6mfTrrSq97BvaYcPZZI1ZSqGSPR73Cxn4LKTD4PttRwfU7jWq5g==", + "dev": true, + "requires": { + "ajv": "^6.5.5", + "har-schema": "^2.0.0" + } + }, + "has": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "dev": true, + "requires": { + "function-bind": "^1.1.1" + } + }, + "has-ansi": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", + "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=", + "dev": true, + "requires": { + "ansi-regex": "^2.0.0" + } + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "dev": true + }, + "has-value": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-value/-/has-value-1.0.0.tgz", + "integrity": "sha1-GLKB2lhbHFxR3vJMkw7SmgvmsXc=", + "dev": true, + "requires": { + "get-value": "^2.0.6", + "has-values": "^1.0.0", + "isobject": "^3.0.0" + } + }, + "has-values": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-values/-/has-values-1.0.0.tgz", + "integrity": "sha1-lbC2P+whRmGab+V/51Yo1aOe/k8=", + "dev": true, + "requires": { + "is-number": "^3.0.0", + "kind-of": "^4.0.0" + }, + "dependencies": { + "kind-of": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-4.0.0.tgz", + "integrity": "sha1-IIE989cSkosgc3hpGkUGb65y3Vc=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "hash-base": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-3.0.4.tgz", + "integrity": "sha1-X8hoaEfs1zSZQDMZprCj8/auSRg=", + "dev": true, + "requires": { + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "hash.js": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.7.tgz", + "integrity": "sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==", + "dev": true, + "requires": { + "inherits": "^2.0.3", + "minimalistic-assert": "^1.0.1" + } + }, + "hasha": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/hasha/-/hasha-2.2.0.tgz", + "integrity": "sha1-eNfL/B5tZjA/55g3NlmEUXsvbuE=", + "dev": true, + "requires": { + "is-stream": "^1.0.1", + "pinkie-promise": "^2.0.0" + } + }, + "hmac-drbg": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz", + "integrity": "sha1-0nRXAQJabHdabFRXk+1QL8DGSaE=", + "dev": true, + "requires": { + "hash.js": "^1.0.3", + "minimalistic-assert": "^1.0.0", + "minimalistic-crypto-utils": "^1.0.1" + } + }, + "homedir-polyfill": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/homedir-polyfill/-/homedir-polyfill-1.0.1.tgz", + "integrity": "sha1-TCu8inWJmP7r9e1oWA921GdotLw=", + "dev": true, + "requires": { + "parse-passwd": "^1.0.0" + } + }, + "hooker": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/hooker/-/hooker-0.2.3.tgz", + "integrity": "sha1-uDT3I8xKJCqmWWNFnfbZhMXT2Vk=", + "dev": true + }, + "hosted-git-info": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.7.1.tgz", + "integrity": "sha512-7T/BxH19zbcCTa8XkMlbK5lTo1WtgkFi3GvdWEyNuc4Vex7/9Dqbnpsf4JMydcfj9HCg4zUWFTL3Za6lapg5/w==", + "dev": true + }, + "htmlescape": { + "version": "1.1.1", + "resolved": "http://registry.npmjs.org/htmlescape/-/htmlescape-1.1.1.tgz", + "integrity": "sha1-OgPtwiFLyjtmQko+eVk0lQnLA1E=", + "dev": true + }, + "htmlparser2": { + "version": "3.8.3", + "resolved": "http://registry.npmjs.org/htmlparser2/-/htmlparser2-3.8.3.tgz", + "integrity": "sha1-mWwosZFRaovoZQGn15dX5ccMEGg=", + "dev": true, + "requires": { + "domelementtype": "1", + "domhandler": "2.3", + "domutils": "1.5", + "entities": "1.0", + "readable-stream": "1.1" + }, + "dependencies": { + "isarray": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=", + "dev": true + }, + "readable-stream": { + "version": "1.1.14", + "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz", + "integrity": "sha1-fPTFTvZI44EwhMY23SB54WbAgdk=", + "dev": true, + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.1", + "isarray": "0.0.1", + "string_decoder": "~0.10.x" + } + }, + "string_decoder": { + "version": "0.10.31", + "resolved": "http://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", + "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=", + "dev": true + } + } + }, + "http-signature": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", + "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=", + "dev": true, + "requires": { + "assert-plus": "^1.0.0", + "jsprim": "^1.2.2", + "sshpk": "^1.7.0" + } + }, + "https-browserify": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/https-browserify/-/https-browserify-1.0.0.tgz", + "integrity": "sha1-7AbBDgo0wPL68Zn3/X/Hj//QPHM=", + "dev": true + }, + "https-proxy-agent": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-2.2.1.tgz", + "integrity": "sha512-HPCTS1LW51bcyMYbxUIOO4HEOlQ1/1qRaFWcyxvwaqUS9TY88aoEuHUY33kuAh1YhVVaDQhLZsnPd+XNARWZlQ==", + "dev": true, + "requires": { + "agent-base": "^4.1.0", + "debug": "^3.1.0" + }, + "dependencies": { + "debug": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", + "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + }, + "ms": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", + "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==", + "dev": true + } + } + }, + "iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "dev": true, + "requires": { + "safer-buffer": ">= 2.1.2 < 3" + } + }, + "ieee754": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.12.tgz", + "integrity": "sha512-GguP+DRY+pJ3soyIiGPTvdiVXjZ+DbXOxGpXn3eMvNW4x4irjqXm4wHKscC+TfxSJ0yw/S1F24tqdMNsMZTiLA==", + "dev": true + }, + "indent-string": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-2.1.0.tgz", + "integrity": "sha1-ji1INIdCEhtKghi3oTfppSBJ3IA=", + "dev": true, + "requires": { + "repeating": "^2.0.0" + } + }, + "inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "dev": true, + "requires": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", + "dev": true + }, + "ini": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.5.tgz", + "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==", + "dev": true + }, + "inline-source-map": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/inline-source-map/-/inline-source-map-0.6.2.tgz", + "integrity": "sha1-+Tk0ccGKedFyT4Y/o4tYY3Ct4qU=", + "dev": true, + "requires": { + "source-map": "~0.5.3" + } + }, + "insert-module-globals": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/insert-module-globals/-/insert-module-globals-7.2.0.tgz", + "integrity": "sha512-VE6NlW+WGn2/AeOMd496AHFYmE7eLKkUY6Ty31k4og5vmA3Fjuwe9v6ifH6Xx/Hz27QvdoMoviw1/pqWRB09Sw==", + "dev": true, + "requires": { + "JSONStream": "^1.0.3", + "acorn-node": "^1.5.2", + "combine-source-map": "^0.8.0", + "concat-stream": "^1.6.1", + "is-buffer": "^1.1.0", + "path-is-absolute": "^1.0.1", + "process": "~0.11.0", + "through2": "^2.0.0", + "undeclared-identifiers": "^1.1.2", + "xtend": "^4.0.0" + } + }, + "interpret": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/interpret/-/interpret-1.1.0.tgz", + "integrity": "sha1-ftGxQQxqDg94z5XTuEQMY/eLhhQ=", + "dev": true + }, + "is-absolute": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-absolute/-/is-absolute-1.0.0.tgz", + "integrity": "sha512-dOWoqflvcydARa360Gvv18DZ/gRuHKi2NU/wU5X1ZFzdYfH29nkiNZsF3mp4OJ3H4yo9Mx8A/uAGNzpzPN3yBA==", + "dev": true, + "requires": { + "is-relative": "^1.0.0", + "is-windows": "^1.0.1" + } + }, + "is-accessor-descriptor": { + "version": "0.1.6", + "resolved": "http://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", + "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=", + "dev": true + }, + "is-buffer": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", + "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", + "dev": true + }, + "is-builtin-module": { + "version": "1.0.0", + "resolved": "http://registry.npmjs.org/is-builtin-module/-/is-builtin-module-1.0.0.tgz", + "integrity": "sha1-VAVy0096wxGfj3bDDLwbHgN6/74=", + "dev": true, + "requires": { + "builtin-modules": "^1.0.0" + } + }, + "is-data-descriptor": { + "version": "0.1.4", + "resolved": "http://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", + "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "is-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", + "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", + "dev": true, + "requires": { + "is-accessor-descriptor": "^0.1.6", + "is-data-descriptor": "^0.1.4", + "kind-of": "^5.0.0" + }, + "dependencies": { + "kind-of": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", + "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", + "dev": true + } + } + }, + "is-extendable": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", + "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=", + "dev": true + }, + "is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", + "dev": true + }, + "is-finite": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-finite/-/is-finite-1.0.2.tgz", + "integrity": "sha1-zGZ3aVYCvlUO8R6LSqYwU0K20Ko=", + "dev": true, + "requires": { + "number-is-nan": "^1.0.0" + } + }, + "is-glob": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz", + "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=", + "dev": true, + "requires": { + "is-extglob": "^2.1.0" + } + }, + "is-number": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", + "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "is-plain-object": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", + "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", + "dev": true, + "requires": { + "isobject": "^3.0.1" + } + }, + "is-relative": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-relative/-/is-relative-1.0.0.tgz", + "integrity": "sha512-Kw/ReK0iqwKeu0MITLFuj0jbPAmEiOsIwyIXvvbfa6QfmN9pkD1M+8pdk7Rl/dTKbH34/XBFMbgD4iMJhLQbGA==", + "dev": true, + "requires": { + "is-unc-path": "^1.0.0" + } + }, + "is-stream": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", + "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=", + "dev": true + }, + "is-typedarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", + "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=", + "dev": true + }, + "is-unc-path": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-unc-path/-/is-unc-path-1.0.0.tgz", + "integrity": "sha512-mrGpVd0fs7WWLfVsStvgF6iEJnbjDFZh9/emhRDcGWTduTfNHd9CHeUwH3gYIjdbwo4On6hunkztwOaAw0yllQ==", + "dev": true, + "requires": { + "unc-path-regex": "^0.1.2" + } + }, + "is-utf8": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-utf8/-/is-utf8-0.2.1.tgz", + "integrity": "sha1-Sw2hRCEE0bM2NA6AeX6GXPOffXI=", + "dev": true + }, + "is-windows": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz", + "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==", + "dev": true + }, + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", + "dev": true + }, + "isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", + "dev": true + }, + "isobject": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", + "dev": true + }, + "isstream": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", + "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=", + "dev": true + }, + "js-yaml": { + "version": "3.5.5", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.5.5.tgz", + "integrity": "sha1-A3fDgBfKvHMisNH7zSWkkWQfL74=", + "dev": true, + "requires": { + "argparse": "^1.0.2", + "esprima": "^2.6.0" + } + }, + "js2xmlparser": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/js2xmlparser/-/js2xmlparser-3.0.0.tgz", + "integrity": "sha1-P7YOqgicVED5MZ9RdgzNB+JJlzM=", + "requires": { + "xmlcreate": "^1.0.1" + } + }, + "jsbn": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", + "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=", + "dev": true + }, + "jsdoc": { + "version": "3.5.5", + "resolved": "https://registry.npmjs.org/jsdoc/-/jsdoc-3.5.5.tgz", + "integrity": "sha512-6PxB65TAU4WO0Wzyr/4/YhlGovXl0EVYfpKbpSroSj0qBxT4/xod/l40Opkm38dRHRdQgdeY836M0uVnJQG7kg==", + "requires": { + "babylon": "7.0.0-beta.19", + "bluebird": "~3.5.0", + "catharsis": "~0.8.9", + "escape-string-regexp": "~1.0.5", + "js2xmlparser": "~3.0.0", + "klaw": "~2.0.0", + "marked": "~0.3.6", + "mkdirp": "~0.5.1", + "requizzle": "~0.2.1", + "strip-json-comments": "~2.0.1", + "taffydb": "2.6.2", + "underscore": "~1.8.3" + } + }, + "jshint": { + "version": "2.9.7", + "resolved": "https://registry.npmjs.org/jshint/-/jshint-2.9.7.tgz", + "integrity": "sha512-Q8XN38hGsVQhdlM+4gd1Xl7OB1VieSuCJf+fEJjpo59JH99bVJhXRXAh26qQ15wfdd1VPMuDWNeSWoNl53T4YA==", + "dev": true, + "requires": { + "cli": "~1.0.0", + "console-browserify": "1.1.x", + "exit": "0.1.x", + "htmlparser2": "3.8.x", + "lodash": "~4.17.10", + "minimatch": "~3.0.2", + "shelljs": "0.3.x", + "strip-json-comments": "1.0.x" + }, + "dependencies": { + "strip-json-comments": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-1.0.4.tgz", + "integrity": "sha1-HhX7ysl9Pumb8tc7TGVrCCu6+5E=", + "dev": true + } + } + }, + "jslint": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/jslint/-/jslint-0.12.0.tgz", + "integrity": "sha512-RoCsyICcKA+6TFsbys9DpKTfPVaC71Mm5QSjvrWA0lDVN+LIvx6apa42FFisMqmCTvJ8DxkcoQGJ0j7m3kTVow==", + "dev": true, + "requires": { + "exit": "~0.1.2", + "glob": "~7.1.2", + "nopt": "~3.0.1", + "readable-stream": "~2.1.5" + }, + "dependencies": { + "nopt": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-3.0.6.tgz", + "integrity": "sha1-xkZdvwirzU2zWTF/eaxopkayj/k=", + "dev": true, + "requires": { + "abbrev": "1" + } + }, + "process-nextick-args": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.7.tgz", + "integrity": "sha1-FQ4gt1ZZCtP5EJPyWk8q2L/zC6M=", + "dev": true + }, + "readable-stream": { + "version": "2.1.5", + "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-2.1.5.tgz", + "integrity": "sha1-ZvqLcg4UOLNkaB8q0aY8YYRIydA=", + "dev": true, + "requires": { + "buffer-shims": "^1.0.0", + "core-util-is": "~1.0.0", + "inherits": "~2.0.1", + "isarray": "~1.0.0", + "process-nextick-args": "~1.0.6", + "string_decoder": "~0.10.x", + "util-deprecate": "~1.0.1" + } + }, + "string_decoder": { + "version": "0.10.31", + "resolved": "http://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", + "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=", + "dev": true + } + } + }, + "json-int64": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-int64/-/json-int64-1.0.0.tgz", + "integrity": "sha512-yrTg9swToElhEPETLMdZkEzDhbXLs+cxkw/b2rglMPOBlM1DE0utH1EReSMLcnpYJk5iUvD12r0fP2/xHitF5Q==", + "requires": { + "node-int64": "0.4.0" + } + }, + "json-schema": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz", + "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=", + "dev": true + }, + "json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true + }, + "json-stable-stringify": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify/-/json-stable-stringify-0.0.1.tgz", + "integrity": "sha1-YRwj6BTbN1Un34URk9tZ3Sryf0U=", + "dev": true, + "requires": { + "jsonify": "~0.0.0" + } + }, + "json-stringify-safe": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", + "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=", + "dev": true + }, + "jsonfile": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-2.4.0.tgz", + "integrity": "sha1-NzaitCi4e72gzIO1P6PWM6NcKug=", + "dev": true, + "requires": { + "graceful-fs": "^4.1.6" + } + }, + "jsonify": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/jsonify/-/jsonify-0.0.0.tgz", + "integrity": "sha1-LHS27kHZPKUbe1qu6PUDYx0lKnM=", + "dev": true + }, + "jsonparse": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/jsonparse/-/jsonparse-1.3.1.tgz", + "integrity": "sha1-P02uSpH6wxX3EGL4UhzCOfE2YoA=", + "dev": true + }, + "jsprim": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz", + "integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=", + "dev": true, + "requires": { + "assert-plus": "1.0.0", + "extsprintf": "1.3.0", + "json-schema": "0.2.3", + "verror": "1.10.0" + } + }, + "kew": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/kew/-/kew-0.7.0.tgz", + "integrity": "sha1-edk9LTM2PW/dKXCzNdkUGtWR15s=", + "dev": true + }, + "kind-of": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", + "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==", + "dev": true + }, + "klaw": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/klaw/-/klaw-2.0.0.tgz", + "integrity": "sha1-WcEo4Nxc5BAgEVEZTuucv4WGUPY=", + "requires": { + "graceful-fs": "^4.1.9" + } + }, + "kuler": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/kuler/-/kuler-1.0.1.tgz", + "integrity": "sha512-J9nVUucG1p/skKul6DU3PUZrhs0LPulNaeUOox0IyXDi8S4CztTHs1gQphhuZmzXG7VOQSf6NJfKuzteQLv9gQ==", + "dev": true, + "requires": { + "colornames": "^1.1.1" + } + }, + "labeled-stream-splicer": { + "version": "2.0.1", + "resolved": "http://registry.npmjs.org/labeled-stream-splicer/-/labeled-stream-splicer-2.0.1.tgz", + "integrity": "sha512-MC94mHZRvJ3LfykJlTUipBqenZz1pacOZEMhhQ8dMGcDHs0SBE5GbsavUXV7YtP3icBW17W0Zy1I0lfASmo9Pg==", + "dev": true, + "requires": { + "inherits": "^2.0.1", + "isarray": "^2.0.4", + "stream-splicer": "^2.0.0" + }, + "dependencies": { + "isarray": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.4.tgz", + "integrity": "sha512-GMxXOiUirWg1xTKRipM0Ek07rX+ubx4nNVElTJdNLYmNO/2YrDkgJGw9CljXn+r4EWiDQg/8lsRdHyg2PJuUaA==", + "dev": true + } + } + }, + "lazy-cache": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/lazy-cache/-/lazy-cache-1.0.4.tgz", + "integrity": "sha1-odePw6UEdMuAhF07O24dpJpEbo4=", + "dev": true + }, + "liftoff": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/liftoff/-/liftoff-2.5.0.tgz", + "integrity": "sha1-IAkpG7Mc6oYbvxCnwVooyvdcMew=", + "dev": true, + "requires": { + "extend": "^3.0.0", + "findup-sync": "^2.0.0", + "fined": "^1.0.1", + "flagged-respawn": "^1.0.0", + "is-plain-object": "^2.0.4", + "object.map": "^1.0.0", + "rechoir": "^0.6.2", + "resolve": "^1.1.7" + }, + "dependencies": { + "findup-sync": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/findup-sync/-/findup-sync-2.0.0.tgz", + "integrity": "sha1-kyaxSIwi0aYIhlCoaQGy2akKLLw=", + "dev": true, + "requires": { + "detect-file": "^1.0.0", + "is-glob": "^3.1.0", + "micromatch": "^3.0.4", + "resolve-dir": "^1.0.1" + } + } + } + }, + "load-json-file": { + "version": "1.1.0", + "resolved": "http://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz", + "integrity": "sha1-lWkFcI1YtLq0wiYbBPWfMcmTdMA=", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "parse-json": "^2.2.0", + "pify": "^2.0.0", + "pinkie-promise": "^2.0.0", + "strip-bom": "^2.0.0" + } + }, + "lodash": { + "version": "4.17.11", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.11.tgz", + "integrity": "sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg==", + "dev": true + }, + "lodash.memoize": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-3.0.4.tgz", + "integrity": "sha1-LcvSwofLwKVcxCMovQxzYVDVPj8=", + "dev": true + }, + "logform": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/logform/-/logform-1.10.0.tgz", + "integrity": "sha512-em5ojIhU18fIMOw/333mD+ZLE2fis0EzXl1ZwHx4iQzmpQi6odNiY/t+ITNr33JZhT9/KEaH+UPIipr6a9EjWg==", + "dev": true, + "requires": { + "colors": "^1.2.1", + "fast-safe-stringify": "^2.0.4", + "fecha": "^2.3.3", + "ms": "^2.1.1", + "triple-beam": "^1.2.0" + }, + "dependencies": { + "colors": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/colors/-/colors-1.3.3.tgz", + "integrity": "sha512-mmGt/1pZqYRjMxB1axhTo16/snVZ5krrKkcmMeVKxzECMMXoCgnvTPp10QgHfcbQZw8Dq2jMNG6je4JlWU0gWg==", + "dev": true + }, + "ms": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", + "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==", + "dev": true + } + } + }, + "longest": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/longest/-/longest-1.0.1.tgz", + "integrity": "sha1-MKCy2jj3N3DoKUoNIuZiXtd9AJc=", + "dev": true + }, + "loud-rejection": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/loud-rejection/-/loud-rejection-1.6.0.tgz", + "integrity": "sha1-W0b4AUft7leIcPCG0Eghz5mOVR8=", + "dev": true, + "requires": { + "currently-unhandled": "^0.4.1", + "signal-exit": "^3.0.0" + } + }, + "make-iterator": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/make-iterator/-/make-iterator-1.0.1.tgz", + "integrity": "sha512-pxiuXh0iVEq7VM7KMIhs5gxsfxCux2URptUQaXo4iZZJxBAzTPOLE2BumO5dbfVYq/hBJFBR/a1mFDmOx5AGmw==", + "dev": true, + "requires": { + "kind-of": "^6.0.2" + } + }, + "map-cache": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz", + "integrity": "sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8=", + "dev": true + }, + "map-obj": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-1.0.1.tgz", + "integrity": "sha1-2TPOuSBdgr3PSIb2dCvcK03qFG0=", + "dev": true + }, + "map-visit": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/map-visit/-/map-visit-1.0.0.tgz", + "integrity": "sha1-7Nyo8TFE5mDxtb1B8S80edmN+48=", + "dev": true, + "requires": { + "object-visit": "^1.0.0" + } + }, + "marked": { + "version": "0.3.19", + "resolved": "https://registry.npmjs.org/marked/-/marked-0.3.19.tgz", + "integrity": "sha512-ea2eGWOqNxPcXv8dyERdSr/6FmzvWwzjMxpfGB/sbMccXoct+xY+YukPD+QTUZwyvK7BZwcr4m21WBOW41pAkg==" + }, + "maxmin": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/maxmin/-/maxmin-1.1.0.tgz", + "integrity": "sha1-cTZehKmd2Piz99X94vANHn9zvmE=", + "dev": true, + "requires": { + "chalk": "^1.0.0", + "figures": "^1.0.1", + "gzip-size": "^1.0.0", + "pretty-bytes": "^1.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", + "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", + "dev": true + }, + "chalk": { + "version": "1.1.3", + "resolved": "http://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", + "dev": true, + "requires": { + "ansi-styles": "^2.2.1", + "escape-string-regexp": "^1.0.2", + "has-ansi": "^2.0.0", + "strip-ansi": "^3.0.0", + "supports-color": "^2.0.0" + } + }, + "supports-color": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", + "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", + "dev": true + } + } + }, + "md5.js": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz", + "integrity": "sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg==", + "dev": true, + "requires": { + "hash-base": "^3.0.0", + "inherits": "^2.0.1", + "safe-buffer": "^5.1.2" + } + }, + "meow": { + "version": "3.7.0", + "resolved": "http://registry.npmjs.org/meow/-/meow-3.7.0.tgz", + "integrity": "sha1-cstmi0JSKCkKu/qFaJJYcwioAfs=", + "dev": true, + "requires": { + "camelcase-keys": "^2.0.0", + "decamelize": "^1.1.2", + "loud-rejection": "^1.0.0", + "map-obj": "^1.0.1", + "minimist": "^1.1.3", + "normalize-package-data": "^2.3.4", + "object-assign": "^4.0.1", + "read-pkg-up": "^1.0.1", + "redent": "^1.0.0", + "trim-newlines": "^1.0.0" + }, + "dependencies": { + "minimist": { + "version": "1.2.0", + "resolved": "http://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", + "dev": true + } + } + }, + "micromatch": { + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", + "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", + "dev": true, + "requires": { + "arr-diff": "^4.0.0", + "array-unique": "^0.3.2", + "braces": "^2.3.1", + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "extglob": "^2.0.4", + "fragment-cache": "^0.2.1", + "kind-of": "^6.0.2", + "nanomatch": "^1.2.9", + "object.pick": "^1.3.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.2" + } + }, + "miller-rabin": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/miller-rabin/-/miller-rabin-4.0.1.tgz", + "integrity": "sha512-115fLhvZVqWwHPbClyntxEVfVDfl9DLLTuJvq3g2O/Oxi8AiNouAHvDSzHS0viUJc+V5vm3eq91Xwqn9dp4jRA==", + "dev": true, + "requires": { + "bn.js": "^4.0.0", + "brorand": "^1.0.1" + } + }, + "mime": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-2.4.0.tgz", + "integrity": "sha512-ikBcWwyqXQSHKtciCcctu9YfPbFYZ4+gbHEmE0Q8jzcTYQg5dHCr3g2wwAZjPoJfQVXZq6KXAjpXOTf5/cjT7w==", + "dev": true + }, + "mime-db": { + "version": "1.37.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.37.0.tgz", + "integrity": "sha512-R3C4db6bgQhlIhPU48fUtdVmKnflq+hRdad7IyKhtFj06VPNVdk2RhiYL3UjQIlso8L+YxAtFkobT0VK+S/ybg==", + "dev": true + }, + "mime-types": { + "version": "2.1.21", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.21.tgz", + "integrity": "sha512-3iL6DbwpyLzjR3xHSFNFeb9Nz/M8WDkX33t1GFQnFOllWk8pOrh/LSrB5OXlnlW5P9LH73X6loW/eogc+F5lJg==", + "dev": true, + "requires": { + "mime-db": "~1.37.0" + } + }, + "minimalistic-assert": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", + "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==", + "dev": true + }, + "minimalistic-crypto-utils": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz", + "integrity": "sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo=", + "dev": true + }, + "minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "dev": true, + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "minimist": { + "version": "0.0.8", + "resolved": "http://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", + "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=" + }, + "mixin-deep": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.1.tgz", + "integrity": "sha512-8ZItLHeEgaqEvd5lYBXfm4EZSFCX29Jb9K+lAHhDKzReKBQKj3R+7NOF6tjqYi9t4oI8VUfaWITJQm86wnXGNQ==", + "dev": true, + "requires": { + "for-in": "^1.0.2", + "is-extendable": "^1.0.1" + }, + "dependencies": { + "is-extendable": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", + "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", + "dev": true, + "requires": { + "is-plain-object": "^2.0.4" + } + } + } + }, + "mkdirp": { + "version": "0.5.1", + "resolved": "http://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", + "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", + "requires": { + "minimist": "0.0.8" + } + }, + "module-deps": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/module-deps/-/module-deps-6.2.0.tgz", + "integrity": "sha512-hKPmO06so6bL/ZvqVNVqdTVO8UAYsi3tQWlCa+z9KuWhoN4KDQtb5hcqQQv58qYiDE21wIvnttZEPiDgEbpwbA==", + "dev": true, + "requires": { + "JSONStream": "^1.0.3", + "browser-resolve": "^1.7.0", + "cached-path-relative": "^1.0.0", + "concat-stream": "~1.6.0", + "defined": "^1.0.0", + "detective": "^5.0.2", + "duplexer2": "^0.1.2", + "inherits": "^2.0.1", + "parents": "^1.0.0", + "readable-stream": "^2.0.2", + "resolve": "^1.4.0", + "stream-combiner2": "^1.1.1", + "subarg": "^1.0.0", + "through2": "^2.0.0", + "xtend": "^4.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + }, + "nanomatch": { + "version": "1.2.13", + "resolved": "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.13.tgz", + "integrity": "sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA==", + "dev": true, + "requires": { + "arr-diff": "^4.0.0", + "array-unique": "^0.3.2", + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "fragment-cache": "^0.2.1", + "is-windows": "^1.0.2", + "kind-of": "^6.0.2", + "object.pick": "^1.3.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" + } + }, + "nice-try": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", + "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==", + "dev": true + }, + "node-gyp-build": { + "version": "3.7.0", + "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-3.7.0.tgz", + "integrity": "sha512-L/Eg02Epx6Si2NXmedx+Okg+4UHqmaf3TNcxd50SF9NQGcJaON3AtU++kax69XV7YWz4tUspqZSAsVofhFKG2w==" + }, + "node-int64": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", + "integrity": "sha1-h6kGXNs1XTGC2PlM4RGIuCXGijs=" + }, + "nopt": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-4.0.1.tgz", + "integrity": "sha1-0NRoWv1UFRk8jHUFYC0NF81kR00=", + "requires": { + "abbrev": "1", + "osenv": "^0.1.4" + } + }, + "normalize-package-data": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.4.0.tgz", + "integrity": "sha512-9jjUFbTPfEy3R/ad/2oNbKtW9Hgovl5O1FvFWKkKblNXoN/Oou6+9+KKohPK13Yc3/TyunyWhJp6gvRNR/PPAw==", + "dev": true, + "requires": { + "hosted-git-info": "^2.1.4", + "is-builtin-module": "^1.0.0", + "semver": "2 || 3 || 4 || 5", + "validate-npm-package-license": "^3.0.1" + } + }, + "number-is-nan": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", + "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=", + "dev": true + }, + "oauth-sign": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", + "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==", + "dev": true + }, + "object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", + "dev": true + }, + "object-copy": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/object-copy/-/object-copy-0.1.0.tgz", + "integrity": "sha1-fn2Fi3gb18mRpBupde04EnVOmYw=", + "dev": true, + "requires": { + "copy-descriptor": "^0.1.0", + "define-property": "^0.2.5", + "kind-of": "^3.0.3" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "requires": { + "is-descriptor": "^0.1.0" + } + }, + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "object-visit": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/object-visit/-/object-visit-1.0.1.tgz", + "integrity": "sha1-95xEk68MU3e1n+OdOV5BBC3QRbs=", + "dev": true, + "requires": { + "isobject": "^3.0.0" + } + }, + "object.defaults": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/object.defaults/-/object.defaults-1.1.0.tgz", + "integrity": "sha1-On+GgzS0B96gbaFtiNXNKeQ1/s8=", + "dev": true, + "requires": { + "array-each": "^1.0.1", + "array-slice": "^1.0.0", + "for-own": "^1.0.0", + "isobject": "^3.0.0" + } + }, + "object.map": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/object.map/-/object.map-1.0.1.tgz", + "integrity": "sha1-z4Plncj8wK1fQlDh94s7gb2AHTc=", + "dev": true, + "requires": { + "for-own": "^1.0.0", + "make-iterator": "^1.0.0" + } + }, + "object.pick": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/object.pick/-/object.pick-1.3.0.tgz", + "integrity": "sha1-h6EKxMFpS9Lhy/U1kaZhQftd10c=", + "dev": true, + "requires": { + "isobject": "^3.0.1" + } + }, + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "dev": true, + "requires": { + "wrappy": "1" + } + }, + "one-time": { + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/one-time/-/one-time-0.0.4.tgz", + "integrity": "sha1-+M33eISCb+Tf+T46nMN7HkSAdC4=", + "dev": true + }, + "os-browserify": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/os-browserify/-/os-browserify-0.3.0.tgz", + "integrity": "sha1-hUNzx/XCMVkU/Jv8a9gjj92h7Cc=", + "dev": true + }, + "os-homedir": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz", + "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=" + }, + "os-tmpdir": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", + "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=" + }, + "osenv": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/osenv/-/osenv-0.1.5.tgz", + "integrity": "sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g==", + "requires": { + "os-homedir": "^1.0.0", + "os-tmpdir": "^1.0.0" + } + }, + "p-each-series": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-each-series/-/p-each-series-1.0.0.tgz", + "integrity": "sha1-kw89Et0fUOdDRFeiLNbwSsatf3E=", + "dev": true, + "requires": { + "p-reduce": "^1.0.0" + } + }, + "p-reduce": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-reduce/-/p-reduce-1.0.0.tgz", + "integrity": "sha1-GMKw3ZNqRpClKfgjH1ig/bakffo=", + "dev": true + }, + "pako": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.7.tgz", + "integrity": "sha512-3HNK5tW4x8o5mO8RuHZp3Ydw9icZXx0RANAOMzlMzx7LVXhMJ4mo3MOBpzyd7r/+RUu8BmndP47LXT+vzjtWcQ==", + "dev": true + }, + "parents": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parents/-/parents-1.0.1.tgz", + "integrity": "sha1-/t1NK/GTp3dF/nHjcdc8MwfZx1E=", + "dev": true, + "requires": { + "path-platform": "~0.11.15" + } + }, + "parse-asn1": { + "version": "5.1.1", + "resolved": "http://registry.npmjs.org/parse-asn1/-/parse-asn1-5.1.1.tgz", + "integrity": "sha512-KPx7flKXg775zZpnp9SxJlz00gTd4BmJ2yJufSc44gMCRrRQ7NSzAcSJQfifuOLgW6bEi+ftrALtsgALeB2Adw==", + "dev": true, + "requires": { + "asn1.js": "^4.0.0", + "browserify-aes": "^1.0.0", + "create-hash": "^1.1.0", + "evp_bytestokey": "^1.0.0", + "pbkdf2": "^3.0.3" + } + }, + "parse-filepath": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/parse-filepath/-/parse-filepath-1.0.2.tgz", + "integrity": "sha1-pjISf1Oq89FYdvWHLz/6x2PWyJE=", + "dev": true, + "requires": { + "is-absolute": "^1.0.0", + "map-cache": "^0.2.0", + "path-root": "^0.1.1" + } + }, + "parse-json": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz", + "integrity": "sha1-9ID0BDTvgHQfhGkJn43qGPVaTck=", + "dev": true, + "requires": { + "error-ex": "^1.2.0" + } + }, + "parse-passwd": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/parse-passwd/-/parse-passwd-1.0.0.tgz", + "integrity": "sha1-bVuTSkVpk7I9N/QKOC1vFmao5cY=", + "dev": true + }, + "pascalcase": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/pascalcase/-/pascalcase-0.1.1.tgz", + "integrity": "sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ=", + "dev": true + }, + "path-browserify": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-0.0.1.tgz", + "integrity": "sha512-BapA40NHICOS+USX9SN4tyhq+A2RrN/Ws5F0Z5aMHDp98Fl86lX8Oti8B7uN93L4Ifv4fHOEA+pQw87gmMO/lQ==", + "dev": true + }, + "path-exists": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-2.1.0.tgz", + "integrity": "sha1-D+tsZPD8UY2adU3V77YscCJ2H0s=", + "dev": true, + "requires": { + "pinkie-promise": "^2.0.0" + } + }, + "path-is-absolute": { + "version": "1.0.1", + "resolved": "http://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", + "dev": true + }, + "path-key": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", + "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=", + "dev": true + }, + "path-parse": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz", + "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==", + "dev": true + }, + "path-platform": { + "version": "0.11.15", + "resolved": "https://registry.npmjs.org/path-platform/-/path-platform-0.11.15.tgz", + "integrity": "sha1-6GQhf3TDaFDwhSt43Hv31KVyG/I=", + "dev": true + }, + "path-root": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/path-root/-/path-root-0.1.1.tgz", + "integrity": "sha1-mkpoFMrBwM1zNgqV8yCDyOpHRbc=", + "dev": true, + "requires": { + "path-root-regex": "^0.1.0" + } + }, + "path-root-regex": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/path-root-regex/-/path-root-regex-0.1.2.tgz", + "integrity": "sha1-v8zcjfWxLcUsi0PsONGNcsBLqW0=", + "dev": true + }, + "path-type": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-1.1.0.tgz", + "integrity": "sha1-WcRPfuSR2nBNpBXaWkBwuk+P5EE=", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "pify": "^2.0.0", + "pinkie-promise": "^2.0.0" + } + }, + "pbkdf2": { + "version": "3.0.17", + "resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.0.17.tgz", + "integrity": "sha512-U/il5MsrZp7mGg3mSQfn742na2T+1/vHDCG5/iTI3X9MKUuYUZVLQhyRsg06mCgDBTd57TxzgZt7P+fYfjRLtA==", + "dev": true, + "requires": { + "create-hash": "^1.1.2", + "create-hmac": "^1.1.4", + "ripemd160": "^2.0.1", + "safe-buffer": "^5.0.1", + "sha.js": "^2.4.8" + } + }, + "pend": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", + "integrity": "sha1-elfrVQpng/kRUzH89GY9XI4AelA=", + "dev": true + }, + "performance-now": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", + "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=", + "dev": true + }, + "phantom": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/phantom/-/phantom-6.0.3.tgz", + "integrity": "sha512-8bb8urWoUiZ0E+JC4goaYBDPxljTnnxGwogz5cvash2SQovf//QAPoshXQz06kY/tpI+5caBVng0K0oZkVMNIQ==", + "dev": true, + "requires": { + "phantomjs-prebuilt": "^2.1.16", + "split": "^1.0.1", + "winston": "^3.0.0" + } + }, + "phantomjs-prebuilt": { + "version": "2.1.16", + "resolved": "https://registry.npmjs.org/phantomjs-prebuilt/-/phantomjs-prebuilt-2.1.16.tgz", + "integrity": "sha1-79ISpKOWbTZHaE6ouniFSb4q7+8=", + "dev": true, + "requires": { + "es6-promise": "^4.0.3", + "extract-zip": "^1.6.5", + "fs-extra": "^1.0.0", + "hasha": "^2.2.0", + "kew": "^0.7.0", + "progress": "^1.1.8", + "request": "^2.81.0", + "request-progress": "^2.0.1", + "which": "^1.2.10" + }, + "dependencies": { + "progress": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/progress/-/progress-1.1.8.tgz", + "integrity": "sha1-4mDHj2Fhzdmw5WzD4Khd4Xx6V74=", + "dev": true + } + } + }, + "pify": { + "version": "2.3.0", + "resolved": "http://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", + "dev": true + }, + "pinkie": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz", + "integrity": "sha1-clVrgM+g1IqXToDnckjoDtT3+HA=", + "dev": true + }, + "pinkie-promise": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz", + "integrity": "sha1-ITXW36ejWMBprJsXh3YogihFD/o=", + "dev": true, + "requires": { + "pinkie": "^2.0.0" + } + }, + "posix-character-classes": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/posix-character-classes/-/posix-character-classes-0.1.1.tgz", + "integrity": "sha1-AerA/jta9xoqbAL+q7jB/vfgDqs=", + "dev": true + }, + "pretty-bytes": { + "version": "1.0.4", + "resolved": "http://registry.npmjs.org/pretty-bytes/-/pretty-bytes-1.0.4.tgz", + "integrity": "sha1-CiLoIQYJrTVUL4yNXSFZr/B1HIQ=", + "dev": true, + "requires": { + "get-stdin": "^4.0.1", + "meow": "^3.1.0" + } + }, + "process": { + "version": "0.11.10", + "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", + "integrity": "sha1-czIwDoQBYb2j5podHZGn1LwW8YI=", + "dev": true + }, + "process-nextick-args": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz", + "integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw==", + "dev": true + }, + "progress": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", + "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", + "dev": true + }, + "proxy-from-env": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.0.0.tgz", + "integrity": "sha1-M8UDmPcOp+uW0h97gXYwpVeRx+4=", + "dev": true + }, + "psl": { + "version": "1.1.31", + "resolved": "https://registry.npmjs.org/psl/-/psl-1.1.31.tgz", + "integrity": "sha512-/6pt4+C+T+wZUieKR620OpzN/LlnNKuWjy1iFLQ/UG35JqHlR/89MP1d96dUfkf6Dne3TuLQzOYEYshJ+Hx8mw==", + "dev": true + }, + "public-encrypt": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/public-encrypt/-/public-encrypt-4.0.3.tgz", + "integrity": "sha512-zVpa8oKZSz5bTMTFClc1fQOnyyEzpl5ozpi1B5YcvBrdohMjH2rfsBtyXcuNuwjsDIXmBYlF2N5FlJYhR29t8Q==", + "dev": true, + "requires": { + "bn.js": "^4.1.0", + "browserify-rsa": "^4.0.0", + "create-hash": "^1.1.0", + "parse-asn1": "^5.0.0", + "randombytes": "^2.0.1", + "safe-buffer": "^5.1.2" + } + }, + "punycode": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", + "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=", + "dev": true + }, + "puppeteer": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/puppeteer/-/puppeteer-1.11.0.tgz", + "integrity": "sha512-iG4iMOHixc2EpzqRV+pv7o3GgmU2dNYEMkvKwSaQO/vMZURakwSOn/EYJ6OIRFYOque1qorzIBvrytPIQB3YzQ==", + "dev": true, + "requires": { + "debug": "^4.1.0", + "extract-zip": "^1.6.6", + "https-proxy-agent": "^2.2.1", + "mime": "^2.0.3", + "progress": "^2.0.1", + "proxy-from-env": "^1.0.0", + "rimraf": "^2.6.1", + "ws": "^6.1.0" + }, + "dependencies": { + "debug": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", + "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + }, + "ms": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", + "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==", + "dev": true + } + } + }, + "qs": { + "version": "6.5.2", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", + "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==", + "dev": true + }, + "querystring": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz", + "integrity": "sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA=", + "dev": true + }, + "querystring-es3": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/querystring-es3/-/querystring-es3-0.2.1.tgz", + "integrity": "sha1-nsYfeQSYdXB9aUFFlv2Qek1xHnM=", + "dev": true + }, + "randombytes": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.0.6.tgz", + "integrity": "sha512-CIQ5OFxf4Jou6uOKe9t1AOgqpeU5fd70A8NPdHSGeYXqXsPe6peOwI0cUl88RWZ6sP1vPMV3avd/R6cZ5/sP1A==", + "dev": true, + "requires": { + "safe-buffer": "^5.1.0" + } + }, + "randomfill": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/randomfill/-/randomfill-1.0.4.tgz", + "integrity": "sha512-87lcbR8+MhcWcUiQ+9e+Rwx8MyR2P7qnt15ynUlbm3TU/fjbgz4GsvfSUDTemtCCtVCqb4ZcEFlyPNTh9bBTLw==", + "dev": true, + "requires": { + "randombytes": "^2.0.5", + "safe-buffer": "^5.1.0" + } + }, + "read-only-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/read-only-stream/-/read-only-stream-2.0.0.tgz", + "integrity": "sha1-JyT9aoET1zdkrCiNQ4YnDB2/F/A=", + "dev": true, + "requires": { + "readable-stream": "^2.0.2" + } + }, + "read-pkg": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-1.1.0.tgz", + "integrity": "sha1-9f+qXs0pyzHAR0vKfXVra7KePyg=", + "dev": true, + "requires": { + "load-json-file": "^1.0.0", + "normalize-package-data": "^2.3.2", + "path-type": "^1.0.0" + } + }, + "read-pkg-up": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-1.0.1.tgz", + "integrity": "sha1-nWPBMnbAZZGNV/ACpX9AobZD+wI=", + "dev": true, + "requires": { + "find-up": "^1.0.0", + "read-pkg": "^1.0.0" + } + }, + "readable-stream": { + "version": "2.3.6", + "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", + "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", + "dev": true, + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + }, + "dependencies": { + "string_decoder": { + "version": "1.1.1", + "resolved": "http://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "requires": { + "safe-buffer": "~5.1.0" + } + } + } + }, + "rechoir": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.6.2.tgz", + "integrity": "sha1-hSBLVNuoLVdC4oyWdW70OvUOM4Q=", + "dev": true, + "requires": { + "resolve": "^1.1.6" + } + }, + "redent": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/redent/-/redent-1.0.0.tgz", + "integrity": "sha1-z5Fqsf1fHxbfsggi3W7H9zDCr94=", + "dev": true, + "requires": { + "indent-string": "^2.1.0", + "strip-indent": "^1.0.1" + } + }, + "regex-not": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/regex-not/-/regex-not-1.0.2.tgz", + "integrity": "sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A==", + "dev": true, + "requires": { + "extend-shallow": "^3.0.2", + "safe-regex": "^1.1.0" + } + }, + "repeat-element": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.3.tgz", + "integrity": "sha512-ahGq0ZnV5m5XtZLMb+vP76kcAM5nkLqk0lpqAuojSKGgQtn4eRi4ZZGm2olo2zKFH+sMsWaqOCW1dqAnOru72g==", + "dev": true + }, + "repeat-string": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz", + "integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc=", + "dev": true + }, + "repeating": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/repeating/-/repeating-2.0.1.tgz", + "integrity": "sha1-UhTFOpJtNVJwdSf7q0FdvAjQbdo=", + "dev": true, + "requires": { + "is-finite": "^1.0.0" + } + }, + "request": { + "version": "2.88.0", + "resolved": "https://registry.npmjs.org/request/-/request-2.88.0.tgz", + "integrity": "sha512-NAqBSrijGLZdM0WZNsInLJpkJokL72XYjUpnB0iwsRgxh7dB6COrHnTBNwN0E+lHDAJzu7kLAkDeY08z2/A0hg==", + "dev": true, + "requires": { + "aws-sign2": "~0.7.0", + "aws4": "^1.8.0", + "caseless": "~0.12.0", + "combined-stream": "~1.0.6", + "extend": "~3.0.2", + "forever-agent": "~0.6.1", + "form-data": "~2.3.2", + "har-validator": "~5.1.0", + "http-signature": "~1.2.0", + "is-typedarray": "~1.0.0", + "isstream": "~0.1.2", + "json-stringify-safe": "~5.0.1", + "mime-types": "~2.1.19", + "oauth-sign": "~0.9.0", + "performance-now": "^2.1.0", + "qs": "~6.5.2", + "safe-buffer": "^5.1.2", + "tough-cookie": "~2.4.3", + "tunnel-agent": "^0.6.0", + "uuid": "^3.3.2" + } + }, + "request-progress": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/request-progress/-/request-progress-2.0.1.tgz", + "integrity": "sha1-XTa7V5YcZzqlt4jbyBQf3yO0Tgg=", + "dev": true, + "requires": { + "throttleit": "^1.0.0" + } + }, + "requizzle": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/requizzle/-/requizzle-0.2.1.tgz", + "integrity": "sha1-aUPDUwxNmn5G8c3dUcFY/GcM294=", + "requires": { + "underscore": "~1.6.0" + }, + "dependencies": { + "underscore": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.6.0.tgz", + "integrity": "sha1-izixDKze9jM3uLJOT/htRa6lKag=" + } + } + }, + "resolve": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.9.0.tgz", + "integrity": "sha512-TZNye00tI67lwYvzxCxHGjwTNlUV70io54/Ed4j6PscB8xVfuBJpRenI/o6dVk0cY0PYTY27AgCoGGxRnYuItQ==", + "dev": true, + "requires": { + "path-parse": "^1.0.6" + } + }, + "resolve-dir": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/resolve-dir/-/resolve-dir-1.0.1.tgz", + "integrity": "sha1-eaQGRMNivoLybv/nOcm7U4IEb0M=", + "dev": true, + "requires": { + "expand-tilde": "^2.0.0", + "global-modules": "^1.0.0" + } + }, + "resolve-url": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz", + "integrity": "sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo=", + "dev": true + }, + "ret": { + "version": "0.1.15", + "resolved": "https://registry.npmjs.org/ret/-/ret-0.1.15.tgz", + "integrity": "sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==", + "dev": true + }, + "right-align": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/right-align/-/right-align-0.1.3.tgz", + "integrity": "sha1-YTObci/mo1FWiSENJOFMlhSGE+8=", + "dev": true, + "requires": { + "align-text": "^0.1.1" + } + }, + "rimraf": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.2.tgz", + "integrity": "sha512-lreewLK/BlghmxtfH36YYVg1i8IAce4TI7oao75I1g245+6BctqTVQiBP3YUJ9C6DQOXJmkYR9X9fCLtCOJc5w==", + "dev": true, + "requires": { + "glob": "^7.0.5" + } + }, + "ripemd160": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/ripemd160/-/ripemd160-2.0.2.tgz", + "integrity": "sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA==", + "dev": true, + "requires": { + "hash-base": "^3.0.0", + "inherits": "^2.0.1" + } + }, + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true + }, + "safe-regex": { + "version": "1.1.0", + "resolved": "http://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz", + "integrity": "sha1-QKNmnzsHfR6UPURinhV91IAjvy4=", + "dev": true, + "requires": { + "ret": "~0.1.10" + } + }, + "safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "dev": true + }, + "semver": { + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.6.0.tgz", + "integrity": "sha512-RS9R6R35NYgQn++fkDWaOmqGoj4Ek9gGs+DPxNUZKuwE183xjJroKvyo1IzVFeXvUrvmALy6FWD5xrdJT25gMg==", + "dev": true + }, + "set-value": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.0.tgz", + "integrity": "sha512-hw0yxk9GT/Hr5yJEYnHNKYXkIA8mVJgd9ditYZCe16ZczcaELYYcfvaXesNACk2O8O0nTiPQcQhGUQj8JLzeeg==", + "dev": true, + "requires": { + "extend-shallow": "^2.0.1", + "is-extendable": "^0.1.1", + "is-plain-object": "^2.0.3", + "split-string": "^3.0.1" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "sha.js": { + "version": "2.4.11", + "resolved": "http://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz", + "integrity": "sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==", + "dev": true, + "requires": { + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "shasum": { + "version": "1.0.2", + "resolved": "http://registry.npmjs.org/shasum/-/shasum-1.0.2.tgz", + "integrity": "sha1-5wEjENj0F/TetXEhUOVni4euVl8=", + "dev": true, + "requires": { + "json-stable-stringify": "~0.0.0", + "sha.js": "~2.4.4" + } + }, + "shebang-command": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", + "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=", + "dev": true, + "requires": { + "shebang-regex": "^1.0.0" + } + }, + "shebang-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", + "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=", + "dev": true + }, + "shell-quote": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.6.1.tgz", + "integrity": "sha1-9HgZSczkAmlxJ0MOo7PFR29IF2c=", + "dev": true, + "requires": { + "array-filter": "~0.0.0", + "array-map": "~0.0.0", + "array-reduce": "~0.0.0", + "jsonify": "~0.0.0" + } + }, + "shelljs": { + "version": "0.3.0", + "resolved": "http://registry.npmjs.org/shelljs/-/shelljs-0.3.0.tgz", + "integrity": "sha1-NZbmMHp4FUT1kfN9phg2DzHbV7E=", + "dev": true + }, + "signal-exit": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", + "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=", + "dev": true + }, + "simple-concat": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.0.tgz", + "integrity": "sha1-c0TLuLbib7J9ZrL8hvn21Zl1IcY=", + "dev": true + }, + "simple-swizzle": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz", + "integrity": "sha1-pNprY1/8zMoz9w0Xy5JZLeleVXo=", + "dev": true, + "requires": { + "is-arrayish": "^0.3.1" + }, + "dependencies": { + "is-arrayish": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz", + "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==", + "dev": true + } + } + }, + "snapdragon": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz", + "integrity": "sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg==", + "dev": true, + "requires": { + "base": "^0.11.1", + "debug": "^2.2.0", + "define-property": "^0.2.5", + "extend-shallow": "^2.0.1", + "map-cache": "^0.2.2", + "source-map": "^0.5.6", + "source-map-resolve": "^0.5.0", + "use": "^3.1.0" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "requires": { + "is-descriptor": "^0.1.0" + } + }, + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "snapdragon-node": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/snapdragon-node/-/snapdragon-node-2.1.1.tgz", + "integrity": "sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw==", + "dev": true, + "requires": { + "define-property": "^1.0.0", + "isobject": "^3.0.0", + "snapdragon-util": "^3.0.1" + }, + "dependencies": { + "define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", + "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", + "dev": true, + "requires": { + "is-descriptor": "^1.0.0" + } + }, + "is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "dev": true, + "requires": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + } + } + } + }, + "snapdragon-util": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/snapdragon-util/-/snapdragon-util-3.0.1.tgz", + "integrity": "sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ==", + "dev": true, + "requires": { + "kind-of": "^3.2.0" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "dev": true + }, + "source-map-resolve": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.5.2.tgz", + "integrity": "sha512-MjqsvNwyz1s0k81Goz/9vRBe9SZdB09Bdw+/zYyO+3CuPk6fouTaxscHkgtE8jKvf01kVfl8riHzERQ/kefaSA==", + "dev": true, + "requires": { + "atob": "^2.1.1", + "decode-uri-component": "^0.2.0", + "resolve-url": "^0.2.1", + "source-map-url": "^0.4.0", + "urix": "^0.1.0" + } + }, + "source-map-url": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/source-map-url/-/source-map-url-0.4.0.tgz", + "integrity": "sha1-PpNdfd1zYxuXZZlW1VEo6HtQhKM=", + "dev": true + }, + "spdx-correct": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.0.tgz", + "integrity": "sha512-lr2EZCctC2BNR7j7WzJ2FpDznxky1sjfxvvYEyzxNyb6lZXHODmEoJeFu4JupYlkfha1KZpJyoqiJ7pgA1qq8Q==", + "dev": true, + "requires": { + "spdx-expression-parse": "^3.0.0", + "spdx-license-ids": "^3.0.0" + } + }, + "spdx-exceptions": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.2.0.tgz", + "integrity": "sha512-2XQACfElKi9SlVb1CYadKDXvoajPgBVPn/gOQLrTvHdElaVhr7ZEbqJaRnJLVNeaI4cMEAgVCeBMKF6MWRDCRA==", + "dev": true + }, + "spdx-expression-parse": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.0.tgz", + "integrity": "sha512-Yg6D3XpRD4kkOmTpdgbUiEJFKghJH03fiC1OPll5h/0sO6neh2jqRDVHOQ4o/LMea0tgCkbMgea5ip/e+MkWyg==", + "dev": true, + "requires": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "spdx-license-ids": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.2.tgz", + "integrity": "sha512-qky9CVt0lVIECkEsYbNILVnPvycuEBkXoMFLRWsREkomQLevYhtRKC+R91a5TOAQ3bCMjikRwhyaRqj1VYatYg==", + "dev": true + }, + "split": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/split/-/split-1.0.1.tgz", + "integrity": "sha512-mTyOoPbrivtXnwnIxZRFYRrPNtEFKlpB2fvjSnCQUiAA6qAZzqwna5envK4uk6OIeP17CsdF3rSBGYVBsU0Tkg==", + "dev": true, + "requires": { + "through": "2" + } + }, + "split-string": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/split-string/-/split-string-3.1.0.tgz", + "integrity": "sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw==", + "dev": true, + "requires": { + "extend-shallow": "^3.0.0" + } + }, + "sprintf-js": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.2.tgz", + "integrity": "sha512-VE0SOVEHCk7Qc8ulkWw3ntAzXuqf7S2lvwQaDLRnUeIEaKNQJzV6BwmLKhOqT61aGhfUMrXeaBk+oDGCzvhcug==", + "dev": true + }, + "sshpk": { + "version": "1.16.0", + "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.16.0.tgz", + "integrity": "sha512-Zhev35/y7hRMcID/upReIvRse+I9SVhyVre/KTJSJQWMz3C3+G+HpO7m1wK/yckEtujKZ7dS4hkVxAnmHaIGVQ==", + "dev": true, + "requires": { + "asn1": "~0.2.3", + "assert-plus": "^1.0.0", + "bcrypt-pbkdf": "^1.0.0", + "dashdash": "^1.12.0", + "ecc-jsbn": "~0.1.1", + "getpass": "^0.1.1", + "jsbn": "~0.1.0", + "safer-buffer": "^2.0.2", + "tweetnacl": "~0.14.0" + } + }, + "stack-trace": { + "version": "0.0.10", + "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.10.tgz", + "integrity": "sha1-VHxws0fo0ytOEI6hoqFZ5f3eGcA=", + "dev": true + }, + "static-extend": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/static-extend/-/static-extend-0.1.2.tgz", + "integrity": "sha1-YICcOcv/VTNyJv1eC1IPNB8ftcY=", + "dev": true, + "requires": { + "define-property": "^0.2.5", + "object-copy": "^0.1.0" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "requires": { + "is-descriptor": "^0.1.0" + } + } + } + }, + "stream-browserify": { + "version": "2.0.1", + "resolved": "http://registry.npmjs.org/stream-browserify/-/stream-browserify-2.0.1.tgz", + "integrity": "sha1-ZiZu5fm9uZQKTkUUyvtDu3Hlyds=", + "dev": true, + "requires": { + "inherits": "~2.0.1", + "readable-stream": "^2.0.2" + } + }, + "stream-combiner2": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/stream-combiner2/-/stream-combiner2-1.1.1.tgz", + "integrity": "sha1-+02KFCDqNidk4hrUeAOXvry0HL4=", + "dev": true, + "requires": { + "duplexer2": "~0.1.0", + "readable-stream": "^2.0.2" + } + }, + "stream-http": { + "version": "2.8.3", + "resolved": "https://registry.npmjs.org/stream-http/-/stream-http-2.8.3.tgz", + "integrity": "sha512-+TSkfINHDo4J+ZobQLWiMouQYB+UVYFttRA94FpEzzJ7ZdqcL4uUUQ7WkdkI4DSozGmgBUE/a47L+38PenXhUw==", + "dev": true, + "requires": { + "builtin-status-codes": "^3.0.0", + "inherits": "^2.0.1", + "readable-stream": "^2.3.6", + "to-arraybuffer": "^1.0.0", + "xtend": "^4.0.0" + } + }, + "stream-splicer": { + "version": "2.0.0", + "resolved": "http://registry.npmjs.org/stream-splicer/-/stream-splicer-2.0.0.tgz", + "integrity": "sha1-G2O+Q4oTPktnHMGTUZdgAXWRDYM=", + "dev": true, + "requires": { + "inherits": "^2.0.1", + "readable-stream": "^2.0.2" + } + }, + "string_decoder": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.2.0.tgz", + "integrity": "sha512-6YqyX6ZWEYguAxgZzHGL7SsCeGx3V2TtOTqZz1xSTSWnqsbWwbptafNyvf/ACquZUXV3DANr5BDIwNYe1mN42w==", + "dev": true, + "requires": { + "safe-buffer": "~5.1.0" + } + }, + "strip-ansi": { + "version": "3.0.1", + "resolved": "http://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "dev": true, + "requires": { + "ansi-regex": "^2.0.0" + } + }, + "strip-bom": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz", + "integrity": "sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4=", + "dev": true, + "requires": { + "is-utf8": "^0.2.0" + } + }, + "strip-indent": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-1.0.1.tgz", + "integrity": "sha1-DHlipq3vp7vUrDZkYKY4VSrhoKI=", + "dev": true, + "requires": { + "get-stdin": "^4.0.1" + } + }, + "strip-json-comments": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", + "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=" + }, + "subarg": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/subarg/-/subarg-1.0.0.tgz", + "integrity": "sha1-9izxdYHplrSPyWVpn1TAauJouNI=", + "dev": true, + "requires": { + "minimist": "^1.1.0" + }, + "dependencies": { + "minimist": { + "version": "1.2.0", + "resolved": "http://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", + "dev": true + } + } + }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + }, + "syntax-error": { + "version": "1.4.0", + "resolved": "http://registry.npmjs.org/syntax-error/-/syntax-error-1.4.0.tgz", + "integrity": "sha512-YPPlu67mdnHGTup2A8ff7BC2Pjq0e0Yp/IyTFN03zWO0RcK07uLcbi7C2KpGR2FvWbaB0+bfE27a+sBKebSo7w==", + "dev": true, + "requires": { + "acorn-node": "^1.2.0" + } + }, + "taffydb": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/taffydb/-/taffydb-2.6.2.tgz", + "integrity": "sha1-fLy2S1oUG2ou/CxdLGe04VCyomg=" + }, + "text-hex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/text-hex/-/text-hex-1.0.0.tgz", + "integrity": "sha512-uuVGNWzgJ4yhRaNSiubPY7OjISw4sw4E5Uv0wbjp+OzcbmVU/rsT8ujgcXJhn9ypzsgr5vlzpPqP+MBBKcGvbg==", + "dev": true + }, + "throttleit": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/throttleit/-/throttleit-1.0.0.tgz", + "integrity": "sha1-nnhYNtr0Z0MUWlmEtiaNgoUorGw=", + "dev": true + }, + "through": { + "version": "2.3.8", + "resolved": "http://registry.npmjs.org/through/-/through-2.3.8.tgz", + "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=", + "dev": true + }, + "through2": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", + "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==", + "dev": true, + "requires": { + "readable-stream": "~2.3.6", + "xtend": "~4.0.1" + } + }, + "timers-browserify": { + "version": "1.4.2", + "resolved": "http://registry.npmjs.org/timers-browserify/-/timers-browserify-1.4.2.tgz", + "integrity": "sha1-ycWLV1voQHN1y14kYtrO50NZ9B0=", + "dev": true, + "requires": { + "process": "~0.11.0" + } + }, + "to-arraybuffer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/to-arraybuffer/-/to-arraybuffer-1.0.1.tgz", + "integrity": "sha1-fSKbH8xjfkZsoIEYCDanqr/4P0M=", + "dev": true + }, + "to-object-path": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/to-object-path/-/to-object-path-0.3.0.tgz", + "integrity": "sha1-KXWIt7Dn4KwI4E5nL4XB9JmeF68=", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "to-regex": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/to-regex/-/to-regex-3.0.2.tgz", + "integrity": "sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw==", + "dev": true, + "requires": { + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "regex-not": "^1.0.2", + "safe-regex": "^1.1.0" + } + }, + "to-regex-range": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz", + "integrity": "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=", + "dev": true, + "requires": { + "is-number": "^3.0.0", + "repeat-string": "^1.6.1" + } + }, + "tough-cookie": { + "version": "2.4.3", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.4.3.tgz", + "integrity": "sha512-Q5srk/4vDM54WJsJio3XNn6K2sCG+CQ8G5Wz6bZhRZoAe/+TxjWB/GlFAnYEbkYVlON9FMk/fE3h2RLpPXo4lQ==", + "dev": true, + "requires": { + "psl": "^1.1.24", + "punycode": "^1.4.1" + } + }, + "trim-newlines": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-1.0.0.tgz", + "integrity": "sha1-WIeWa7WCpFA6QetST301ARgVphM=", + "dev": true + }, + "triple-beam": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/triple-beam/-/triple-beam-1.3.0.tgz", + "integrity": "sha512-XrHUvV5HpdLmIj4uVMxHggLbFSZYIn7HEWsqePZcI50pco+MPqJ50wMGY794X7AOOhxOBAjbkqfAbEe/QMp2Lw==", + "dev": true + }, + "tty-browserify": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/tty-browserify/-/tty-browserify-0.0.1.tgz", + "integrity": "sha512-C3TaO7K81YvjCgQH9Q1S3R3P3BtN3RIM8n+OvX4il1K1zgE8ZhI0op7kClgkxtutIE8hQrcrHBXvIheqKUUCxw==", + "dev": true + }, + "tunnel-agent": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", + "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", + "dev": true, + "requires": { + "safe-buffer": "^5.0.1" + } + }, + "tweetnacl": { + "version": "0.14.5", + "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", + "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=", + "dev": true + }, + "typedarray": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", + "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=", + "dev": true + }, + "typescript": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.2.4.tgz", + "integrity": "sha512-0RNDbSdEokBeEAkgNbxJ+BLwSManFy9TeXz8uW+48j/xhEXv1ePME60olyzw2XzUqUBNAYFeJadIqAgNqIACwg==", + "dev": true + }, + "uglify-js": { + "version": "2.6.4", + "resolved": "http://registry.npmjs.org/uglify-js/-/uglify-js-2.6.4.tgz", + "integrity": "sha1-ZeovswWck5RpLxX+2HwrNsFrmt8=", + "dev": true, + "requires": { + "async": "~0.2.6", + "source-map": "~0.5.1", + "uglify-to-browserify": "~1.0.0", + "yargs": "~3.10.0" + }, + "dependencies": { + "async": { + "version": "0.2.10", + "resolved": "http://registry.npmjs.org/async/-/async-0.2.10.tgz", + "integrity": "sha1-trvgsGdLnXGXCMo43owjfLUmw9E=", + "dev": true + } + } + }, + "uglify-to-browserify": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/uglify-to-browserify/-/uglify-to-browserify-1.0.2.tgz", + "integrity": "sha1-bgkk1r2mta/jSeOabWMoUKD4grc=", + "dev": true + }, + "umd": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/umd/-/umd-3.0.3.tgz", + "integrity": "sha512-4IcGSufhFshvLNcMCV80UnQVlZ5pMOC8mvNPForqwA4+lzYQuetTESLDQkeLmihq8bRcnpbQa48Wb8Lh16/xow==", + "dev": true + }, + "unc-path-regex": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/unc-path-regex/-/unc-path-regex-0.1.2.tgz", + "integrity": "sha1-5z3T17DXxe2G+6xrCufYxqadUPo=", + "dev": true + }, + "undeclared-identifiers": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/undeclared-identifiers/-/undeclared-identifiers-1.1.2.tgz", + "integrity": "sha512-13EaeocO4edF/3JKime9rD7oB6QI8llAGhgn5fKOPyfkJbRb6NFv9pYV6dFEmpa4uRjKeBqLZP8GpuzqHlKDMQ==", + "dev": true, + "requires": { + "acorn-node": "^1.3.0", + "get-assigned-identifiers": "^1.2.0", + "simple-concat": "^1.0.0", + "xtend": "^4.0.1" + } + }, + "underscore": { + "version": "1.8.3", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.8.3.tgz", + "integrity": "sha1-Tz+1OxBuYJf8+ctBCfKl6b36UCI=" + }, + "underscore-contrib": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/underscore-contrib/-/underscore-contrib-0.3.0.tgz", + "integrity": "sha1-ZltmwkeD+PorGMn4y7Dix9SMJsc=", + "requires": { + "underscore": "1.6.0" + }, + "dependencies": { + "underscore": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.6.0.tgz", + "integrity": "sha1-izixDKze9jM3uLJOT/htRa6lKag=" + } + } + }, + "underscore.string": { + "version": "3.3.5", + "resolved": "https://registry.npmjs.org/underscore.string/-/underscore.string-3.3.5.tgz", + "integrity": "sha512-g+dpmgn+XBneLmXXo+sGlW5xQEt4ErkS3mgeN2GFbremYeMBSJKr9Wf2KJplQVaiPY/f7FN6atosWYNm9ovrYg==", + "dev": true, + "requires": { + "sprintf-js": "^1.0.3", + "util-deprecate": "^1.0.2" + } + }, + "union-value": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.0.tgz", + "integrity": "sha1-XHHDTLW61dzr4+oM0IIHulqhrqQ=", + "dev": true, + "requires": { + "arr-union": "^3.1.0", + "get-value": "^2.0.6", + "is-extendable": "^0.1.1", + "set-value": "^0.4.3" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + }, + "set-value": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/set-value/-/set-value-0.4.3.tgz", + "integrity": "sha1-fbCPnT0i3H945Trzw79GZuzfzPE=", + "dev": true, + "requires": { + "extend-shallow": "^2.0.1", + "is-extendable": "^0.1.1", + "is-plain-object": "^2.0.1", + "to-object-path": "^0.3.0" + } + } + } + }, + "unset-value": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unset-value/-/unset-value-1.0.0.tgz", + "integrity": "sha1-g3aHP30jNRef+x5vw6jtDfyKtVk=", + "dev": true, + "requires": { + "has-value": "^0.3.1", + "isobject": "^3.0.0" + }, + "dependencies": { + "has-value": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/has-value/-/has-value-0.3.1.tgz", + "integrity": "sha1-ex9YutpiyoJ+wKIHgCVlSEWZXh8=", + "dev": true, + "requires": { + "get-value": "^2.0.3", + "has-values": "^0.1.4", + "isobject": "^2.0.0" + }, + "dependencies": { + "isobject": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz", + "integrity": "sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=", + "dev": true, + "requires": { + "isarray": "1.0.0" + } + } + } + }, + "has-values": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/has-values/-/has-values-0.1.4.tgz", + "integrity": "sha1-bWHeldkd/Km5oCCJrThL/49it3E=", + "dev": true + } + } + }, + "uri-js": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.2.2.tgz", + "integrity": "sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ==", + "dev": true, + "requires": { + "punycode": "^2.1.0" + }, + "dependencies": { + "punycode": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", + "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", + "dev": true + } + } + }, + "uri-path": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/uri-path/-/uri-path-1.0.0.tgz", + "integrity": "sha1-l0fwGDWJM8Md4PzP2C0TjmcmLjI=", + "dev": true + }, + "urix": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/urix/-/urix-0.1.0.tgz", + "integrity": "sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI=", + "dev": true + }, + "url": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/url/-/url-0.11.0.tgz", + "integrity": "sha1-ODjpfPxgUh63PFJajlW/3Z4uKPE=", + "dev": true, + "requires": { + "punycode": "1.3.2", + "querystring": "0.2.0" + }, + "dependencies": { + "punycode": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz", + "integrity": "sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0=", + "dev": true + } + } + }, + "use": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/use/-/use-3.1.1.tgz", + "integrity": "sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ==", + "dev": true + }, + "util": { + "version": "0.10.4", + "resolved": "https://registry.npmjs.org/util/-/util-0.10.4.tgz", + "integrity": "sha512-0Pm9hTQ3se5ll1XihRic3FDIku70C+iHUdT/W926rSgHV5QgXsYbKZN8MSC3tJtSkhuROzvsQjAaFENRXr+19A==", + "dev": true, + "requires": { + "inherits": "2.0.3" + } + }, + "util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", + "dev": true + }, + "uuid": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.2.tgz", + "integrity": "sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA==", + "dev": true + }, + "v8flags": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/v8flags/-/v8flags-3.1.2.tgz", + "integrity": "sha512-MtivA7GF24yMPte9Rp/BWGCYQNaUj86zeYxV/x2RRJMKagImbbv3u8iJC57lNhWLPcGLJmHcHmFWkNsplbbLWw==", + "dev": true, + "requires": { + "homedir-polyfill": "^1.0.1" + } + }, + "validate-npm-package-license": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", + "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", + "dev": true, + "requires": { + "spdx-correct": "^3.0.0", + "spdx-expression-parse": "^3.0.0" + } + }, + "verror": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", + "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=", + "dev": true, + "requires": { + "assert-plus": "^1.0.0", + "core-util-is": "1.0.2", + "extsprintf": "^1.2.0" + } + }, + "vm-browserify": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/vm-browserify/-/vm-browserify-1.1.0.tgz", + "integrity": "sha512-iq+S7vZJE60yejDYM0ek6zg308+UZsdtPExWP9VZoCFCz1zkJoXFnAX7aZfd/ZwrkidzdUZL0C/ryW+JwAiIGw==", + "dev": true + }, + "which": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "dev": true, + "requires": { + "isexe": "^2.0.0" + } + }, + "window-size": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/window-size/-/window-size-0.1.0.tgz", + "integrity": "sha1-VDjNLqk7IC76Ohn+iIeu58lPnJ0=", + "dev": true + }, + "winston": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/winston/-/winston-3.1.0.tgz", + "integrity": "sha512-FsQfEE+8YIEeuZEYhHDk5cILo1HOcWkGwvoidLrDgPog0r4bser1lEIOco2dN9zpDJ1M88hfDgZvxe5z4xNcwg==", + "dev": true, + "requires": { + "async": "^2.6.0", + "diagnostics": "^1.1.1", + "is-stream": "^1.1.0", + "logform": "^1.9.1", + "one-time": "0.0.4", + "readable-stream": "^2.3.6", + "stack-trace": "0.0.x", + "triple-beam": "^1.3.0", + "winston-transport": "^4.2.0" + }, + "dependencies": { + "async": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/async/-/async-2.6.1.tgz", + "integrity": "sha512-fNEiL2+AZt6AlAw/29Cr0UDe4sRAHCpEHh54WMz+Bb7QfNcFw4h3loofyJpLeQs4Yx7yuqu/2dLgM5hKOs6HlQ==", + "dev": true, + "requires": { + "lodash": "^4.17.10" + } + } + } + }, + "winston-transport": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/winston-transport/-/winston-transport-4.3.0.tgz", + "integrity": "sha512-B2wPuwUi3vhzn/51Uukcao4dIduEiPOcOt9HJ3QeaXgkJ5Z7UwpBzxS4ZGNHtrxrUvTwemsQiSys0ihOf8Mp1A==", + "dev": true, + "requires": { + "readable-stream": "^2.3.6", + "triple-beam": "^1.2.0" + } + }, + "wordwrap": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.2.tgz", + "integrity": "sha1-t5Zpu0LstAn4PVg8rVLKF+qhZD8=", + "dev": true + }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", + "dev": true + }, + "ws": { + "version": "6.1.3", + "resolved": "https://registry.npmjs.org/ws/-/ws-6.1.3.tgz", + "integrity": "sha512-tbSxiT+qJI223AP4iLfQbkbxkwdFcneYinM2+x46Gx2wgvbaOMO36czfdfVUBRTHvzAMRhDd98sA5d/BuWbQdg==", + "dev": true, + "requires": { + "async-limiter": "~1.0.0" + } + }, + "xmlcreate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/xmlcreate/-/xmlcreate-1.0.2.tgz", + "integrity": "sha1-+mv3YqYKQT+z3Y9LA8WyaSONMI8=" + }, + "xtend": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz", + "integrity": "sha1-pcbVMr5lbiPbgg77lDofBJmNY68=", + "dev": true + }, + "yargs": { + "version": "3.10.0", + "resolved": "http://registry.npmjs.org/yargs/-/yargs-3.10.0.tgz", + "integrity": "sha1-9+572FfdfB0tOMDnTvvWgdFDH9E=", + "dev": true, + "requires": { + "camelcase": "^1.0.2", + "cliui": "^2.1.0", + "decamelize": "^1.0.0", + "window-size": "0.1.0" + }, + "dependencies": { + "camelcase": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-1.2.1.tgz", + "integrity": "sha1-m7UwTS4LVmmLLHWLCKPqqdqlijk=", + "dev": true + } + } + }, + "yauzl": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.4.1.tgz", + "integrity": "sha1-lSj0QtqxsihOWLQ3m7GU4i4MQAU=", + "dev": true, + "requires": { + "fd-slicer": "~1.0.1" + } + } + } +} diff --git a/lib/ts/package.json b/lib/ts/package.json new file mode 100644 index 00000000000..1cd6c710033 --- /dev/null +++ b/lib/ts/package.json @@ -0,0 +1,39 @@ +{ + "name": "thrift", + "version": "0.14.0", + "description": "Thrift is a software framework for scalable cross-language services development.", + "author": { + "name": "Apache Thrift Developers", + "email": "dev@thrift.apache.org" + }, + "bugs": "https://issues.apache.org/jira/projects/THRIFT/summary", + "homepage": "http://thrift.apache.org", + "repository": "https://github.com/apache/thrift", + "license": "Apache-2.0", + "devDependencies": { + "@types/node-int64": "^0.4.29", + "@types/phantom": "^3.2.5", + "@types/qunit": "^2.5.4", + "browserify": "^16.2.3", + "bufferutil": "^4.0.1", + "grunt": "^1.0.3", + "grunt-cli": "^1.2.0", + "grunt-contrib-concat": "^1.0.1", + "grunt-contrib-jshint": "^1.0.0", + "grunt-contrib-qunit": "^3.1.0", + "grunt-contrib-uglify": "^1.0.1", + "grunt-jsdoc": "^2.2.1", + "grunt-shell-spawn": "^0.3.12", + "jslint": "^0.12.0", + "node-int64": "^0.4.0", + "phantom": "^6.0.3", + "typescript": "^3.2.4" + }, + "dependencies": { + "bufferutil": "^4.0.1", + "jsdoc": "^3.5.5", + "json-int64": "^1.0.0", + "nopt": "^4.0.1" + }, + "types": "./thrift.d.ts" +} diff --git a/lib/ts/test/build.xml b/lib/ts/test/build.xml new file mode 100755 index 00000000000..5c3a4a87b92 --- /dev/null +++ b/lib/ts/test/build.xml @@ -0,0 +1,250 @@ + + + + Java Script Test based on Thrift Java Library + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + You need libthrift*.jar and libthrift*test.jar located at + ${thrift.java.dir}/build/libs + Did you compile Thrift Java library and its test suite by "ant compile-test"? + + + + + + + + + + Thrift compiler is missing ! + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + check if Xvfb is available: + + + + + + + check if phantomjs is available: + + + + + + + + + + + + + + + + Running Unit Tests with headless browser! + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + check if gjslint is available: + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/lib/ts/test/phantom-client.ts b/lib/ts/test/phantom-client.ts new file mode 100644 index 00000000000..55189372536 --- /dev/null +++ b/lib/ts/test/phantom-client.ts @@ -0,0 +1,352 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + /* jshint -W100 */ + +import { ThriftTest } from "./gen-js/ThriftTest_types"; +import "./gen-js/ThriftTest"; +var Int64 = require("node-int64"); +var phantom = require("phantom"); + +const int64_2_pow_60: typeof Int64 = new Int64('1000000000000000'); +const int64_minus_2_pow_60: typeof Int64 = new Int64('f000000000000000'); + + (function() { + 'use strict'; + + // Rudimentary test helper functions + // TODO: Return error code based on kind of errors rather than throw + var ok = function(t, msg) { + if (!t) { + console.log('*** FAILED ***'); + throw new Error(msg); + } + }; + var equal = function(a, b) { + if (a !== b) { + console.log('*** FAILED ***'); + throw new Error(); + } + }; + var test = function(name, f) { + console.log('TEST : ' + name); + f(); + console.log('OK\n'); + }; + + var parseArgs = function(args) { + var skips = [ + '--transport=http', + '--protocol=json' + ]; + var opts = { + port: '9090' + // protocol: 'json', + }; + var keys = {}; + for (var key in opts) { + keys['--' + key + '='] = key; + } + for (var i in args) { + var arg = args[i]; + if (skips.indexOf(arg) != -1) { + continue; + } + var hit = false; + for (var k in keys) { + if (arg.slice(0, k.length) === k) { + opts[keys[k]] = arg.slice(k.length); + hit = true; + break; + } + } + if (!hit) { + throw new Error('Unknown argument: ' + arg); + } + } + var portAsInt: number = parseInt(opts.port, 10); + if (!opts.port || portAsInt < 1 || portAsInt > 65535) { + throw new Error('Invalid port number'); + } + return opts; + }; + + var execute = function() { + console.log('### Apache Thrift Javascript standalone test client'); + console.log('------------------------------------------------------------'); + + phantom.page.injectJs('thrift.js'); + phantom.page.injectJs('gen-js/ThriftTest_types.js'); + phantom.page.injectJs('gen-js/ThriftTest.js'); + + var system = require('system'); + var opts = parseArgs(system.args.slice(1)); + var port = opts.port; + var transport = new Thrift.Transport('http://localhost:' + port + '/service'); + var protocol = new Thrift.Protocol(transport); + var client = new ThriftTest.ThriftTestClient(protocol); + + + // TODO: Remove duplicate code with test.js. + // all Languages in UTF-8 + var stringTest = "Afrikaans, Alemannisch, Aragonés, العربية, مصرى, Asturianu, Aymar aru, Azərbaycan, Башҡорт, Boarisch, Žemaitėška, Беларуская, Беларуская (тарашкевіца), Български, Bamanankan, বাংলা, Brezhoneg, Bosanski, Català, Mìng-dĕ̤ng-ngṳ̄, Нохчийн, Cebuano, ᏣᎳᎩ, Česky, Словѣ́ньскъ / ⰔⰎⰑⰂⰡⰐⰠⰔⰍⰟ, Чӑвашла, Cymraeg, Dansk, Zazaki, ދިވެހިބަސް, Ελληνικά, Emiliàn e rumagnòl, English, Esperanto, Español, Eesti, Euskara, فارسی, Suomi, Võro, Føroyskt, Français, Arpetan, Furlan, Frysk, Gaeilge, 贛語, Gàidhlig, Galego, Avañe'ẽ, ગુજરાતી, Gaelg, עברית, हिन्दी, Fiji Hindi, Hrvatski, Kreyòl ayisyen, Magyar, Հայերեն, Interlingua, Bahasa Indonesia, Ilokano, Ido, Íslenska, Italiano, 日本語, Lojban, Basa Jawa, ქართული, Kongo, Kalaallisut, ಕನ್ನಡ, 한국어, Къарачай-Малкъар, Ripoarisch, Kurdî, Коми, Kernewek, Кыргызча, Latina, Ladino, Lëtzebuergesch, Limburgs, Lingála, ລາວ, Lietuvių, Latviešu, Basa Banyumasan, Malagasy, Македонски, മലയാളം, मराठी, Bahasa Melayu, مازِرونی, Nnapulitano, Nedersaksisch, नेपाल भाषा, Nederlands, ‪Norsk (nynorsk)‬, ‪Norsk (bokmål)‬, Nouormand, Diné bizaad, Occitan, Иронау, Papiamentu, Deitsch, Norfuk / Pitkern, Polski, پنجابی, پښتو, Português, Runa Simi, Rumantsch, Romani, Română, Русский, Саха тыла, Sardu, Sicilianu, Scots, Sámegiella, Simple English, Slovenčina, Slovenščina, Српски / Srpski, Seeltersk, Svenska, Kiswahili, தமிழ், తెలుగు, Тоҷикӣ, ไทย, Türkmençe, Tagalog, Türkçe, Татарча/Tatarça, Українська, اردو, Tiếng Việt, Volapük, Walon, Winaray, 吴语, isiXhosa, ייִדיש, Yorùbá, Zeêuws, 中文, Bân-lâm-gú, 粵語"; + + function checkRecursively(map1, map2) { + if (typeof map1 !== 'function' && typeof map2 !== 'function') { + if (!map1 || typeof map1 !== 'object') { + equal(map1, map2); + } else { + for (var key in map1) { + checkRecursively(map1[key], map2[key]); + } + } + } + } + + test('Void', function() { + equal(client.testVoid(), undefined); + }); + test('Binary (String)', function() { + var binary: string = ''; + for (var v = 255; v >= 0; --v) { + binary += String.fromCharCode(v); + } + equal(client.testBinary(binary), binary); + }); + test('Binary (Uint8Array)', function() { + var binary: string = ''; + for (var v = 255; v >= 0; --v) { + binary += String.fromCharCode(v); + } + var arr = new Uint8Array(binary.length); + for (var i = 0; i < binary.length; ++i) { + arr[i] = binary[i].charCodeAt(0); + } + const hexEncodedString = Array.from(arr, function(byte) { + return String.fromCharCode(byte); + }).join('') + equal(client.testBinary(hexEncodedString), binary); + }); + test('String', function() { + equal(client.testString(''), ''); + equal(client.testString(stringTest), stringTest); + + var specialCharacters = 'quote: \" backslash:' + + ' forwardslash-escaped: \/ ' + + ' backspace: \b formfeed: \f newline: \n return: \r tab: ' + + ' now-all-of-them-together: "\\\/\b\n\r\t' + + ' now-a-bunch-of-junk: !@#$%&()(&%$#{}{}<><><'; + equal(client.testString(specialCharacters), specialCharacters); + }); + test('Double', function() { + equal(client.testDouble(0), 0); + equal(client.testDouble(-1), -1); + equal(client.testDouble(3.14), 3.14); + equal(client.testDouble(Math.pow(2, 60)), Math.pow(2, 60)); + }); + test('Bool', function() { + equal(client.testBool(true), true); + equal(client.testBool(false), false); + }); + test('I8', function() { + equal(client.testByte(0), 0); + equal(client.testByte(0x01), 0x01); + }); + test('I32', function() { + equal(client.testI32(0), 0); + equal(client.testI32(Math.pow(2, 30)), Math.pow(2, 30)); + equal(client.testI32(-Math.pow(2, 30)), -Math.pow(2, 30)); + }); + test('I64', function() { + equal(client.testI64(new Int64(0)), 0); + equal(client.testI64(int64_2_pow_60), Math.pow(2, 52)); + equal(client.testI64(int64_minus_2_pow_60), -Math.pow(2, 52)); + }); + + test('Struct', function() { + var structTestInput: ThriftTest.Xtruct = new ThriftTest.Xtruct(); + structTestInput.string_thing = 'worked'; + structTestInput.byte_thing = 0x01; + structTestInput.i32_thing = Math.pow(2, 30); + structTestInput.i64_thing = int64_2_pow_60; + + var structTestOutput: ThriftTest.Xtruct = client.testStruct(structTestInput); + + equal(structTestOutput.string_thing, structTestInput.string_thing); + equal(structTestOutput.byte_thing, structTestInput.byte_thing); + equal(structTestOutput.i32_thing, structTestInput.i32_thing); + equal(structTestOutput.i64_thing, structTestInput.i64_thing); + + equal(JSON.stringify(structTestOutput), JSON.stringify(structTestInput)); + }); + + test('Nest', function() { + var xtrTestInput: ThriftTest.Xtruct = new ThriftTest.Xtruct(); + xtrTestInput.string_thing = 'worked'; + xtrTestInput.byte_thing = 0x01; + xtrTestInput.i32_thing = Math.pow(2, 30); + xtrTestInput.i64_thing = int64_2_pow_60; + + var nestTestInput: ThriftTest.Xtruct2 = new ThriftTest.Xtruct2(); + nestTestInput.byte_thing = 0x02; + nestTestInput.struct_thing = xtrTestInput; + nestTestInput.i32_thing = Math.pow(2, 15); + + var nestTestOutput = client.testNest(nestTestInput); + + equal(nestTestOutput.byte_thing, nestTestInput.byte_thing); + equal(nestTestOutput.struct_thing.string_thing, nestTestInput.struct_thing.string_thing); + equal(nestTestOutput.struct_thing.byte_thing, nestTestInput.struct_thing.byte_thing); + equal(nestTestOutput.struct_thing.i32_thing, nestTestInput.struct_thing.i32_thing); + equal(nestTestOutput.struct_thing.i64_thing, nestTestInput.struct_thing.i64_thing); + equal(nestTestOutput.i32_thing, nestTestInput.i32_thing); + + equal(JSON.stringify(nestTestOutput), JSON.stringify(nestTestInput)); + }); + + test('Map', function() { + var mapTestInput: {[k: number]: number;} = {7: 77, 8: 88, 9: 99}; + + var mapTestOutput: {[k: number]: number;} = client.testMap(mapTestInput); + + for (var key in mapTestOutput) { + equal(mapTestOutput[key], mapTestInput[key]); + } + }); + + test('StringMap', function() { + var mapTestInput: {[k: string]: string;} = { + 'a': '123', 'a b': 'with spaces ', 'same': 'same', '0': 'numeric key', + 'longValue': stringTest, stringTest: 'long key' + }; + + var mapTestOutput: {[k: string]: string;} = client.testStringMap(mapTestInput); + + for (var key in mapTestOutput) { + equal(mapTestOutput[key], mapTestInput[key]); + } + }); + + test('Set', function() { + var setTestInput: number[] = [1, 2, 3]; + ok(client.testSet(setTestInput), setTestInput); + }); + + test('List', function() { + var listTestInput: number[] = [1, 2, 3]; + ok(client.testList(listTestInput), listTestInput); + }); + + test('Enum', function() { + equal(client.testEnum(ThriftTest.Numberz.ONE), ThriftTest.Numberz.ONE); + }); + + test('TypeDef', function() { + equal(client.testTypedef(new Int64(69)), 69); + }); + + test('MapMap', function() { + var mapMapTestExpectedResult: {[K: number]: {[k: number]: number}} = { + '4': {'1': 1, '2': 2, '3': 3, '4': 4}, + '-4': {'-4': -4, '-3': -3, '-2': -2, '-1': -1} + }; + + var mapMapTestOutput = client.testMapMap(1); + + + for (var key in mapMapTestOutput) { + for (var key2 in mapMapTestOutput[key]) { + equal(mapMapTestOutput[key][key2], mapMapTestExpectedResult[key][key2]); + } + } + + checkRecursively(mapMapTestOutput, mapMapTestExpectedResult); + }); + + test('Xception', function() { + try { + client.testException('Xception'); + ok(false, "expected an exception but there was no exception"); + } catch (e) { + equal(e.errorCode, 1001); + equal(e.message, 'Xception'); + } + }); + + test('no Exception', function() { + try { + client.testException('no Exception'); + } catch (e) { + ok(false, "expected no exception but here was an exception"); + } + }); + + test('TException', function() { + try { + client.testException('TException'); + ok(false, "expected an exception but there was no exception"); + } catch (e) { + ok(ok, "succesfully got exception"); + } + }); + + const crazy: ThriftTest.Insanity = { + 'userMap': { '5': new Int64(5), '8': new Int64(8) }, + 'xtructs': [{ + 'string_thing': 'Goodbye4', + 'byte_thing': 4, + 'i32_thing': 4, + 'i64_thing': new Int64(4) + }, + { + 'string_thing': 'Hello2', + 'byte_thing': 2, + 'i32_thing': 2, + 'i64_thing': new Int64(2) + }] + }; + test('Insanity', function() { + const insanity: {[k: number]: (ThriftTest.Insanity | {[k:number]: ThriftTest.Insanity})} = { + '1': { + '2': crazy, + '3': crazy + }, + '2': { '6': new ThriftTest.Insanity() } + }; + var res = client.testInsanity(new ThriftTest.Insanity(crazy)); + ok(res, JSON.stringify(res)); + ok(insanity, JSON.stringify(insanity)); + + checkRecursively(res, insanity); + }); + + console.log('------------------------------------------------------------'); + console.log('### All tests succeeded.'); + return 0; + }; + + try { + var ret = execute(); + phantom.exit(ret); + } catch (err) { + // Catch all and exit to avoid hang. + console.error(err); + phantom.exit(1); + } + })(); + \ No newline at end of file diff --git a/lib/ts/test/server_http.js b/lib/ts/test/server_http.js new file mode 100644 index 00000000000..8380c3a7732 --- /dev/null +++ b/lib/ts/test/server_http.js @@ -0,0 +1,55 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +// This HTTP server is designed to serve the test.html browser +// based JavaScript test page (which must be in the current directory). +// This server also supplies the Thrift based test service, which depends +// on the standard ThriftTest.thrift IDL service (which must be compiled +// for Node and browser based JavaScript in ./gen-nodejs and ./gen-js +// respectively). +// +// Using the command flag --es6, this server can be run using nodejs code built +// for the es6 environment or for pre-es6 environment. +// + +const thrift = require('../../nodejs/lib/thrift'); +const es6Mode = process.argv.includes('--es6'); +const genFolder = es6Mode ? 'gen-nodejs-es6' : 'gen-nodejs'; +const ThriftTestSvc = require(`./${genFolder}/ThriftTest.js`); +const ThriftTestHandler = require('./test_handler').ThriftTestHandler; + +const ThriftTestSvcOpt = { + transport: thrift.TBufferedTransport, + protocol: thrift.TJSONProtocol, + processor: ThriftTestSvc, + handler: ThriftTestHandler +}; + +const ThriftWebServerOptions = { + files: __dirname, + services: { + '/service': ThriftTestSvcOpt + } +}; + +const server = thrift.createWebServer(ThriftWebServerOptions); +const port = es6Mode ? 8088 : 8089; +server.listen(port); +console.log(`Serving files from: ${__dirname}`); +console.log(`Http/Thrift Server (ES6 mode ${es6Mode}) running on port: ${port}`); diff --git a/lib/ts/test/test-int64.html b/lib/ts/test/test-int64.html new file mode 100644 index 00000000000..75ac27839ef --- /dev/null +++ b/lib/ts/test/test-int64.html @@ -0,0 +1,46 @@ ++ + + + + + Int64 Constants in JS: Unit Test + + + + + + + + + +

Int64 Constants in JS: Unit Test

+

+
+

+
+ + + diff --git a/lib/ts/test/test-int64.ts b/lib/ts/test/test-int64.ts new file mode 100644 index 00000000000..254d8d7bfcc --- /dev/null +++ b/lib/ts/test/test-int64.ts @@ -0,0 +1,101 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + /* jshint -W100 */ + +var Int64 = require("node-int64"); +var JSONInt64 = require("json-int64"); +import { Int64Test } from "./gen-js/Int64Test_types"; + + +// Work around for old API used by QUnitAdapter of jsTestDriver +if (typeof QUnit.log == 'function') { + // When using real QUnit (fron PhantomJS) log failures to console + QUnit.log(function(details) { + if (!details.result) { + console.log('======== FAIL ========'); + console.log('TestName: ' + details.name); + if (details.message) console.log(details.message); + console.log('Expected: ' + details.expected); + console.log('Actual : ' + details.actual); + console.log('======================'); + } + }); +} + +QUnit.module('Int64'); + + QUnit.test('Int64', function(assert) { + console.log('Int64 test -- starts'); + const EXPECTED_SMALL_INT64_AS_NUMBER: number = 42; + const EXPECTED_SMALL_INT64: typeof Int64 = new Int64(42); + const EXPECTED_MAX_JS_SAFE_INT64: typeof Int64 = new Int64(Number.MAX_SAFE_INTEGER); + const EXPECTED_MIN_JS_SAFE_INT64: typeof Int64 = new Int64(Number.MIN_SAFE_INTEGER); + const EXPECTED_MAX_JS_SAFE_PLUS_ONE_INT64: typeof Int64 = new Int64("0020000000000000"); // hex-encoded + const EXPECTED_MIN_JS_SAFE_MINUS_ONE_INT64: typeof Int64 = new Int64("ffe0000000000000"); // hex-encoded 2's complement + const EXPECTED_MAX_SIGNED_INT64: typeof Int64 = new Int64("7fffffffffffffff"); // hex-encoded + const EXPECTED_MIN_SIGNED_INT64: typeof Int64 = new Int64("8000000000000000"); // hex-encoded 2's complement + const EXPECTED_INT64_LIST = [ + EXPECTED_SMALL_INT64, + EXPECTED_MAX_JS_SAFE_INT64, + EXPECTED_MIN_JS_SAFE_INT64, + EXPECTED_MAX_JS_SAFE_PLUS_ONE_INT64, + EXPECTED_MIN_JS_SAFE_MINUS_ONE_INT64, + EXPECTED_MAX_SIGNED_INT64, + EXPECTED_MIN_SIGNED_INT64 + ]; + assert.ok(EXPECTED_SMALL_INT64.equals(Int64Test.SMALL_INT64)); + assert.ok(EXPECTED_MAX_JS_SAFE_INT64.equals(Int64Test.MAX_JS_SAFE_INT64)); + assert.ok(EXPECTED_MIN_JS_SAFE_INT64.equals(Int64Test.MIN_JS_SAFE_INT64)); + assert.ok( + EXPECTED_MAX_JS_SAFE_PLUS_ONE_INT64.equals( + Int64Test.MAX_JS_SAFE_PLUS_ONE_INT64 + ) + ); + assert.ok( + EXPECTED_MIN_JS_SAFE_MINUS_ONE_INT64.equals( + Int64Test.MIN_JS_SAFE_MINUS_ONE_INT64 + ) + ); + assert.ok(EXPECTED_MAX_SIGNED_INT64.equals(Int64Test.MAX_SIGNED_INT64)); + assert.ok(EXPECTED_MIN_SIGNED_INT64.equals(Int64Test.MIN_SIGNED_INT64)); + assert.equal( + EXPECTED_SMALL_INT64_AS_NUMBER, + Int64Test.SMALL_INT64.toNumber() + ); + assert.equal( + Number.MAX_SAFE_INTEGER, + Int64Test.MAX_JS_SAFE_INT64.toNumber() + ); + assert.equal( + Number.MIN_SAFE_INTEGER, + Int64Test.MIN_JS_SAFE_INT64.toNumber() + ); + + for (let i = 0; i < EXPECTED_INT64_LIST.length; ++i) { + assert.ok(EXPECTED_INT64_LIST[i].equals(Int64Test.INT64_LIST[i])); + } + + for (let i = 0; i < EXPECTED_INT64_LIST.length; ++i){ + let int64Object = EXPECTED_INT64_LIST[i]; + assert.ok(Int64Test.INT64_2_INT64_MAP[JSONInt64.toDecimalString(int64Object)].equals(int64Object)); + } + + console.log('Int64 test -- ends'); + }); + diff --git a/lib/ts/test/test.html b/lib/ts/test/test.html new file mode 100755 index 00000000000..ec7d7ebe403 --- /dev/null +++ b/lib/ts/test/test.html @@ -0,0 +1,53 @@ + + + + + + Thrift Javascript Bindings: Unit Test + + + + + + + + + + + + + + + + +

Thrift Javascript Bindings: Unit Test (ThriftTest.thrift)

+

+
+

+
+ + + diff --git a/lib/ts/test/test.ts b/lib/ts/test/test.ts new file mode 100644 index 00000000000..31fdcbff8bc --- /dev/null +++ b/lib/ts/test/test.ts @@ -0,0 +1,342 @@ +import { ThriftTest } from "./gen-js/ThriftTest_types"; +import "./gen-js/ThriftTest"; + +var Int64 = require("node-int64"); +var JSONInt64 = require("json-int64"); +var QUnit = require("./qunit"); + +const transport: Thrift.Transport = new Thrift.Transport("/service"); +const protocol: Thrift.Protocol = new Thrift.Protocol(transport); +const client: ThriftTest.ThriftTestClient = new ThriftTest.ThriftTestClient(protocol); + +const int64_2_pow_60: typeof Int64 = new Int64('1000000000000000'); +const int64_minus_2_pow_60: typeof Int64 = new Int64('f000000000000000'); + +// Work around for old API used by QUnitAdapter of jsTestDriver +if (typeof QUnit.log == 'function') { + // When using real QUnit (fron PhantomJS) log failures to console + QUnit.log(function(details) { + if (!details.result) { + console.log('======== FAIL ========'); + console.log('TestName: ' + details.name); + if (details.message) console.log(details.message); + console.log('Expected: ' + JSONInt64.stringify(details.expected)); + console.log('Actual : ' + JSONInt64.stringify(details.actual)); + console.log('======================'); + } + }); +} + +// all Languages in UTF-8 +const stringTest: string = "Afrikaans, Alemannisch, Aragonés, العربية, مصرى, Asturianu, Aymar aru, Azərbaycan, Башҡорт, Boarisch, Žemaitėška, Беларуская, Беларуская (тарашкевіца), Български, Bamanankan, বাংলা, Brezhoneg, Bosanski, Català, Mìng-dĕ̤ng-ngṳ̄, Нохчийн, Cebuano, ᏣᎳᎩ, Česky, Словѣ́ньскъ / ⰔⰎⰑⰂⰡⰐⰠⰔⰍⰟ, Чӑвашла, Cymraeg, Dansk, Zazaki, ދިވެހިބަސް, Ελληνικά, Emiliàn e rumagnòl, English, Esperanto, Español, Eesti, Euskara, فارسی, Suomi, Võro, Føroyskt, Français, Arpetan, Furlan, Frysk, Gaeilge, 贛語, Gàidhlig, Galego, Avañe'ẽ, ગુજરાતી, Gaelg, עברית, हिन्दी, Fiji Hindi, Hrvatski, Kreyòl ayisyen, Magyar, Հայերեն, Interlingua, Bahasa Indonesia, Ilokano, Ido, Íslenska, Italiano, 日本語, Lojban, Basa Jawa, ქართული, Kongo, Kalaallisut, ಕನ್ನಡ, 한국어, Къарачай-Малкъар, Ripoarisch, Kurdî, Коми, Kernewek, Кыргызча, Latina, Ladino, Lëtzebuergesch, Limburgs, Lingála, ລາວ, Lietuvių, Latviešu, Basa Banyumasan, Malagasy, Македонски, മലയാളം, मराठी, Bahasa Melayu, مازِرونی, Nnapulitano, Nedersaksisch, नेपाल भाषा, Nederlands, ‪Norsk (nynorsk)‬, ‪Norsk (bokmål)‬, Nouormand, Diné bizaad, Occitan, Иронау, Papiamentu, Deitsch, Norfuk / Pitkern, Polski, پنجابی, پښتو, Português, Runa Simi, Rumantsch, Romani, Română, Русский, Саха тыла, Sardu, Sicilianu, Scots, Sámegiella, Simple English, Slovenčina, Slovenščina, Српски / Srpski, Seeltersk, Svenska, Kiswahili, தமிழ், తెలుగు, Тоҷикӣ, ไทย, Türkmençe, Tagalog, Türkçe, Татарча/Tatarça, Українська, اردو, Tiếng Việt, Volapük, Walon, Winaray, 吴语, isiXhosa, ייִדיש, Yorùbá, Zeêuws, 中文, Bân-lâm-gú, 粵語"; + + +function checkRecursively(assert, map1: Object, map2: Object): void { + if (typeof map1 !== 'function' && typeof map2 !== 'function') { + if (!map1 || typeof map1 !== 'object') { + assert.equal(map1, map2); + } else { + for (let key in map1) { + checkRecursively(assert, map1[key], map2[key]); + } + } + } +} + + +QUnit.module('Base Types'); + + QUnit.test('Void', function(assert) { + assert.equal(client.testVoid(), undefined); + }); + QUnit.test('Binary (String)', function(assert) { + let binary: string = ''; + for (let v = 255; v >= 0; --v) { + binary += String.fromCharCode(v); + } + assert.equal(client.testBinary(binary), binary); + }); + QUnit.test('Binary (Uint8Array)', function(assert) { + let binary: string = ''; + for (let v = 255; v >= 0; --v) { + binary += String.fromCharCode(v); + } + const arr: Uint8Array = new Uint8Array(binary.length); + for (let i = 0; i < binary.length; ++i) { + arr[i] = binary[i].charCodeAt(0); + } + const hexEncodedString = Array.from(arr, function(byte) { + return String.fromCharCode(byte); + }).join('') + assert.equal(client.testBinary(hexEncodedString), binary); + }); + QUnit.test('String', function(assert) { + assert.equal(client.testString(''), ''); + assert.equal(client.testString(stringTest), stringTest); + + const specialCharacters: string = 'quote: \" backslash:' + + ' forwardslash-escaped: \/ ' + + ' backspace: \b formfeed: \f newline: \n return: \r tab: ' + + ' now-all-of-them-together: "\\\/\b\n\r\t' + + ' now-a-bunch-of-junk: !@#$%&()(&%$#{}{}<><><'; + assert.equal(client.testString(specialCharacters), specialCharacters); + }); + QUnit.test('Double', function(assert) { + assert.equal(client.testDouble(0), 0); + assert.equal(client.testDouble(-1), -1); + assert.equal(client.testDouble(3.14), 3.14); + assert.equal(client.testDouble(Math.pow(2, 60)), Math.pow(2, 60)); + }); + QUnit.test('Byte', function(assert) { + assert.equal(client.testByte(0), 0); + assert.equal(client.testByte(0x01), 0x01); + }); + QUnit.test('I32', function(assert) { + assert.equal(client.testI32(0), 0); + assert.equal(client.testI32(Math.pow(2, 30)), Math.pow(2, 30)); + assert.equal(client.testI32(-Math.pow(2, 30)), -Math.pow(2, 30)); + }); + QUnit.test('I64', function(assert) { + assert.equal(client.testI64(new Int64(0)), 0); + + let int64_2_pow_60_result: typeof Int64 = client.testI64(int64_2_pow_60); + assert.ok(int64_2_pow_60.equals(int64_2_pow_60_result)); + + let int64_minus_2_pow_60_result: typeof Int64 = client.testI64(int64_minus_2_pow_60); + assert.ok(int64_minus_2_pow_60.equals(int64_minus_2_pow_60_result)); + }); + + +QUnit.module('Structured Types'); + + QUnit.test('Struct', function(assert) { + const structTestInput: ThriftTest.Xtruct = new ThriftTest.Xtruct(); + structTestInput.string_thing = 'worked'; + structTestInput.byte_thing = 0x01; + structTestInput.i32_thing = Math.pow(2, 30); + structTestInput.i64_thing = int64_2_pow_60; + + const structTestOutput: ThriftTest.Xtruct = client.testStruct(structTestInput); + + assert.equal(structTestOutput.string_thing, structTestInput.string_thing); + assert.equal(structTestOutput.byte_thing, structTestInput.byte_thing); + assert.equal(structTestOutput.i32_thing, structTestInput.i32_thing); + assert.ok(structTestOutput.i64_thing.equals(structTestInput.i64_thing)); + assert.ok(structTestInput.i64_thing.equals(structTestOutput.i64_thing)); + + assert.equal(JSONInt64.stringify(structTestOutput), JSONInt64.stringify(structTestInput)); + }); + + QUnit.test('Nest', function(assert) { + const xtrTestInput: ThriftTest.Xtruct = new ThriftTest.Xtruct(); + xtrTestInput.string_thing = 'worked'; + xtrTestInput.byte_thing = 0x01; + xtrTestInput.i32_thing = Math.pow(2, 30); + xtrTestInput.i64_thing = int64_2_pow_60; + + const nestTestInput: ThriftTest.Xtruct2 = new ThriftTest.Xtruct2(); + nestTestInput.byte_thing = 0x02; + nestTestInput.struct_thing = xtrTestInput; + nestTestInput.i32_thing = Math.pow(2, 15); + + const nestTestOutput: ThriftTest.Xtruct2 = client.testNest(nestTestInput); + + assert.equal(nestTestOutput.byte_thing, nestTestInput.byte_thing); + assert.equal(nestTestOutput.struct_thing.string_thing, nestTestInput.struct_thing.string_thing); + assert.equal(nestTestOutput.struct_thing.byte_thing, nestTestInput.struct_thing.byte_thing); + assert.equal(nestTestOutput.struct_thing.i32_thing, nestTestInput.struct_thing.i32_thing); + assert.ok(nestTestOutput.struct_thing.i64_thing.equals(nestTestInput.struct_thing.i64_thing)); + assert.equal(nestTestOutput.i32_thing, nestTestInput.i32_thing); + + assert.equal(JSONInt64.stringify(nestTestOutput), JSONInt64.stringify(nestTestInput)); + }); + + QUnit.test('Map', function(assert) { + const mapTestInput: {[k: number]: number;} = {7: 77, 8: 88, 9: 99}; + + const mapTestOutput: {[k: number]: number;} = client.testMap(mapTestInput); + + for (let key in mapTestOutput) { + assert.equal(mapTestOutput[key], mapTestInput[key]); + } + }); + + QUnit.test('StringMap', function(assert) { + const mapTestInput: {[k: string]: string;} = { + 'a': '123', 'a b': 'with spaces ', 'same': 'same', '0': 'numeric key', + 'longValue': stringTest, stringTest: 'long key' + }; + + const mapTestOutput: {[k: string]: string;} = client.testStringMap(mapTestInput); + + for (let key in mapTestOutput) { + assert.equal(mapTestOutput[key], mapTestInput[key]); + } + }); + + QUnit.test('Set', function(assert) { + const setTestInput: number[] = [1, 2, 3]; + assert.ok(client.testSet(setTestInput), setTestInput); + }); + + QUnit.test('List', function(assert) { + const listTestInput: number[] = [1, 2, 3]; + assert.ok(client.testList(listTestInput), listTestInput); + }); + + QUnit.test('Enum', function(assert) { + assert.equal(client.testEnum(ThriftTest.Numberz.ONE), ThriftTest.Numberz.ONE); + }); + + QUnit.test('TypeDef', function(assert) { + assert.equal(client.testTypedef(new Int64(69)), 69); + }); + + +QUnit.module('deeper!'); + + QUnit.test('MapMap', function(assert) { + const mapMapTestExpectedResult: {[K: number]: {[k: number]: number}} = { + '4': {'1': 1, '2': 2, '3': 3, '4': 4}, + '-4': {'-4': -4, '-3': -3, '-2': -2, '-1': -1} + }; + + const mapMapTestOutput = client.testMapMap(1); + + + for (let key in mapMapTestOutput) { + for (let key2 in mapMapTestOutput[key]) { + assert.equal(mapMapTestOutput[key][key2], mapMapTestExpectedResult[key][key2]); + } + } + + checkRecursively(assert, mapMapTestOutput, mapMapTestExpectedResult); + }); + + +QUnit.module('Exception'); + + QUnit.test('Xception', function(assert) { + assert.expect(2); + const done = assert.async(); + try { + client.testException('Xception'); + assert.ok(false); + }catch (e) { + assert.equal(e.errorCode, 1001); + assert.equal(e.message, 'Xception'); + done(); + } + }); + + QUnit.test('no Exception', function(assert) { + assert.expect(1); + try { + client.testException('no Exception'); + assert.ok(true); + }catch (e) { + assert.ok(false); + } + }); + + QUnit.test('TException', function(assert) { + //ThriftTest does not list TException as a legal exception so it will + // generate an exception on the server that does not propagate back to + // the client. This test has been modified to equate to "no exception" + assert.expect(1); + try { + client.testException('TException'); + } catch (e) { + //assert.ok(false); + } + assert.ok(true); + }); + + +QUnit.module('Insanity'); + + const crazy: ThriftTest.Insanity = { + 'userMap': { '5': new Int64(5), '8': new Int64(8) }, + 'xtructs': [{ + 'string_thing': 'Goodbye4', + 'byte_thing': 4, + 'i32_thing': 4, + 'i64_thing': new Int64(4) + }, + { + 'string_thing': 'Hello2', + 'byte_thing': 2, + 'i32_thing': 2, + 'i64_thing': new Int64(2) + }] + }; + QUnit.test('testInsanity', function(assert) { + const insanity: {[k: number]: (ThriftTest.Insanity | {[k:number]: ThriftTest.Insanity})} = { + '1': { + '2': crazy, + '3': crazy + }, + '2': { '6': new ThriftTest.Insanity() } + }; + const res = client.testInsanity(new ThriftTest.Insanity(crazy)); + assert.ok(res, JSONInt64.stringify(res)); + assert.ok(insanity, JSONInt64.stringify(insanity)); + + checkRecursively(assert, res, insanity); + }); + + +////////////////////////////////// +//Run same tests asynchronously + +QUnit.module('Async'); + + QUnit.test('Double', function(assert) { + assert.expect(1); + + const done = assert.async(); + client.testDouble(3.14159265, function(result) { + assert.equal(result, 3.14159265); + done(); + }); + }); + + QUnit.test('Byte', function(assert) { + assert.expect(1); + + const done = assert.async(); + client.testByte(0x01, function(result) { + assert.equal(result, 0x01); + done(); + }); + }); + + QUnit.test('I32', function(assert) { + assert.expect(2); + + const done = assert.async(2); + client.testI32(Math.pow(2, 30), function(result) { + assert.equal(result, Math.pow(2, 30)); + done(); + }); + + client.testI32(Math.pow(-2, 31), function(result) { + assert.equal(result, Math.pow(-2, 31)); + done(); + }); + }); + + QUnit.test('I64', function(assert) { + assert.expect(2); + + const done = assert.async(2); + client.testI64(int64_2_pow_60, function(result) { + assert.ok(int64_2_pow_60.equals(result)); + done(); + }); + + client.testI64(int64_minus_2_pow_60, function(result) { + assert.ok(int64_minus_2_pow_60.equals(result)); + done(); + }); + }); diff --git a/lib/ts/test/test_handler.js b/lib/ts/test/test_handler.js new file mode 100644 index 00000000000..8ba296ba108 --- /dev/null +++ b/lib/ts/test/test_handler.js @@ -0,0 +1,202 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * 'License'); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * 'AS IS' BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +//This is the server side Node test handler for the standard +// Apache Thrift test service. + +const es6Mode = process.argv.includes('--es6'); +const genFolder = es6Mode ? 'gen-nodejs-es6' : 'gen-nodejs'; +const ttypes = require(`./${genFolder}/ThriftTest_types`); +const TException = require('../../nodejs/lib/thrift').TException; +const Int64 = require('node-int64'); + +exports.ThriftTestHandler = { + testVoid: function(result) { + console.log('testVoid()'); + result(null); + }, + testString: function(thing, result) { + console.log('testString(\'' + thing + '\')'); + result(null, thing); + }, + testByte: function(thing, result) { + console.log('testByte(' + thing + ')'); + result(null, thing); + }, + testI32: function(thing, result) { + console.log('testI32(' + thing + ')'); + result(null, thing); + }, + testI64: function(thing, result) { + console.log('testI64(' + thing + ')'); + result(null, thing); + }, + testDouble: function(thing, result) { + console.log('testDouble(' + thing + ')'); + result(null, thing); + }, + testBinary: function(thing, result) { + console.log('testBinary(\'' + thing + '\')'); + result(null, thing); + }, + testStruct: function(thing, result) { + console.log('testStruct('); + console.log(thing); + console.log(')'); + result(null, thing); + }, + testNest: function(nest, result) { + console.log('testNest('); + console.log(nest); + console.log(')'); + result(null, nest); + }, + testMap: function(thing, result) { + console.log('testMap('); + console.log(thing); + console.log(')'); + result(null, thing); + }, + testStringMap: function(thing, result) { + console.log('testStringMap('); + console.log(thing); + console.log(')'); + result(null, thing); + }, + testSet: function(thing, result) { + console.log('testSet('); + console.log(thing); + console.log(')'); + result(null, thing); + }, + testList: function(thing, result) { + console.log('testList('); + console.log(thing); + console.log(')'); + result(null, thing); + }, + testEnum: function(thing, result) { + console.log('testEnum(' + thing + ')'); + result(null, thing); + }, + testTypedef: function(thing, result) { + console.log('testTypedef(' + thing + ')'); + result(null, thing); + }, + testMapMap: function(hello, result) { + console.log('testMapMap(' + hello + ')'); + + const mapmap = []; + const pos = []; + const neg = []; + for (let i = 1; i < 5; i++) { + pos[i] = i; + neg[-i] = -i; + } + mapmap[4] = pos; + mapmap[-4] = neg; + + result(null, mapmap); + }, + testInsanity: function(argument, result) { + console.log('testInsanity('); + console.log(argument); + console.log(')'); + + const hello = new ttypes.Xtruct(); + hello.string_thing = 'Hello2'; + hello.byte_thing = 2; + hello.i32_thing = 2; + hello.i64_thing = new Int64(2); + + const goodbye = new ttypes.Xtruct(); + goodbye.string_thing = 'Goodbye4'; + goodbye.byte_thing = 4; + goodbye.i32_thing = 4; + goodbye.i64_thing = new Int64(4); + + const crazy = new ttypes.Insanity(); + crazy.userMap = []; + crazy.userMap[ttypes.Numberz.EIGHT] = 8; + crazy.userMap[ttypes.Numberz.FIVE] = 5; + crazy.xtructs = [goodbye, hello]; + + const first_map = []; + const second_map = []; + + first_map[ttypes.Numberz.TWO] = crazy; + first_map[ttypes.Numberz.THREE] = crazy; + + const looney = new ttypes.Insanity(); + second_map[ttypes.Numberz.SIX] = looney; + + const insane = []; + insane[1] = first_map; + insane[2] = second_map; + + console.log('insane result:'); + console.log(insane); + result(null, insane); + }, + testMulti: function(arg0, arg1, arg2, arg3, arg4, arg5, result) { + console.log('testMulti()'); + + const hello = new ttypes.Xtruct(); + hello.string_thing = 'Hello2'; + hello.byte_thing = arg0; + hello.i32_thing = arg1; + hello.i64_thing = arg2; + result(null, hello); + }, + testException: function(arg, result) { + console.log('testException(' + arg + ')'); + if (arg === 'Xception') { + const x = new ttypes.Xception(); + x.errorCode = 1001; + x.message = arg; + result(x); + } else if (arg === 'TException') { + result(new TException(arg)); + } else { + result(null); + } + }, + testMultiException: function(arg0, arg1, result) { + console.log('testMultiException(' + arg0 + ', ' + arg1 + ')'); + if (arg0 === ('Xception')) { + const x = new ttypes.Xception(); + x.errorCode = 1001; + x.message = 'This is an Xception'; + result(x); + } else if (arg0 === ('Xception2')) { + const x2 = new ttypes.Xception2(); + x2.errorCode = 2002; + x2.struct_thing = new ttypes.Xtruct(); + x2.struct_thing.string_thing = 'This is an Xception2'; + result(x2); + } + + const res = new ttypes.Xtruct(); + res.string_thing = arg1; + result(null, res); + }, + testOneway: function(sleepFor, result) { + console.log('testOneway(' + sleepFor + ') => JavaScript (like Rust) never sleeps!'); + } +}; //ThriftTestSvcHandler diff --git a/lib/ts/tsconfig.json b/lib/ts/tsconfig.json new file mode 100644 index 00000000000..97fa2258e19 --- /dev/null +++ b/lib/ts/tsconfig.json @@ -0,0 +1,31 @@ +{ + "compilerOptions": { + "allowJs": false, + "alwaysStrict": true, + "baseUrl": ".", + "declaration": true, + "emitDecoratorMetadata": true, + "experimentalDecorators": true, + "module": "commonjs", + "moduleResolution": "node", + "noImplicitThis": true, + "noUnusedLocals": true, + "preserveConstEnums": true, + "removeComments": true, + "strictFunctionTypes": true, + "strictNullChecks": true, + "target": "es6", + "paths": { + "*": [ + "*", + "test/", + "test/gen-js/*" + + ] + }, + }, + "exclude": [ + "./test/gen-nodejs/", + "./test/build/", + ] +} diff --git a/package-lock.json b/package-lock.json index 0342c15b2a0..5cab4bff159 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "thrift", - "version": "0.12.0", + "version": "0.14.0", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -13,6 +13,147 @@ "@babel/highlight": "^7.0.0" } }, + "@babel/core": { + "version": "7.8.4", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.8.4.tgz", + "integrity": "sha512-0LiLrB2PwrVI+a2/IEskBopDYSd8BCb3rOvH7D5tzoWd696TBEduBvuLVm4Nx6rltrLZqvI3MCalB2K2aVzQjA==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.8.3", + "@babel/generator": "^7.8.4", + "@babel/helpers": "^7.8.4", + "@babel/parser": "^7.8.4", + "@babel/template": "^7.8.3", + "@babel/traverse": "^7.8.4", + "@babel/types": "^7.8.3", + "convert-source-map": "^1.7.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.1", + "json5": "^2.1.0", + "lodash": "^4.17.13", + "resolve": "^1.3.2", + "semver": "^5.4.1", + "source-map": "^0.5.0" + }, + "dependencies": { + "@babel/code-frame": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.8.3.tgz", + "integrity": "sha512-a9gxpmdXtZEInkCSHUJDLHZVBgb1QS0jhss4cPP93EW7s+uC5bikET2twEF3KV+7rDblJcmNvTR7VJejqd2C2g==", + "dev": true, + "requires": { + "@babel/highlight": "^7.8.3" + } + }, + "@babel/highlight": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.8.3.tgz", + "integrity": "sha512-PX4y5xQUvy0fnEVHrYOarRPXVWafSjTW9T0Hab8gVIawpl2Sj0ORyrygANq+KjcNlSSTw0YCLSNA8OyZ1I4yEg==", + "dev": true, + "requires": { + "chalk": "^2.0.0", + "esutils": "^2.0.2", + "js-tokens": "^4.0.0" + } + }, + "@babel/parser": { + "version": "7.8.4", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.8.4.tgz", + "integrity": "sha512-0fKu/QqildpXmPVaRBoXOlyBb3MC+J0A66x97qEfLOMkn3u6nfY5esWogQwi/K0BjASYy4DbnsEWnpNL6qT5Mw==", + "dev": true + }, + "debug": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", + "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "resolve": { + "version": "1.15.1", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.15.1.tgz", + "integrity": "sha512-84oo6ZTtoTUpjgNEr5SJyzQhzL72gaRodsSfyxC/AXRvwu0Yse9H8eF9IpGo7b8YetZhlI6v7ZQ6bKBFV/6S7w==", + "dev": true, + "requires": { + "path-parse": "^1.0.6" + } + }, + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "dev": true + } + } + }, + "@babel/generator": { + "version": "7.8.4", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.8.4.tgz", + "integrity": "sha512-PwhclGdRpNAf3IxZb0YVuITPZmmrXz9zf6fH8lT4XbrmfQKr6ryBzhv593P5C6poJRciFCL/eHGW2NuGrgEyxA==", + "dev": true, + "requires": { + "@babel/types": "^7.8.3", + "jsesc": "^2.5.1", + "lodash": "^4.17.13", + "source-map": "^0.5.0" + }, + "dependencies": { + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "dev": true + } + } + }, + "@babel/helper-function-name": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.8.3.tgz", + "integrity": "sha512-BCxgX1BC2hD/oBlIFUgOCQDOPV8nSINxCwM3o93xP4P9Fq6aV5sgv2cOOITDMtCfQ+3PvHp3l689XZvAM9QyOA==", + "dev": true, + "requires": { + "@babel/helper-get-function-arity": "^7.8.3", + "@babel/template": "^7.8.3", + "@babel/types": "^7.8.3" + } + }, + "@babel/helper-get-function-arity": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.8.3.tgz", + "integrity": "sha512-FVDR+Gd9iLjUMY1fzE2SR0IuaJToR4RkCDARVfsBBPSP53GEqSFjD8gNyxg246VUyc/ALRxFaAK8rVG7UT7xRA==", + "dev": true, + "requires": { + "@babel/types": "^7.8.3" + } + }, + "@babel/helper-split-export-declaration": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.8.3.tgz", + "integrity": "sha512-3x3yOeyBhW851hroze7ElzdkeRXQYQbFIb7gLK1WQYsw2GWDay5gAJNw1sWJ0VFP6z5J1whqeXH/WCdCjZv6dA==", + "dev": true, + "requires": { + "@babel/types": "^7.8.3" + } + }, + "@babel/helpers": { + "version": "7.8.4", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.8.4.tgz", + "integrity": "sha512-VPbe7wcQ4chu4TDQjimHv/5tj73qz88o12EPkO2ValS2QiQS/1F2SsjyIGNnAD0vF/nZS6Cf9i+vW6HIlnaR8w==", + "dev": true, + "requires": { + "@babel/template": "^7.8.3", + "@babel/traverse": "^7.8.4", + "@babel/types": "^7.8.3" + } + }, "@babel/highlight": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.0.0.tgz", @@ -24,24 +165,175 @@ "js-tokens": "^4.0.0" } }, + "@babel/parser": { + "version": "7.7.3", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.7.3.tgz", + "integrity": "sha512-bqv+iCo9i+uLVbI0ILzKkvMorqxouI+GbV13ivcARXn9NNEabi2IEz912IgNpT/60BNXac5dgcfjb94NjsF33A==", + "dev": true + }, + "@babel/template": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.8.3.tgz", + "integrity": "sha512-04m87AcQgAFdvuoyiQ2kgELr2tV8B4fP/xJAVUL3Yb3bkNdMedD3d0rlSQr3PegP0cms3eHjl1F7PWlvWbU8FQ==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.8.3", + "@babel/parser": "^7.8.3", + "@babel/types": "^7.8.3" + }, + "dependencies": { + "@babel/code-frame": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.8.3.tgz", + "integrity": "sha512-a9gxpmdXtZEInkCSHUJDLHZVBgb1QS0jhss4cPP93EW7s+uC5bikET2twEF3KV+7rDblJcmNvTR7VJejqd2C2g==", + "dev": true, + "requires": { + "@babel/highlight": "^7.8.3" + } + }, + "@babel/highlight": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.8.3.tgz", + "integrity": "sha512-PX4y5xQUvy0fnEVHrYOarRPXVWafSjTW9T0Hab8gVIawpl2Sj0ORyrygANq+KjcNlSSTw0YCLSNA8OyZ1I4yEg==", + "dev": true, + "requires": { + "chalk": "^2.0.0", + "esutils": "^2.0.2", + "js-tokens": "^4.0.0" + } + }, + "@babel/parser": { + "version": "7.8.4", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.8.4.tgz", + "integrity": "sha512-0fKu/QqildpXmPVaRBoXOlyBb3MC+J0A66x97qEfLOMkn3u6nfY5esWogQwi/K0BjASYy4DbnsEWnpNL6qT5Mw==", + "dev": true + } + } + }, + "@babel/traverse": { + "version": "7.8.4", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.8.4.tgz", + "integrity": "sha512-NGLJPZwnVEyBPLI+bl9y9aSnxMhsKz42so7ApAv9D+b4vAFPpY013FTS9LdKxcABoIYFU52HcYga1pPlx454mg==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.8.3", + "@babel/generator": "^7.8.4", + "@babel/helper-function-name": "^7.8.3", + "@babel/helper-split-export-declaration": "^7.8.3", + "@babel/parser": "^7.8.4", + "@babel/types": "^7.8.3", + "debug": "^4.1.0", + "globals": "^11.1.0", + "lodash": "^4.17.13" + }, + "dependencies": { + "@babel/code-frame": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.8.3.tgz", + "integrity": "sha512-a9gxpmdXtZEInkCSHUJDLHZVBgb1QS0jhss4cPP93EW7s+uC5bikET2twEF3KV+7rDblJcmNvTR7VJejqd2C2g==", + "dev": true, + "requires": { + "@babel/highlight": "^7.8.3" + } + }, + "@babel/highlight": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.8.3.tgz", + "integrity": "sha512-PX4y5xQUvy0fnEVHrYOarRPXVWafSjTW9T0Hab8gVIawpl2Sj0ORyrygANq+KjcNlSSTw0YCLSNA8OyZ1I4yEg==", + "dev": true, + "requires": { + "chalk": "^2.0.0", + "esutils": "^2.0.2", + "js-tokens": "^4.0.0" + } + }, + "@babel/parser": { + "version": "7.8.4", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.8.4.tgz", + "integrity": "sha512-0fKu/QqildpXmPVaRBoXOlyBb3MC+J0A66x97qEfLOMkn3u6nfY5esWogQwi/K0BjASYy4DbnsEWnpNL6qT5Mw==", + "dev": true + }, + "debug": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", + "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + } + } + }, + "@babel/types": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.8.3.tgz", + "integrity": "sha512-jBD+G8+LWpMBBWvVcdr4QysjUE4mU/syrhN17o1u3gx0/WzJB1kwiVZAXRtWbsIPOwW8pF/YJV5+nmetPzepXg==", + "dev": true, + "requires": { + "esutils": "^2.0.2", + "lodash": "^4.17.13", + "to-fast-properties": "^2.0.0" + } + }, + "@istanbuljs/load-nyc-config": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.0.0.tgz", + "integrity": "sha512-ZR0rq/f/E4f4XcgnDvtMWXCUJpi8eO0rssVhmztsZqLIEFA9UUP9zmpE0VxlM+kv/E1ul2I876Fwil2ayptDVg==", + "dev": true, + "requires": { + "camelcase": "^5.3.1", + "find-up": "^4.1.0", + "js-yaml": "^3.13.1", + "resolve-from": "^5.0.0" + }, + "dependencies": { + "resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "dev": true + } + } + }, + "@istanbuljs/schema": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.2.tgz", + "integrity": "sha512-tsAQNx32a8CoFhjhijUIhI4kccIAgmGhy8LZMZgGfmXcpMbPRUqn5LWmgRttILi6yeGmBJd2xsPkFMs0PzgPCw==", + "dev": true + }, + "@types/color-name": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@types/color-name/-/color-name-1.1.1.tgz", + "integrity": "sha512-rr+OQyAjxze7GgWrSaJwydHStIhHq2lvY3BOC2Mj7KnzI7XK0Uw1TOOdI9lDoajEbSWLiYgoo4f1R51erQfhPQ==", + "dev": true + }, "@types/node": { "version": "10.12.6", "resolved": "https://registry.npmjs.org/@types/node/-/node-10.12.6.tgz", "integrity": "sha512-+ZWB5Ec1iki99xQFzBlivlKxSZQ+fuUKBott8StBOnLN4dWbRHlgdg1XknpW6g0tweniN5DcOqA64CJyOUPSAw==", "dev": true }, + "@types/node-int64": { + "version": "0.4.29", + "resolved": "https://registry.npmjs.org/@types/node-int64/-/node-int64-0.4.29.tgz", + "integrity": "sha512-rHXvenLTj/CcsmNAebaBOhxQ2MqEGl3yXZZcZ21XYR+gzGTTcpOy2N4IxpvTCz48loyQNatHvfn6GhIbbZ1R3Q==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, "@types/q": { "version": "1.5.1", "resolved": "https://registry.npmjs.org/@types/q/-/q-1.5.1.tgz", "integrity": "sha512-eqz8c/0kwNi/OEHQfvIuJVLTst3in0e7uTKeuY+WL/zfKn0xVujOTp42bS/vUUokhK5P2BppLd9JXMOMHcgbjA==", "dev": true }, - "abbrev": { - "version": "1.0.9", - "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.0.9.tgz", - "integrity": "sha1-kbR5JYinc4wl813W9jdSovh3YTU=", - "dev": true - }, "acorn": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/acorn/-/acorn-6.0.2.tgz", @@ -54,6 +346,16 @@ "integrity": "sha512-XkB50fn0MURDyww9+UYL3c1yLbOBz0ZFvrdYlGB8l+Ije1oSC75qAqrzSPjYQbdnQUzhlUGNKuesryAv0gxZOg==", "dev": true }, + "aggregate-error": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.0.1.tgz", + "integrity": "sha512-quoaXsZ9/BLNae5yiNoUz+Nhkwz83GhWwtYFglcjEQB2NDHCIpApbqXxIFnm4Pq/Nvhrsq5sYJFyohrrxnTGAA==", + "dev": true, + "requires": { + "clean-stack": "^2.0.0", + "indent-string": "^4.0.0" + } + }, "ajv": { "version": "6.5.4", "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.5.4.tgz", @@ -66,13 +368,6 @@ "uri-js": "^4.2.2" } }, - "amdefine": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/amdefine/-/amdefine-1.0.1.tgz", - "integrity": "sha1-SlKCrBZHKek2Gbz9OtFR+BfOkfU=", - "dev": true, - "optional": true - }, "ansi-escapes": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-3.1.0.tgz", @@ -94,22 +389,21 @@ "color-convert": "^1.9.0" } }, - "aproba": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz", - "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==", - "dev": true - }, - "are-we-there-yet": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.5.tgz", - "integrity": "sha512-5hYdAkZlcG8tOLujVDTgCT+uPX0VnpAH28gWsLfzpXYm7wP6mp5Q/gYyR7YQ0cKVJcXJnl3j2kpBan13PtQf6w==", + "append-transform": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/append-transform/-/append-transform-2.0.0.tgz", + "integrity": "sha512-7yeyCEurROLQJFv5Xj4lEGTy0borxepjFv1g22oAdqFu//SrAlDl1O1Nxx15SH1RoliUml6p8dwJW9jvZughhg==", "dev": true, "requires": { - "delegates": "^1.0.0", - "readable-stream": "^2.0.6" + "default-require-extensions": "^3.0.0" } }, + "archy": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/archy/-/archy-1.0.0.tgz", + "integrity": "sha1-+cjBN1fMHde8N5rHeyxipcKGjEA=", + "dev": true + }, "argparse": { "version": "1.0.10", "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", @@ -140,21 +434,42 @@ "integrity": "sha1-iYUI2iIm84DfkEcoRWhJwVAaSw0=", "dev": true }, - "async": { - "version": "1.5.2", - "resolved": "http://registry.npmjs.org/async/-/async-1.5.2.tgz", - "integrity": "sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo=", + "asn1": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz", + "integrity": "sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg==", + "dev": true, + "requires": { + "safer-buffer": "~2.1.0" + } + }, + "assert-plus": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=", "dev": true }, "async-limiter": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.0.tgz", - "integrity": "sha512-jp/uFnooOiO+L211eZOoSyzpOITMXx1rBITauYykG3BRYPu8h0UcxsPNB04RR5vo4Tyz3+ay17tR6JVf9qzYWg==" + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.1.tgz", + "integrity": "sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ==" + }, + "asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=", + "dev": true + }, + "aws-sign2": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", + "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=", + "dev": true }, - "babylon": { - "version": "7.0.0-beta.19", - "resolved": "https://registry.npmjs.org/babylon/-/babylon-7.0.0-beta.19.tgz", - "integrity": "sha512-Vg0C9s/REX6/WIXN37UKpv5ZhRi6A4pjHlpkE34+8/a6c2W1Q692n3hmc+SZG5lKRnaExLUbxtJ1SVT+KaCQ/A==", + "aws4": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.8.0.tgz", + "integrity": "sha512-ReZxvNHIOv88FlT7rxcXIIC0fPt4KZqZbOlivyWtXLt8ESx84zd3kMC6iK5jVeS2qt+g7ftS7ye4fi06X5rtRQ==", "dev": true }, "balanced-match": { @@ -163,26 +478,19 @@ "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", "dev": true }, - "bindings": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.3.0.tgz", - "integrity": "sha512-DpLh5EzMR2kzvX1KIlVC0VkC3iZtHKTgdtZ0a3pglBZdaQFjt5S9g9xd1lE+YvXyfd6mtCeRnrUfOLYiTMlNSw==", - "dev": true - }, - "bl": { - "version": "1.2.2", - "resolved": "http://registry.npmjs.org/bl/-/bl-1.2.2.tgz", - "integrity": "sha512-e8tQYnZodmebYDWGH7KMRvtzKXaJHx3BbilrgZCfvyLUYdKpK1t5PSPmpkny/SgiTSCnjfLW7v5rlONXVFkQEA==", + "bcrypt-pbkdf": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", + "integrity": "sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=", "dev": true, "requires": { - "readable-stream": "^2.3.5", - "safe-buffer": "^5.1.1" + "tweetnacl": "^0.14.3" } }, "bluebird": { - "version": "3.5.2", - "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.2.tgz", - "integrity": "sha512-dhHTWMI7kMx5whMQntl7Vr9C6BvV10lFXDAasnqnrMYhXVCzzk6IO9Fo2L75jXHT07WrOngL1WDXOp+yYS91Yg==", + "version": "3.7.1", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.1.tgz", + "integrity": "sha512-DdmyoGCleJnkbp3nkbxTLJ18rjDsE4yCggEwKNXkeV123sPNfOCYeDoeuOY+F2FrSjO1YXcTU+dsy96KMy+gcg==", "dev": true }, "brace-expansion": { @@ -195,21 +503,10 @@ "concat-map": "0.0.1" } }, - "buffer-alloc": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/buffer-alloc/-/buffer-alloc-1.2.0.tgz", - "integrity": "sha512-CFsHQgjtW1UChdXgbyJGtnm+O/uLQeZdtbDo8mfUgYXCHSM1wgrVxXm6bSyrUuErEb+4sYVGCzASBRot7zyrow==", - "dev": true, - "requires": { - "buffer-alloc-unsafe": "^1.1.0", - "buffer-fill": "^1.0.0" - } - }, - "buffer-alloc-unsafe": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/buffer-alloc-unsafe/-/buffer-alloc-unsafe-1.1.0.tgz", - "integrity": "sha512-TEM2iMIEQdJ2yjPJoSIsldnleVaAk1oW3DBVUykyOLsEsFmEc9kn+SFFPz+gl54KQNxlDnAwCXosOS9Okx2xAg==", - "dev": true + "browser-or-node": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/browser-or-node/-/browser-or-node-1.2.1.tgz", + "integrity": "sha512-sVIA0cysIED0nbmNOm7sZzKfgN1rpFmrqvLZaFWspaBAftfQcezlC81G6j6U2RJf4Lh66zFxrCeOsvkUXIcPWg==" }, "buffer-equals": { "version": "1.0.4", @@ -217,11 +514,17 @@ "integrity": "sha1-A1O1T9B/2VZBcGca5vZrnPENJ/U=", "dev": true }, - "buffer-fill": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/buffer-fill/-/buffer-fill-1.0.0.tgz", - "integrity": "sha1-+PeLdniYiO858gXNY39o5wISKyw=", - "dev": true + "caching-transform": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/caching-transform/-/caching-transform-4.0.0.tgz", + "integrity": "sha512-kpqOvwXnjjN44D89K5ccQC+RUrsy7jB/XLlRrx0D7/2HNcTPqzsb6XgYoErwko6QsV184CA2YgS1fxDiiDZMWA==", + "dev": true, + "requires": { + "hasha": "^5.0.0", + "make-dir": "^3.0.0", + "package-hash": "^4.0.0", + "write-file-atomic": "^3.0.0" + } }, "caller-path": { "version": "0.1.0", @@ -238,13 +541,25 @@ "integrity": "sha1-r6uWJikQp/M8GaV3WCXGnzTjUMo=", "dev": true }, + "camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "dev": true + }, + "caseless": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", + "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=", + "dev": true + }, "catharsis": { - "version": "0.8.9", - "resolved": "https://registry.npmjs.org/catharsis/-/catharsis-0.8.9.tgz", - "integrity": "sha1-mMyJDKZS3S7w5ws3klMQ/56Q/Is=", + "version": "0.8.11", + "resolved": "https://registry.npmjs.org/catharsis/-/catharsis-0.8.11.tgz", + "integrity": "sha512-a+xUyMV7hD1BrDQA/3iPV7oc+6W26BgVJO05PGEoatMyIuPScQKsde6i3YorWX1qs+AZjnJ18NqdKoCtKiNh1g==", "dev": true, "requires": { - "underscore-contrib": "~0.3.0" + "lodash": "^4.17.14" } }, "chalk": { @@ -264,18 +579,18 @@ "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==", "dev": true }, - "chownr": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.1.tgz", - "integrity": "sha512-j38EvO5+LHX84jlo6h4UzmOwi0UgW61WRyPtJz4qaadK5eY3BTS5TY/S1Stc3Uk2lIM6TPevAlULiEJwie860g==", - "dev": true - }, "circular-json": { "version": "0.3.3", "resolved": "https://registry.npmjs.org/circular-json/-/circular-json-0.3.3.tgz", "integrity": "sha512-UZK3NBx2Mca+b5LsG7bY183pHWt5Y1xts4P3Pz7ENTwGVnJOUWbRb3ocjvX7hx9tq/yTAdclXm9sZ38gNuem4A==", "dev": true }, + "clean-stack": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", + "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==", + "dev": true + }, "cli-cursor": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-2.1.0.tgz", @@ -291,11 +606,50 @@ "integrity": "sha1-/xnt6Kml5XkyQUewwR8PvLq+1jk=", "dev": true }, - "code-point-at": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", - "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=", - "dev": true + "cliui": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz", + "integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==", + "dev": true, + "requires": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^6.2.0" + }, + "dependencies": { + "ansi-regex": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", + "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true + }, + "string-width": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.0.tgz", + "integrity": "sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg==", + "dev": true, + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.0" + } + }, + "strip-ansi": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", + "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", + "dev": true, + "requires": { + "ansi-regex": "^5.0.0" + } + } + } }, "color-convert": { "version": "1.9.3", @@ -312,12 +666,27 @@ "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", "dev": true }, + "combined-stream": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.7.tgz", + "integrity": "sha512-brWl9y6vOB1xYPZcpZde3N9zDByXTosAeMDo4p1wzo6UMOX4vumB+TP1RZ76sfE6Md68Q0NJSrE/gbezd4Ul+w==", + "dev": true, + "requires": { + "delayed-stream": "~1.0.0" + } + }, "commander": { "version": "2.19.0", "resolved": "https://registry.npmjs.org/commander/-/commander-2.19.0.tgz", "integrity": "sha512-6tvAOO+D6OENvRAh524Dh9jcfKTYDQAqvqezbCW82xj5X0pSrcpxtvRKHLG0yBY6SD7PSDrJaj+0AiOcKVd1Xg==", "dev": true }, + "commondir": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", + "integrity": "sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs=", + "dev": true + }, "concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", @@ -336,11 +705,14 @@ "utils-merge": "1.0.1" } }, - "console-control-strings": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", - "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=", - "dev": true + "convert-source-map": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.7.0.tgz", + "integrity": "sha512-4FJkXzKXEDB1snCFZlLP4gpC3JILicCpGbzG9f9G7tGqGCzETQ2hWPrcinA9oU4wtf2biUaEH5065UnMeR33oA==", + "dev": true, + "requires": { + "safe-buffer": "~5.1.1" + } }, "core-util-is": { "version": "1.0.2", @@ -361,6 +733,15 @@ "which": "^1.2.9" } }, + "dashdash": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", + "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=", + "dev": true, + "requires": { + "assert-plus": "^1.0.0" + } + }, "debug": { "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", @@ -370,14 +751,11 @@ "ms": "2.0.0" } }, - "decompress-response": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-3.3.0.tgz", - "integrity": "sha1-gKTdMjdIOEv6JICDYirt7Jgq3/M=", - "dev": true, - "requires": { - "mimic-response": "^1.0.0" - } + "decamelize": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=", + "dev": true }, "deep-equal": { "version": "1.0.1", @@ -385,18 +763,21 @@ "integrity": "sha1-9dJgKStmDghO/0zbyfCK0yR0SLU=", "dev": true }, - "deep-extend": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", - "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", - "dev": true - }, "deep-is": { "version": "0.1.3", "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz", "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=", "dev": true }, + "default-require-extensions": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/default-require-extensions/-/default-require-extensions-3.0.0.tgz", + "integrity": "sha512-ek6DpXq/SCpvjhpFsLFRVtIxJCRw6fUR42lYMVZuUMK7n8eMz4Uh5clckdBjEpLhn/gEBZo7hDJnJcwdKLKQjg==", + "dev": true, + "requires": { + "strip-bom": "^4.0.0" + } + }, "define-properties": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", @@ -427,16 +808,10 @@ "rimraf": "^2.2.8" } }, - "delegates": { + "delayed-stream": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", - "integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=", - "dev": true - }, - "detect-libc": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz", - "integrity": "sha1-+hN8S9aY7fVc1c0CrFWfkaTEups=", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=", "dev": true }, "doctrine": { @@ -448,26 +823,39 @@ "esutils": "^2.0.2" } }, + "ecc-jsbn": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", + "integrity": "sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=", + "dev": true, + "requires": { + "jsbn": "~0.1.0", + "safer-buffer": "^2.1.0" + } + }, "ee-first": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=", "dev": true }, + "emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, "encodeurl": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=", "dev": true }, - "end-of-stream": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.1.tgz", - "integrity": "sha512-1MkrZNvWTKCaigbn+W15elq2BB/L22nqrSY5DKlo3X6+vclJm8Bb5djXJBmEX6fS3+zCh/F4VBK5Z2KxJt4s2Q==", - "dev": true, - "requires": { - "once": "^1.4.0" - } + "entities": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/entities/-/entities-1.1.2.tgz", + "integrity": "sha512-f2LZMYl1Fzu7YSBKg+RoROelpOaNrcGmE9AZubeDfrCEia483oW4MI4VyFd5VNHIgQ/7qm1I0wUHK1eJnn2y2w==", + "dev": true }, "es-abstract": { "version": "1.12.0", @@ -493,6 +881,12 @@ "is-symbol": "^1.0.2" } }, + "es6-error": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/es6-error/-/es6-error-4.1.1.tgz", + "integrity": "sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg==", + "dev": true + }, "escape-html": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", @@ -505,37 +899,10 @@ "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", "dev": true }, - "escodegen": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.8.1.tgz", - "integrity": "sha1-WltTr0aTEQvrsIZ6o0MN07cKEBg=", - "dev": true, - "requires": { - "esprima": "^2.7.1", - "estraverse": "^1.9.1", - "esutils": "^2.0.2", - "optionator": "^0.8.1", - "source-map": "~0.2.0" - }, - "dependencies": { - "esprima": { - "version": "2.7.3", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-2.7.3.tgz", - "integrity": "sha1-luO3DVd59q1JzQMmc9HDEnZ7pYE=", - "dev": true - }, - "estraverse": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-1.9.3.tgz", - "integrity": "sha1-r2fy3JIlgkFZUJJgkaQAXSnJu0Q=", - "dev": true - } - } - }, - "eslint": { - "version": "5.7.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-5.7.0.tgz", - "integrity": "sha512-zYCeFQahsxffGl87U2aJ7DPyH8CbWgxBC213Y8+TCanhUTf2gEvfq3EKpHmEcozTLyPmGe9LZdMAwC/CpJBM5A==", + "eslint": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-5.7.0.tgz", + "integrity": "sha512-zYCeFQahsxffGl87U2aJ7DPyH8CbWgxBC213Y8+TCanhUTf2gEvfq3EKpHmEcozTLyPmGe9LZdMAwC/CpJBM5A==", "dev": true, "requires": { "@babel/code-frame": "^7.0.0", @@ -624,10 +991,21 @@ } }, "eslint-utils": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-1.3.1.tgz", - "integrity": "sha512-Z7YjnIldX+2XMcjr7ZkgEsOj/bREONV60qYeB/bjMAqqqZ4zxKyWX+BOUkdmRmA9riiIPVvo5x86m5elviOk0Q==", - "dev": true + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-1.4.3.tgz", + "integrity": "sha512-fbBN5W2xdY45KulGXmLHZ3c3FHfVYmKg0IrAKGOkT/464PQsx2UeIzfz1RmEci+KLm1bBaAzZAh8+/E+XAeZ8Q==", + "dev": true, + "requires": { + "eslint-visitor-keys": "^1.1.0" + }, + "dependencies": { + "eslint-visitor-keys": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.1.0.tgz", + "integrity": "sha512-8y9YjtM1JBJU/A9Kc+SbaOV4y29sSWckBwMHa+FGtVj5gN/sbnKDf6xJUl+8g7FAij9LVaP8C24DUiH/f/2Z9A==", + "dev": true + } + } }, "eslint-visitor-keys": { "version": "1.0.0", @@ -682,10 +1060,10 @@ "integrity": "sha1-Cr9PHKpbyx96nYrMbepPqqBLrJs=", "dev": true }, - "expand-template": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/expand-template/-/expand-template-1.1.1.tgz", - "integrity": "sha512-cebqLtV8KOZfw0UI8TEFWxtczxxC1jvyUvx6H4fyp1K1FN7A4Q+uggVUlOsI1K8AGU0rwOGqP8nCapdrw8CYQg==", + "extend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", "dev": true }, "external-editor": { @@ -699,6 +1077,12 @@ "tmp": "^0.0.33" } }, + "extsprintf": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", + "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=", + "dev": true + }, "fast-deep-equal": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz", @@ -757,6 +1141,27 @@ "unpipe": "~1.0.0" } }, + "find-cache-dir": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-3.2.0.tgz", + "integrity": "sha512-1JKclkYYsf1q9WIJKLZa9S9muC+08RIjzAlLrK4QcYLJMS6mk9yombQ9qf+zJ7H9LS800k0s44L4sDq9VYzqyg==", + "dev": true, + "requires": { + "commondir": "^1.0.1", + "make-dir": "^3.0.0", + "pkg-dir": "^4.1.0" + } + }, + "find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "requires": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + } + }, "flat-cache": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-1.3.0.tgz", @@ -778,10 +1183,80 @@ "is-callable": "^1.1.3" } }, - "fs-constants": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", - "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==", + "foreground-child": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-2.0.0.tgz", + "integrity": "sha512-dCIq9FpEcyQyXKCkyzmlPTFNgrCzPudOe+mhvJU5zAtlBnGVy2yKxtfsxK2tQBThwq225jcvBjpw1Gr40uzZCA==", + "dev": true, + "requires": { + "cross-spawn": "^7.0.0", + "signal-exit": "^3.0.2" + }, + "dependencies": { + "cross-spawn": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.1.tgz", + "integrity": "sha512-u7v4o84SwFpD32Z8IIcPZ6z1/ie24O6RU3RbtL5Y316l3KuHVPx9ItBgWQ6VlfAFnRnTtMUrsQ9MUUTuEZjogg==", + "dev": true, + "requires": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + } + }, + "path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true + }, + "shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "requires": { + "shebang-regex": "^3.0.0" + } + }, + "shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true + }, + "which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "requires": { + "isexe": "^2.0.0" + } + } + } + }, + "forever-agent": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", + "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=", + "dev": true + }, + "form-data": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", + "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", + "dev": true, + "requires": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.6", + "mime-types": "^2.1.12" + } + }, + "fromentries": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/fromentries/-/fromentries-1.2.0.tgz", + "integrity": "sha512-33X7H/wdfO99GdRLLgkjUrD4geAFdq/Uv0kl3HD4da6HDixd2GUg8Mw7dahLCV9r/EARkmtYBB6Tch4EEokFTQ==", "dev": true }, "fs.realpath": { @@ -802,58 +1277,17 @@ "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=", "dev": true }, - "gauge": { - "version": "2.7.4", - "resolved": "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz", - "integrity": "sha1-LANAXHU4w51+s3sxcCLjJfsBi/c=", - "dev": true, - "requires": { - "aproba": "^1.0.3", - "console-control-strings": "^1.0.0", - "has-unicode": "^2.0.0", - "object-assign": "^4.1.0", - "signal-exit": "^3.0.0", - "string-width": "^1.0.1", - "strip-ansi": "^3.0.1", - "wide-align": "^1.1.0" - }, - "dependencies": { - "ansi-regex": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", - "dev": true - }, - "is-fullwidth-code-point": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", - "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", - "dev": true, - "requires": { - "number-is-nan": "^1.0.0" - } - }, - "string-width": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", - "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", - "dev": true, - "requires": { - "code-point-at": "^1.0.0", - "is-fullwidth-code-point": "^1.0.0", - "strip-ansi": "^3.0.0" - } - }, - "strip-ansi": { - "version": "3.0.1", - "resolved": "http://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", - "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", - "dev": true, - "requires": { - "ansi-regex": "^2.0.0" - } - } - } + "gensync": { + "version": "1.0.0-beta.1", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.1.tgz", + "integrity": "sha512-r8EC6NO1sngH/zdD9fiRDLdcgnbayXah+mLgManTaIZJqEC1MZstmnox8KpnI2/fxQwrp5OpCOYWLp4rBl4Jcg==", + "dev": true + }, + "get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true }, "get-stdin": { "version": "6.0.0", @@ -861,11 +1295,14 @@ "integrity": "sha512-jp4tHawyV7+fkkSKyvjuLZswblUtz+SQKzSWnBbii16BuZksJlU1wuBYXY75r+duh/llF1ur6oNwi+2ZzjKZ7g==", "dev": true }, - "github-from-package": { - "version": "0.0.0", - "resolved": "https://registry.npmjs.org/github-from-package/-/github-from-package-0.0.0.tgz", - "integrity": "sha1-l/tdlr/eiXMxPyDoKI75oWf6ZM4=", - "dev": true + "getpass": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", + "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=", + "dev": true, + "requires": { + "assert-plus": "^1.0.0" + } }, "glob": { "version": "7.1.3", @@ -907,32 +1344,33 @@ "integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg=", "dev": true }, - "handlebars": { - "version": "4.0.12", - "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.0.12.tgz", - "integrity": "sha512-RhmTekP+FZL+XNhwS1Wf+bTTZpdLougwt5pcgA1tuz6Jcx0fpH/7z0qd71RKnZHBCxIRBHfBOnio4gViPemNzA==", + "har-schema": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", + "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=", + "dev": true + }, + "har-validator": { + "version": "5.1.3", + "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.3.tgz", + "integrity": "sha512-sNvOCzEQNr/qrvJgc3UG/kD4QtlHycrzwS+6mfTrrSq97BvaYcPZZI1ZSqGSPR73Cxn4LKTD4PttRwfU7jWq5g==", "dev": true, "requires": { - "async": "^2.5.0", - "optimist": "^0.6.1", - "source-map": "^0.6.1", - "uglify-js": "^3.1.4" + "ajv": "^6.5.5", + "har-schema": "^2.0.0" }, "dependencies": { - "async": { - "version": "2.6.1", - "resolved": "https://registry.npmjs.org/async/-/async-2.6.1.tgz", - "integrity": "sha512-fNEiL2+AZt6AlAw/29Cr0UDe4sRAHCpEHh54WMz+Bb7QfNcFw4h3loofyJpLeQs4Yx7yuqu/2dLgM5hKOs6HlQ==", + "ajv": { + "version": "6.7.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.7.0.tgz", + "integrity": "sha512-RZXPviBTtfmtka9n9sy1N5M5b82CbxWIR6HIis4s3WQTXDJamc/0gpCWNGz6EWdWp4DOfjzJfhz/AS9zVPjjWg==", "dev": true, "requires": { - "lodash": "^4.17.10" + "fast-deep-equal": "^2.0.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" } - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true } } }, @@ -957,12 +1395,61 @@ "integrity": "sha1-uhqPGvKg/DllD1yFA2dwQSIGO0Q=", "dev": true }, - "has-unicode": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", - "integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=", + "hasha": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/hasha/-/hasha-5.2.0.tgz", + "integrity": "sha512-2W+jKdQbAdSIrggA8Q35Br8qKadTrqCTC8+XZvBWepKDK6m9XkX6Iz1a2yh2KP01kzAR/dpuMeUnocoLYDcskw==", + "dev": true, + "requires": { + "is-stream": "^2.0.0", + "type-fest": "^0.8.0" + } + }, + "html-escaper": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.0.tgz", + "integrity": "sha512-a4u9BeERWGu/S8JiWEAQcdrg9v4QArtP9keViQjGMdff20fBdd8waotXaNmODqBe6uZ3Nafi7K/ho4gCQHV3Ig==", "dev": true }, + "html-validator": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/html-validator/-/html-validator-3.1.3.tgz", + "integrity": "sha512-RhjcQIHS/SfYzQ+/JrFWKU6AVve6AuwftAG/cWX3+bpvBK/tGMqbOleKlsAxLrKD84+GSJ1oJGnkyhdVLBGCqg==", + "dev": true, + "requires": { + "request": "2.88.0", + "valid-url": "1.0.9" + } + }, + "html-validator-cli": { + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/html-validator-cli/-/html-validator-cli-4.1.4.tgz", + "integrity": "sha512-4vGP107UDhhNHeWA5N8j/nUPlQbtB/W/K2x/P7aElbWMWrOkJA0MRSVFsMFrTPSAAjZWCG9uki2+1cQDzFtVcQ==", + "dev": true, + "requires": { + "html-validator": "3.1.3", + "minimist": "1.2.0" + }, + "dependencies": { + "minimist": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", + "dev": true + } + } + }, + "http-signature": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", + "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=", + "dev": true, + "requires": { + "assert-plus": "^1.0.0", + "jsprim": "^1.2.2", + "sshpk": "^1.7.0" + } + }, "iconv-lite": { "version": "0.4.24", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", @@ -984,6 +1471,12 @@ "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", "dev": true }, + "indent-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", + "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", + "dev": true + }, "inflight": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", @@ -1000,12 +1493,6 @@ "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", "dev": true }, - "ini": { - "version": "1.3.5", - "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.5.tgz", - "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==", - "dev": true - }, "inquirer": { "version": "6.2.0", "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-6.2.0.tgz", @@ -1090,6 +1577,12 @@ "integrity": "sha512-qgDYXFSR5WvEfuS5dMj6oTMEbrrSaM0CrFk2Yiq/gXnBvD9pMa2jGXxyhGLfvhZpuMZe18CJpFxAt3CRs42NMg==", "dev": true }, + "is-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.0.tgz", + "integrity": "sha512-XCoy+WlUr7d1+Z8GgSuXmpuUFC9fOhRXglJMx+dwLKTkL44Cjd4W1Z5P+BQZpr+cR93aGP4S/s7Ftw6Nd/kiEw==", + "dev": true + }, "is-symbol": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.2.tgz", @@ -1099,10 +1592,16 @@ "has-symbols": "^1.0.0" } }, - "isarray": { + "is-typedarray": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", + "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", + "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=", + "dev": true + }, + "is-windows": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz", + "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==", "dev": true }, "isexe": { @@ -1111,64 +1610,206 @@ "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", "dev": true }, - "istanbul": { - "version": "0.4.5", - "resolved": "https://registry.npmjs.org/istanbul/-/istanbul-0.4.5.tgz", - "integrity": "sha1-ZcfXPUxNqE1POsMQuRj7C4Azczs=", - "dev": true, - "requires": { - "abbrev": "1.0.x", - "async": "1.x", - "escodegen": "1.8.x", - "esprima": "2.7.x", - "glob": "^5.0.15", - "handlebars": "^4.0.1", - "js-yaml": "3.x", - "mkdirp": "0.5.x", - "nopt": "3.x", - "once": "1.x", - "resolve": "1.1.x", - "supports-color": "^3.1.0", - "which": "^1.1.1", - "wordwrap": "^1.0.0" + "isomorphic-ws": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/isomorphic-ws/-/isomorphic-ws-4.0.1.tgz", + "integrity": "sha512-BhBvN2MBpWTaSHdWRb/bwdZJ1WaehQ2L1KngkCkfLUGF0mAWAT1sQUQacEmQ0jXkFw/czDXPNQSL5u2/Krsz1w==" + }, + "isstream": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", + "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=", + "dev": true + }, + "istanbul-lib-coverage": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.0.0.tgz", + "integrity": "sha512-UiUIqxMgRDET6eR+o5HbfRYP1l0hqkWOs7vNxC/mggutCMUIhWMm8gAHb8tHlyfD3/l6rlgNA5cKdDzEAf6hEg==", + "dev": true + }, + "istanbul-lib-hook": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-hook/-/istanbul-lib-hook-3.0.0.tgz", + "integrity": "sha512-Pt/uge1Q9s+5VAZ+pCo16TYMWPBIl+oaNIjgLQxcX0itS6ueeaA+pEfThZpH8WxhFgCiEb8sAJY6MdUKgiIWaQ==", + "dev": true, + "requires": { + "append-transform": "^2.0.0" + } + }, + "istanbul-lib-instrument": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-4.0.1.tgz", + "integrity": "sha512-imIchxnodll7pvQBYOqUu88EufLCU56LMeFPZZM/fJZ1irYcYdqroaV+ACK1Ila8ls09iEYArp+nqyC6lW1Vfg==", + "dev": true, + "requires": { + "@babel/core": "^7.7.5", + "@babel/parser": "^7.7.5", + "@babel/template": "^7.7.4", + "@babel/traverse": "^7.7.4", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-coverage": "^3.0.0", + "semver": "^6.3.0" }, "dependencies": { - "esprima": { - "version": "2.7.3", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-2.7.3.tgz", - "integrity": "sha1-luO3DVd59q1JzQMmc9HDEnZ7pYE=", + "@babel/parser": { + "version": "7.8.4", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.8.4.tgz", + "integrity": "sha512-0fKu/QqildpXmPVaRBoXOlyBb3MC+J0A66x97qEfLOMkn3u6nfY5esWogQwi/K0BjASYy4DbnsEWnpNL6qT5Mw==", "dev": true }, - "glob": { - "version": "5.0.15", - "resolved": "https://registry.npmjs.org/glob/-/glob-5.0.15.tgz", - "integrity": "sha1-G8k2ueAvSmA/zCIuz3Yz0wuLk7E=", + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + } + } + }, + "istanbul-lib-processinfo": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/istanbul-lib-processinfo/-/istanbul-lib-processinfo-2.0.2.tgz", + "integrity": "sha512-kOwpa7z9hme+IBPZMzQ5vdQj8srYgAtaRqeI48NGmAQ+/5yKiHLV0QbYqQpxsdEF0+w14SoB8YbnHKcXE2KnYw==", + "dev": true, + "requires": { + "archy": "^1.0.0", + "cross-spawn": "^7.0.0", + "istanbul-lib-coverage": "^3.0.0-alpha.1", + "make-dir": "^3.0.0", + "p-map": "^3.0.0", + "rimraf": "^3.0.0", + "uuid": "^3.3.3" + }, + "dependencies": { + "cross-spawn": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.1.tgz", + "integrity": "sha512-u7v4o84SwFpD32Z8IIcPZ6z1/ie24O6RU3RbtL5Y316l3KuHVPx9ItBgWQ6VlfAFnRnTtMUrsQ9MUUTuEZjogg==", "dev": true, "requires": { - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "2 || 3", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" } }, + "path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true + }, + "rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "dev": true, + "requires": { + "glob": "^7.1.3" + } + }, + "shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "requires": { + "shebang-regex": "^3.0.0" + } + }, + "shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true + }, + "uuid": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", + "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==", + "dev": true + }, + "which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "requires": { + "isexe": "^2.0.0" + } + } + } + }, + "istanbul-lib-report": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz", + "integrity": "sha512-wcdi+uAKzfiGT2abPpKZ0hSU1rGQjUQnLvtY5MpQ7QCTahD3VODhcu4wcfY1YtkGaDD5yuydOLINXsfbus9ROw==", + "dev": true, + "requires": { + "istanbul-lib-coverage": "^3.0.0", + "make-dir": "^3.0.0", + "supports-color": "^7.1.0" + }, + "dependencies": { "has-flag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz", - "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "dev": true }, "supports-color": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz", - "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=", + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz", + "integrity": "sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "istanbul-lib-source-maps": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.0.tgz", + "integrity": "sha512-c16LpFRkR8vQXyHZ5nLpY35JZtzj1PQY1iZmesUbf1FZHbIupcWfjgOXBY9YHkLEQ6puz1u4Dgj6qmU/DisrZg==", + "dev": true, + "requires": { + "debug": "^4.1.1", + "istanbul-lib-coverage": "^3.0.0", + "source-map": "^0.6.1" + }, + "dependencies": { + "debug": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", + "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", "dev": true, "requires": { - "has-flag": "^1.0.0" + "ms": "^2.1.1" } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true } } }, + "istanbul-reports": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.0.0.tgz", + "integrity": "sha512-2osTcC8zcOSUkImzN2EWQta3Vdi4WjjKw99P2yWx5mLnigAM0Rd5uYFn1cf2i/Ois45GkNjaoTqc5CxgMSX80A==", + "dev": true, + "requires": { + "html-escaper": "^2.0.0", + "istanbul-lib-report": "^3.0.0" + } + }, "js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", @@ -1176,9 +1817,9 @@ "dev": true }, "js-yaml": { - "version": "3.12.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.12.0.tgz", - "integrity": "sha512-PIt2cnwmPfL4hKNwqeiuz4bKfnzHTBv6HyVgjahA6mPLwPDzjDWrplJBMjHUFxku/N3FlmrbyPclad+I+4mJ3A==", + "version": "3.13.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.13.1.tgz", + "integrity": "sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw==", "dev": true, "requires": { "argparse": "^1.0.7", @@ -1186,34 +1827,77 @@ } }, "js2xmlparser": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/js2xmlparser/-/js2xmlparser-3.0.0.tgz", - "integrity": "sha1-P7YOqgicVED5MZ9RdgzNB+JJlzM=", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js2xmlparser/-/js2xmlparser-4.0.0.tgz", + "integrity": "sha512-WuNgdZOXVmBk5kUPMcTcVUpbGRzLfNkv7+7APq7WiDihpXVKrgxo6wwRpRl9OQeEBgKCVk9mR7RbzrnNWC8oBw==", "dev": true, "requires": { - "xmlcreate": "^1.0.1" + "xmlcreate": "^2.0.0" } }, + "jsbn": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", + "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=", + "dev": true + }, "jsdoc": { - "version": "3.5.5", - "resolved": "https://registry.npmjs.org/jsdoc/-/jsdoc-3.5.5.tgz", - "integrity": "sha512-6PxB65TAU4WO0Wzyr/4/YhlGovXl0EVYfpKbpSroSj0qBxT4/xod/l40Opkm38dRHRdQgdeY836M0uVnJQG7kg==", - "dev": true, - "requires": { - "babylon": "7.0.0-beta.19", - "bluebird": "~3.5.0", - "catharsis": "~0.8.9", - "escape-string-regexp": "~1.0.5", - "js2xmlparser": "~3.0.0", - "klaw": "~2.0.0", - "marked": "~0.3.6", - "mkdirp": "~0.5.1", - "requizzle": "~0.2.1", - "strip-json-comments": "~2.0.1", + "version": "3.6.3", + "resolved": "https://registry.npmjs.org/jsdoc/-/jsdoc-3.6.3.tgz", + "integrity": "sha512-Yf1ZKA3r9nvtMWHO1kEuMZTlHOF8uoQ0vyo5eH7SQy5YeIiHM+B0DgKnn+X6y6KDYZcF7G2SPkKF+JORCXWE/A==", + "dev": true, + "requires": { + "@babel/parser": "^7.4.4", + "bluebird": "^3.5.4", + "catharsis": "^0.8.11", + "escape-string-regexp": "^2.0.0", + "js2xmlparser": "^4.0.0", + "klaw": "^3.0.0", + "markdown-it": "^8.4.2", + "markdown-it-anchor": "^5.0.2", + "marked": "^0.7.0", + "mkdirp": "^0.5.1", + "requizzle": "^0.2.3", + "strip-json-comments": "^3.0.1", "taffydb": "2.6.2", - "underscore": "~1.8.3" + "underscore": "~1.9.1" + }, + "dependencies": { + "escape-string-regexp": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", + "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", + "dev": true + }, + "strip-json-comments": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.0.1.tgz", + "integrity": "sha512-VTyMAUfdm047mwKl+u79WIdrZxtFtn+nBxHeb844XBQ9uMNTuTHdx2hc5RiAJYqwTj3wc/xe5HLSdJSkJ+WfZw==", + "dev": true + } + } + }, + "jsesc": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", + "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", + "dev": true + }, + "json-int64": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json-int64/-/json-int64-1.0.2.tgz", + "integrity": "sha512-uGrIXtRehbksM17S2lJRLPljufK52KL2ewbJi0xgcRPONoRLXa4yAUIKAUxF69dbnqIoBu33fB28MAWSxupB8Q==", + "dev": true, + "requires": { + "node-int64": "0.4.0" } }, + "json-schema": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz", + "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=", + "dev": true + }, "json-schema-traverse": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", @@ -1226,10 +1910,45 @@ "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=", "dev": true }, + "json-stringify-safe": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", + "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=", + "dev": true + }, + "json5": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.1.1.tgz", + "integrity": "sha512-l+3HXD0GEI3huGq1njuqtzYK8OYJyXMkOLtQ53pjWh89tvWS2h6l+1zMkYWqlb57+SiQodKZyvMEFb2X+KrFhQ==", + "dev": true, + "requires": { + "minimist": "^1.2.0" + }, + "dependencies": { + "minimist": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", + "dev": true + } + } + }, + "jsprim": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz", + "integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=", + "dev": true, + "requires": { + "assert-plus": "1.0.0", + "extsprintf": "1.3.0", + "json-schema": "0.2.3", + "verror": "1.10.0" + } + }, "klaw": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/klaw/-/klaw-2.0.0.tgz", - "integrity": "sha1-WcEo4Nxc5BAgEVEZTuucv4WGUPY=", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/klaw/-/klaw-3.0.0.tgz", + "integrity": "sha512-0Fo5oir+O9jnXu5EefYbVK+mHMBeEVEy2cmctR1O1NECcCkPRreJKrS6Qt/j3KC2C148Dfo9i3pCmCMsdqGr0g==", "dev": true, "requires": { "graceful-fs": "^4.1.9" @@ -1245,30 +1964,105 @@ "type-check": "~0.3.2" } }, + "linkify-it": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-2.2.0.tgz", + "integrity": "sha512-GnAl/knGn+i1U/wjBz3akz2stz+HrHLsxMwHQGofCDfPvlf+gDKN58UtfmUquTY4/MXeE2x7k19KQmeoZi94Iw==", + "dev": true, + "requires": { + "uc.micro": "^1.0.1" + } + }, + "locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "requires": { + "p-locate": "^4.1.0" + } + }, "lodash": { - "version": "4.17.11", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.11.tgz", - "integrity": "sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg==", + "version": "4.17.15", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz", + "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==", + "dev": true + }, + "lodash.flattendeep": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/lodash.flattendeep/-/lodash.flattendeep-4.4.0.tgz", + "integrity": "sha1-+wMJF/hqMTTlvJvsDWngAT3f7bI=", + "dev": true + }, + "make-dir": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.0.2.tgz", + "integrity": "sha512-rYKABKutXa6vXTXhoV18cBE7PaewPXHe/Bdq4v+ZLMhxbWApkFFplT0LcbMW+6BbjnQXzZ/sAvSE/JdguApG5w==", + "dev": true, + "requires": { + "semver": "^6.0.0" + }, + "dependencies": { + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + } + } + }, + "markdown-it": { + "version": "8.4.2", + "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-8.4.2.tgz", + "integrity": "sha512-GcRz3AWTqSUphY3vsUqQSFMbgR38a4Lh3GWlHRh/7MRwz8mcu9n2IO7HOh+bXHrR9kOPDl5RNCaEsrneb+xhHQ==", + "dev": true, + "requires": { + "argparse": "^1.0.7", + "entities": "~1.1.1", + "linkify-it": "^2.0.0", + "mdurl": "^1.0.1", + "uc.micro": "^1.0.5" + } + }, + "markdown-it-anchor": { + "version": "5.2.5", + "resolved": "https://registry.npmjs.org/markdown-it-anchor/-/markdown-it-anchor-5.2.5.tgz", + "integrity": "sha512-xLIjLQmtym3QpoY9llBgApknl7pxAcN3WDRc2d3rwpl+/YvDZHPmKscGs+L6E05xf2KrCXPBvosWt7MZukwSpQ==", "dev": true }, "marked": { - "version": "0.3.19", - "resolved": "http://registry.npmjs.org/marked/-/marked-0.3.19.tgz", - "integrity": "sha512-ea2eGWOqNxPcXv8dyERdSr/6FmzvWwzjMxpfGB/sbMccXoct+xY+YukPD+QTUZwyvK7BZwcr4m21WBOW41pAkg==", + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/marked/-/marked-0.7.0.tgz", + "integrity": "sha512-c+yYdCZJQrsRjTPhUx7VKkApw9bwDkNbHUKo1ovgcfDjb2kc8rLuRbIFyXL5WOEUwzSSKo3IXpph2K6DqB/KZg==", + "dev": true + }, + "mdurl": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/mdurl/-/mdurl-1.0.1.tgz", + "integrity": "sha1-/oWy7HWlkDfyrf7BAP1sYBdhFS4=", + "dev": true + }, + "mime-db": { + "version": "1.37.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.37.0.tgz", + "integrity": "sha512-R3C4db6bgQhlIhPU48fUtdVmKnflq+hRdad7IyKhtFj06VPNVdk2RhiYL3UjQIlso8L+YxAtFkobT0VK+S/ybg==", "dev": true }, + "mime-types": { + "version": "2.1.21", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.21.tgz", + "integrity": "sha512-3iL6DbwpyLzjR3xHSFNFeb9Nz/M8WDkX33t1GFQnFOllWk8pOrh/LSrB5OXlnlW5P9LH73X6loW/eogc+F5lJg==", + "dev": true, + "requires": { + "mime-db": "~1.37.0" + } + }, "mimic-fn": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-1.2.0.tgz", "integrity": "sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ==", "dev": true }, - "mimic-response": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-1.0.1.tgz", - "integrity": "sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==", - "dev": true - }, "minimatch": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", @@ -1305,12 +2099,6 @@ "integrity": "sha1-MHXOk7whuPq0PhvE2n6BFe0ee6s=", "dev": true }, - "nan": { - "version": "2.10.0", - "resolved": "http://registry.npmjs.org/nan/-/nan-2.10.0.tgz", - "integrity": "sha512-bAdJv7fBLhWC+/Bls0Oza+mvTaNQtP+1RyhhhvD95pgUJz6XM5IzgmxOkItJ9tkoCiplvAnXI1tNmmUD/eScyA==", - "dev": true - }, "natural-compare": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", @@ -1323,51 +2111,103 @@ "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==", "dev": true }, - "node-abi": { - "version": "2.4.5", - "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-2.4.5.tgz", - "integrity": "sha512-aa/UC6Nr3+tqhHGRsAuw/edz7/q9nnetBrKWxj6rpTtm+0X9T1qU7lIEHMS3yN9JwAbRiKUbRRFy1PLz/y3aaA==", - "dev": true, - "requires": { - "semver": "^5.4.1" - } + "node-gyp-build": { + "version": "3.7.0", + "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-3.7.0.tgz", + "integrity": "sha512-L/Eg02Epx6Si2NXmedx+Okg+4UHqmaf3TNcxd50SF9NQGcJaON3AtU++kax69XV7YWz4tUspqZSAsVofhFKG2w==", + "dev": true }, "node-int64": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", "integrity": "sha1-h6kGXNs1XTGC2PlM4RGIuCXGijs=" }, - "noop-logger": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/noop-logger/-/noop-logger-0.1.1.tgz", - "integrity": "sha1-lKKxYzxPExdVMAfYlm/Q6EG2pMI=", - "dev": true - }, - "nopt": { - "version": "3.0.6", - "resolved": "https://registry.npmjs.org/nopt/-/nopt-3.0.6.tgz", - "integrity": "sha1-xkZdvwirzU2zWTF/eaxopkayj/k=", - "dev": true, - "requires": { - "abbrev": "1" - } - }, - "npmlog": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-4.1.2.tgz", - "integrity": "sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==", - "dev": true, - "requires": { - "are-we-there-yet": "~1.1.2", - "console-control-strings": "~1.1.0", - "gauge": "~2.7.3", - "set-blocking": "~2.0.0" + "node-preload": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/node-preload/-/node-preload-0.2.1.tgz", + "integrity": "sha512-RM5oyBy45cLEoHqCeh+MNuFAxO0vTFBLskvQbOKnEE7YTTSN4tbN8QWDIPQ6L+WvKsB/qLEGpYe2ZZ9d4W9OIQ==", + "dev": true, + "requires": { + "process-on-spawn": "^1.0.0" + } + }, + "nyc": { + "version": "15.0.0", + "resolved": "https://registry.npmjs.org/nyc/-/nyc-15.0.0.tgz", + "integrity": "sha512-qcLBlNCKMDVuKb7d1fpxjPR8sHeMVX0CHarXAVzrVWoFrigCkYR8xcrjfXSPi5HXM7EU78L6ywO7w1c5rZNCNg==", + "dev": true, + "requires": { + "@istanbuljs/load-nyc-config": "^1.0.0", + "@istanbuljs/schema": "^0.1.2", + "caching-transform": "^4.0.0", + "convert-source-map": "^1.7.0", + "decamelize": "^1.2.0", + "find-cache-dir": "^3.2.0", + "find-up": "^4.1.0", + "foreground-child": "^2.0.0", + "glob": "^7.1.6", + "istanbul-lib-coverage": "^3.0.0", + "istanbul-lib-hook": "^3.0.0", + "istanbul-lib-instrument": "^4.0.0", + "istanbul-lib-processinfo": "^2.0.2", + "istanbul-lib-report": "^3.0.0", + "istanbul-lib-source-maps": "^4.0.0", + "istanbul-reports": "^3.0.0", + "js-yaml": "^3.13.1", + "make-dir": "^3.0.0", + "node-preload": "^0.2.0", + "p-map": "^3.0.0", + "process-on-spawn": "^1.0.0", + "resolve-from": "^5.0.0", + "rimraf": "^3.0.0", + "signal-exit": "^3.0.2", + "spawn-wrap": "^2.0.0", + "test-exclude": "^6.0.0", + "uuid": "^3.3.3", + "yargs": "^15.0.2" + }, + "dependencies": { + "glob": { + "version": "7.1.6", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", + "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "dev": true + }, + "rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "dev": true, + "requires": { + "glob": "^7.1.3" + } + }, + "uuid": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", + "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==", + "dev": true + } } }, - "number-is-nan": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", - "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=", + "oauth-sign": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", + "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==", "dev": true }, "object-assign": { @@ -1415,24 +2255,6 @@ "mimic-fn": "^1.0.0" } }, - "optimist": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/optimist/-/optimist-0.6.1.tgz", - "integrity": "sha1-2j6nRob6IaGaERwybpDrFaAZZoY=", - "dev": true, - "requires": { - "minimist": "~0.0.1", - "wordwrap": "~0.0.2" - }, - "dependencies": { - "wordwrap": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.3.tgz", - "integrity": "sha1-o9XabNXAvAAI03I0u68b7WMFkQc=", - "dev": true - } - } - }, "optionator": { "version": "0.8.2", "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.2.tgz", @@ -1447,17 +2269,64 @@ "wordwrap": "~1.0.0" } }, - "os-homedir": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz", - "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=", + "os-tmpdir": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", + "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=", + "dev": true + }, + "p-limit": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.2.2.tgz", + "integrity": "sha512-WGR+xHecKTr7EbUEhyLSh5Dube9JtdiG78ufaeLxTgpudf/20KqyMioIUZJAezlTIi6evxuoUs9YXc11cU+yzQ==", + "dev": true, + "requires": { + "p-try": "^2.0.0" + } + }, + "p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "requires": { + "p-limit": "^2.2.0" + } + }, + "p-map": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-3.0.0.tgz", + "integrity": "sha512-d3qXVTF/s+W+CdJ5A29wywV2n8CQQYahlgz2bFiA+4eVNJbHJodPZ+/gXwPGh0bOqA+j8S+6+ckmvLGPk1QpxQ==", + "dev": true, + "requires": { + "aggregate-error": "^3.0.0" + } + }, + "p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", "dev": true }, - "os-tmpdir": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", - "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=", - "dev": true + "package-hash": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/package-hash/-/package-hash-4.0.0.tgz", + "integrity": "sha512-whdkPIooSu/bASggZ96BWVvZTRMOFxnyUG5PnTSGKoJE2gd5mbVNmR2Nj20QFzxYYgAXpoqC+AiXzl+UMRh7zQ==", + "dev": true, + "requires": { + "graceful-fs": "^4.1.15", + "hasha": "^5.0.0", + "lodash.flattendeep": "^4.4.0", + "release-zalgo": "^1.0.0" + }, + "dependencies": { + "graceful-fs": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.3.tgz", + "integrity": "sha512-a30VEBm4PEdx1dRB7MFK7BejejvCvBronbLjht+sHuGYj8PHs7M/5Z+rt5lw551vZ7yfTCj4Vuyy3mSJytDWRQ==", + "dev": true + } + } }, "parseurl": { "version": "1.3.2", @@ -1465,6 +2334,12 @@ "integrity": "sha1-/CidTtiZMRlGDBViUyYs3I3mW/M=", "dev": true }, + "path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true + }, "path-is-absolute": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", @@ -1489,6 +2364,12 @@ "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==", "dev": true }, + "performance-now": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", + "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=", + "dev": true + }, "pify": { "version": "2.3.0", "resolved": "http://registry.npmjs.org/pify/-/pify-2.3.0.tgz", @@ -1510,43 +2391,21 @@ "pinkie": "^2.0.0" } }, + "pkg-dir": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", + "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", + "dev": true, + "requires": { + "find-up": "^4.0.0" + } + }, "pluralize": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/pluralize/-/pluralize-7.0.0.tgz", "integrity": "sha512-ARhBOdzS3e41FbkW/XWrTEtukqqLoK5+Z/4UeDaLuSW+39JPeFgs4gCGqsrJHVZX0fUrx//4OF0K1CUGwlIFow==", "dev": true }, - "prebuild-install": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-4.0.0.tgz", - "integrity": "sha512-7tayxeYboJX0RbVzdnKyGl2vhQRWr6qfClEXDhOkXjuaOKCw2q8aiuFhONRYVsG/czia7KhpykIlI2S2VaPunA==", - "dev": true, - "requires": { - "detect-libc": "^1.0.3", - "expand-template": "^1.0.2", - "github-from-package": "0.0.0", - "minimist": "^1.2.0", - "mkdirp": "^0.5.1", - "node-abi": "^2.2.0", - "noop-logger": "^0.1.1", - "npmlog": "^4.0.1", - "os-homedir": "^1.0.1", - "pump": "^2.0.1", - "rc": "^1.1.6", - "simple-get": "^2.7.0", - "tar-fs": "^1.13.0", - "tunnel-agent": "^0.6.0", - "which-pm-runs": "^1.0.0" - }, - "dependencies": { - "minimist": { - "version": "1.2.0", - "resolved": "http://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", - "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", - "dev": true - } - } - }, "prelude-ls": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", @@ -1568,11 +2427,14 @@ "fast-diff": "^1.1.2" } }, - "process-nextick-args": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz", - "integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw==", - "dev": true + "process-on-spawn": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/process-on-spawn/-/process-on-spawn-1.0.0.tgz", + "integrity": "sha512-1WsPDsUSMmZH5LeMLegqkPDrsGgsWwk1Exipy2hvB0o/F0ASzbpIctSCcZIK1ykJvtTJULEH+20WOFjMvGnCTg==", + "dev": true, + "requires": { + "fromentries": "^1.2.0" + } }, "progress": { "version": "2.0.1", @@ -1580,15 +2442,11 @@ "integrity": "sha512-OE+a6vzqazc+K6LxJrX5UPyKFvGnL5CYmq2jFGNIBWHpc4QyE49/YOumcrpQFJpfejmvRtbJzgO1zPmMCqlbBg==", "dev": true }, - "pump": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/pump/-/pump-2.0.1.tgz", - "integrity": "sha512-ruPMNRkN3MHP1cWJc9OWr+T/xDP0jhXYCLfJcBuX54hhfIBnaQmAUMfDcG4DM5UMWByBbJY69QSphm3jtDKIkA==", - "dev": true, - "requires": { - "end-of-stream": "^1.1.0", - "once": "^1.3.1" - } + "psl": { + "version": "1.1.31", + "resolved": "https://registry.npmjs.org/psl/-/psl-1.1.31.tgz", + "integrity": "sha512-/6pt4+C+T+wZUieKR620OpzN/LlnNKuWjy1iFLQ/UG35JqHlR/89MP1d96dUfkf6Dne3TuLQzOYEYshJ+Hx8mw==", + "dev": true }, "punycode": { "version": "2.1.1", @@ -1601,40 +2459,11 @@ "resolved": "https://registry.npmjs.org/q/-/q-1.5.1.tgz", "integrity": "sha1-fjL3W0E4EpHQRhHxvxQQmsAGUdc=" }, - "rc": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", - "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", - "dev": true, - "requires": { - "deep-extend": "^0.6.0", - "ini": "~1.3.0", - "minimist": "^1.2.0", - "strip-json-comments": "~2.0.1" - }, - "dependencies": { - "minimist": { - "version": "1.2.0", - "resolved": "http://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", - "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", - "dev": true - } - } - }, - "readable-stream": { - "version": "2.3.6", - "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", - "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", - "dev": true, - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } + "qs": { + "version": "6.5.2", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", + "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==", + "dev": true }, "regexpp": { "version": "2.0.1", @@ -1642,6 +2471,55 @@ "integrity": "sha512-lv0M6+TkDVniA3aD1Eg0DVpfU/booSu7Eev3TDO/mZKHBfVjgCGTV4t4buppESEYDtkArYFOxTJWv6S5C+iaNw==", "dev": true }, + "release-zalgo": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/release-zalgo/-/release-zalgo-1.0.0.tgz", + "integrity": "sha1-CXALflB0Mpc5Mw5TXFqQ+2eFFzA=", + "dev": true, + "requires": { + "es6-error": "^4.0.1" + } + }, + "request": { + "version": "2.88.0", + "resolved": "https://registry.npmjs.org/request/-/request-2.88.0.tgz", + "integrity": "sha512-NAqBSrijGLZdM0WZNsInLJpkJokL72XYjUpnB0iwsRgxh7dB6COrHnTBNwN0E+lHDAJzu7kLAkDeY08z2/A0hg==", + "dev": true, + "requires": { + "aws-sign2": "~0.7.0", + "aws4": "^1.8.0", + "caseless": "~0.12.0", + "combined-stream": "~1.0.6", + "extend": "~3.0.2", + "forever-agent": "~0.6.1", + "form-data": "~2.3.2", + "har-validator": "~5.1.0", + "http-signature": "~1.2.0", + "is-typedarray": "~1.0.0", + "isstream": "~0.1.2", + "json-stringify-safe": "~5.0.1", + "mime-types": "~2.1.19", + "oauth-sign": "~0.9.0", + "performance-now": "^2.1.0", + "qs": "~6.5.2", + "safe-buffer": "^5.1.2", + "tough-cookie": "~2.4.3", + "tunnel-agent": "^0.6.0", + "uuid": "^3.3.2" + } + }, + "require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=", + "dev": true + }, + "require-main-filename": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", + "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", + "dev": true + }, "require-uncached": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/require-uncached/-/require-uncached-1.0.3.tgz", @@ -1653,28 +2531,14 @@ } }, "requizzle": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/requizzle/-/requizzle-0.2.1.tgz", - "integrity": "sha1-aUPDUwxNmn5G8c3dUcFY/GcM294=", + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/requizzle/-/requizzle-0.2.3.tgz", + "integrity": "sha512-YanoyJjykPxGHii0fZP0uUPEXpvqfBDxWV7s6GKAiiOsiqhX6vHNyW3Qzdmqp/iq/ExbhaGbVrjB4ruEVSM4GQ==", "dev": true, "requires": { - "underscore": "~1.6.0" - }, - "dependencies": { - "underscore": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.6.0.tgz", - "integrity": "sha1-izixDKze9jM3uLJOT/htRa6lKag=", - "dev": true - } + "lodash": "^4.17.14" } }, - "resolve": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.1.7.tgz", - "integrity": "sha1-IDEU2CrSxe2ejgQRs5ModeiJ6Xs=", - "dev": true - }, "resolve-from": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-1.0.1.tgz", @@ -1772,23 +2636,6 @@ "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=", "dev": true }, - "simple-concat": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.0.tgz", - "integrity": "sha1-c0TLuLbib7J9ZrL8hvn21Zl1IcY=", - "dev": true - }, - "simple-get": { - "version": "2.8.1", - "resolved": "https://registry.npmjs.org/simple-get/-/simple-get-2.8.1.tgz", - "integrity": "sha512-lSSHRSw3mQNUGPAYRqo7xy9dhKmxFXIjLjp4KHpf99GEH2VH7C3AM+Qfx6du6jhfUi6Vm7XnbEVEf7Wb6N8jRw==", - "dev": true, - "requires": { - "decompress-response": "^3.3.0", - "once": "^1.3.1", - "simple-concat": "^1.0.0" - } - }, "slice-ansi": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-1.0.0.tgz", @@ -1798,14 +2645,38 @@ "is-fullwidth-code-point": "^2.0.0" } }, - "source-map": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.2.0.tgz", - "integrity": "sha1-2rc/vPwrqBm03gO9b26qSBZLP50=", + "spawn-wrap": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/spawn-wrap/-/spawn-wrap-2.0.0.tgz", + "integrity": "sha512-EeajNjfN9zMnULLwhZZQU3GWBoFNkbngTUPfaawT4RkMiviTxcX0qfhVbGey39mfctfDHkWtuecgQ8NJcyQWHg==", "dev": true, - "optional": true, "requires": { - "amdefine": ">=0.0.4" + "foreground-child": "^2.0.0", + "is-windows": "^1.0.2", + "make-dir": "^3.0.0", + "rimraf": "^3.0.0", + "signal-exit": "^3.0.2", + "which": "^2.0.1" + }, + "dependencies": { + "rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "dev": true, + "requires": { + "glob": "^7.1.3" + } + }, + "which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "requires": { + "isexe": "^2.0.0" + } + } } }, "sprintf-js": { @@ -1814,6 +2685,23 @@ "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", "dev": true }, + "sshpk": { + "version": "1.16.1", + "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.16.1.tgz", + "integrity": "sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg==", + "dev": true, + "requires": { + "asn1": "~0.2.3", + "assert-plus": "^1.0.0", + "bcrypt-pbkdf": "^1.0.0", + "dashdash": "^1.12.0", + "ecc-jsbn": "~0.1.1", + "getpass": "^0.1.1", + "jsbn": "~0.1.0", + "safer-buffer": "^2.0.2", + "tweetnacl": "~0.14.0" + } + }, "statuses": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.3.1.tgz", @@ -1841,15 +2729,6 @@ "function-bind": "^1.0.2" } }, - "string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "dev": true, - "requires": { - "safe-buffer": "~5.1.0" - } - }, "strip-ansi": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", @@ -1859,6 +2738,12 @@ "ansi-regex": "^3.0.0" } }, + "strip-bom": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", + "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==", + "dev": true + }, "strip-json-comments": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", @@ -1930,45 +2815,33 @@ } } }, - "tar-fs": { - "version": "1.16.3", - "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-1.16.3.tgz", - "integrity": "sha512-NvCeXpYx7OsmOh8zIOP/ebG55zZmxLE0etfWRbWok+q2Qo8x/vOR/IJT1taADXPe+jsiu9axDb3X4B+iIgNlKw==", + "test-exclude": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", + "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", "dev": true, "requires": { - "chownr": "^1.0.1", - "mkdirp": "^0.5.1", - "pump": "^1.0.0", - "tar-stream": "^1.1.2" + "@istanbuljs/schema": "^0.1.2", + "glob": "^7.1.4", + "minimatch": "^3.0.4" }, "dependencies": { - "pump": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/pump/-/pump-1.0.3.tgz", - "integrity": "sha512-8k0JupWme55+9tCVE+FS5ULT3K6AbgqrGa58lTT49RpyfwwcGedHqaC5LlQNdEAumn/wFsu6aPwkuPMioy8kqw==", + "glob": { + "version": "7.1.6", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", + "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", "dev": true, "requires": { - "end-of-stream": "^1.1.0", - "once": "^1.3.1" + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" } } } }, - "tar-stream": { - "version": "1.6.2", - "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-1.6.2.tgz", - "integrity": "sha512-rzS0heiNf8Xn7/mpdSVVSMAWAoy9bfb1WOTYC78Z0UQKeKa/CWS8FOq0lKGNa8DWKAn9gxjCvMLYc5PGXYlK2A==", - "dev": true, - "requires": { - "bl": "^1.0.0", - "buffer-alloc": "^1.2.0", - "end-of-stream": "^1.0.0", - "fs-constants": "^1.0.0", - "readable-stream": "^2.3.0", - "to-buffer": "^1.1.1", - "xtend": "^4.0.0" - } - }, "text-table": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", @@ -1990,12 +2863,30 @@ "os-tmpdir": "~1.0.2" } }, - "to-buffer": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/to-buffer/-/to-buffer-1.1.1.tgz", - "integrity": "sha512-lx9B5iv7msuFYE3dytT+KE5tap+rNYw+K4jVkb9R/asAb+pbBSM17jtunHplhBe6RRJdZx3Pn2Jph24O32mOVg==", + "to-fast-properties": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", + "integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=", "dev": true }, + "tough-cookie": { + "version": "2.4.3", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.4.3.tgz", + "integrity": "sha512-Q5srk/4vDM54WJsJio3XNn6K2sCG+CQ8G5Wz6bZhRZoAe/+TxjWB/GlFAnYEbkYVlON9FMk/fE3h2RLpPXo4lQ==", + "dev": true, + "requires": { + "psl": "^1.1.24", + "punycode": "^1.4.1" + }, + "dependencies": { + "punycode": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", + "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=", + "dev": true + } + } + }, "tslib": { "version": "1.9.3", "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.9.3.tgz", @@ -2011,6 +2902,12 @@ "safe-buffer": "^5.0.1" } }, + "tweetnacl": { + "version": "0.14.5", + "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", + "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=", + "dev": true + }, "type-check": { "version": "0.3.2", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", @@ -2020,62 +2917,39 @@ "prelude-ls": "~1.1.2" } }, + "type-fest": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", + "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", + "dev": true + }, + "typedarray-to-buffer": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz", + "integrity": "sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==", + "dev": true, + "requires": { + "is-typedarray": "^1.0.0" + } + }, "typescript": { "version": "3.1.6", "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.1.6.tgz", "integrity": "sha512-tDMYfVtvpb96msS1lDX9MEdHrW4yOuZ4Kdc4Him9oU796XldPYF/t2+uKoX0BBa0hXXwDlqYQbXY5Rzjzc5hBA==", "dev": true }, - "uglify-js": { - "version": "3.4.9", - "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.4.9.tgz", - "integrity": "sha512-8CJsbKOtEbnJsTyv6LE6m6ZKniqMiFWmm9sRbopbkGs3gMPPfd3Fh8iIA4Ykv5MgaTbqHr4BaoGLJLZNhsrW1Q==", - "dev": true, - "optional": true, - "requires": { - "commander": "~2.17.1", - "source-map": "~0.6.1" - }, - "dependencies": { - "commander": { - "version": "2.17.1", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.17.1.tgz", - "integrity": "sha512-wPMUt6FnH2yzG95SA6mzjQOEKUU3aLaDEmzs1ti+1E9h+CsrZghRlqEM/EJ4KscsQVG8uNN4uVreUeT8+drlgg==", - "dev": true, - "optional": true - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true, - "optional": true - } - } + "uc.micro": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-1.0.6.tgz", + "integrity": "sha512-8Y75pvTYkLJW2hWQHXxoqRgV7qb9B+9vFEtidML+7koHUFapnVJAZ6cKs+Qjz5Aw3aZWHMC6u0wJE3At+nSGwA==", + "dev": true }, "underscore": { - "version": "1.8.3", - "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.8.3.tgz", - "integrity": "sha1-Tz+1OxBuYJf8+ctBCfKl6b36UCI=", + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.9.1.tgz", + "integrity": "sha512-5/4etnCkd9c8gwgowi5/om/mYO5ajCaOgdzj/oW+0eQV9WxKBDZw5+ycmKmeaTXjInS/W0BzpGLo2xR2aBwZdg==", "dev": true }, - "underscore-contrib": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/underscore-contrib/-/underscore-contrib-0.3.0.tgz", - "integrity": "sha1-ZltmwkeD+PorGMn4y7Dix9SMJsc=", - "dev": true, - "requires": { - "underscore": "1.6.0" - }, - "dependencies": { - "underscore": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.6.0.tgz", - "integrity": "sha1-izixDKze9jM3uLJOT/htRa6lKag=", - "dev": true - } - } - }, "unpipe": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", @@ -2092,28 +2966,43 @@ } }, "utf-8-validate": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/utf-8-validate/-/utf-8-validate-4.0.2.tgz", - "integrity": "sha512-CS63Ssp6zynBQ4IxVzgjP5Abdo6LuJ87HFIcgIiVUeY96+MTHkqKtrUhphbwQ6jX8aSGZv8zX6l1DCPpfcAnxA==", + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/utf-8-validate/-/utf-8-validate-5.0.2.tgz", + "integrity": "sha512-SwV++i2gTD5qh2XqaPzBnNX88N6HdyhQrNNRykvcS0QKvItV9u3vPEJr+X5Hhfb1JC0r0e1alL0iB09rY8+nmw==", "dev": true, "requires": { - "bindings": "~1.3.0", - "nan": "~2.10.0", - "prebuild-install": "~4.0.0" + "node-gyp-build": "~3.7.0" } }, - "util-deprecate": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", - "dev": true - }, "utils-merge": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=", "dev": true }, + "uuid": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.2.tgz", + "integrity": "sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA==", + "dev": true + }, + "valid-url": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/valid-url/-/valid-url-1.0.9.tgz", + "integrity": "sha1-HBRHm0DxOXp1eC8RXkCGRHQzogA=", + "dev": true + }, + "verror": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", + "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=", + "dev": true, + "requires": { + "assert-plus": "^1.0.0", + "core-util-is": "1.0.2", + "extsprintf": "^1.2.0" + } + }, "which": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", @@ -2123,27 +3012,88 @@ "isexe": "^2.0.0" } }, - "which-pm-runs": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/which-pm-runs/-/which-pm-runs-1.0.0.tgz", - "integrity": "sha1-Zws6+8VS4LVd9rd4DKdGFfI60cs=", + "which-module": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", + "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=", "dev": true }, - "wide-align": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz", - "integrity": "sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==", - "dev": true, - "requires": { - "string-width": "^1.0.2 || 2" - } - }, "wordwrap": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", "integrity": "sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus=", "dev": true }, + "wrap-ansi": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", + "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", + "dev": true, + "requires": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "dependencies": { + "ansi-regex": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", + "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", + "dev": true + }, + "ansi-styles": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz", + "integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==", + "dev": true, + "requires": { + "@types/color-name": "^1.1.1", + "color-convert": "^2.0.1" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true + }, + "string-width": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.0.tgz", + "integrity": "sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg==", + "dev": true, + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.0" + } + }, + "strip-ansi": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", + "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", + "dev": true, + "requires": { + "ansi-regex": "^5.0.0" + } + } + } + }, "wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", @@ -2159,6 +3109,18 @@ "mkdirp": "^0.5.1" } }, + "write-file-atomic": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-3.0.1.tgz", + "integrity": "sha512-JPStrIyyVJ6oCSz/691fAjFtefZ6q+fP6tm+OS4Qw6o+TGQxNp1ziY2PgS+X/m0V8OWhZiO/m4xSj+Pr4RrZvw==", + "dev": true, + "requires": { + "imurmurhash": "^0.1.4", + "is-typedarray": "^1.0.0", + "signal-exit": "^3.0.2", + "typedarray-to-buffer": "^3.1.5" + } + }, "ws": { "version": "5.2.2", "resolved": "https://registry.npmjs.org/ws/-/ws-5.2.2.tgz", @@ -2168,16 +3130,79 @@ } }, "xmlcreate": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/xmlcreate/-/xmlcreate-1.0.2.tgz", - "integrity": "sha1-+mv3YqYKQT+z3Y9LA8WyaSONMI8=", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/xmlcreate/-/xmlcreate-2.0.1.tgz", + "integrity": "sha512-MjGsXhKG8YjTKrDCXseFo3ClbMGvUD4en29H2Cev1dv4P/chlpw6KdYmlCWDkhosBVKRDjM836+3e3pm1cBNJA==", "dev": true }, - "xtend": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz", - "integrity": "sha1-pcbVMr5lbiPbgg77lDofBJmNY68=", - "dev": true + "y18n": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.0.tgz", + "integrity": "sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w==", + "dev": true + }, + "yargs": { + "version": "15.1.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.1.0.tgz", + "integrity": "sha512-T39FNN1b6hCW4SOIk1XyTOWxtXdcen0t+XYrysQmChzSipvhBO8Bj0nK1ozAasdk24dNWuMZvr4k24nz+8HHLg==", + "dev": true, + "requires": { + "cliui": "^6.0.0", + "decamelize": "^1.2.0", + "find-up": "^4.1.0", + "get-caller-file": "^2.0.1", + "require-directory": "^2.1.1", + "require-main-filename": "^2.0.0", + "set-blocking": "^2.0.0", + "string-width": "^4.2.0", + "which-module": "^2.0.0", + "y18n": "^4.0.0", + "yargs-parser": "^16.1.0" + }, + "dependencies": { + "ansi-regex": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", + "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true + }, + "string-width": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.0.tgz", + "integrity": "sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg==", + "dev": true, + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.0" + } + }, + "strip-ansi": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", + "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", + "dev": true, + "requires": { + "ansi-regex": "^5.0.0" + } + } + } + }, + "yargs-parser": { + "version": "16.1.0", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-16.1.0.tgz", + "integrity": "sha512-H/V41UNZQPkUMIT5h5hiwg4QKIY1RPvoBV4XcjUbRM8Bk2oKqqyZ0DIEbTFZB0XjbtSPG8SAa/0DxCQmiRgzKg==", + "dev": true, + "requires": { + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" + } } } } diff --git a/package.json b/package.json index 722ae7dd17d..4928653e3be 100644 --- a/package.json +++ b/package.json @@ -4,9 +4,9 @@ "homepage": "http://thrift.apache.org/", "repository": { "type": "git", - "url": "https://git-wip-us.apache.org/repos/asf/thrift.git" + "url": "https://github.com/apache/thrift.git" }, - "version": "0.12.0", + "version": "0.14.0", "author": { "name": "Apache Thrift Developers", "email": "dev@thrift.apache.org", @@ -30,30 +30,36 @@ "directories": { "lib": "./lib/nodejs/lib/thrift" }, + "browser": "./lib/nodejs/lib/thrift/browser.js", "main": "./lib/nodejs/lib/thrift", "engines": { - "node": ">= 4.1.0" + "node": ">= 10.18.0" }, "dependencies": { + "browser-or-node": "^1.2.1", + "isomorphic-ws": "^4.0.1", "node-int64": "^0.4.0", "q": "^1.5.0", - "ws": "^5.0.0" + "ws": "^5.2.2" }, "devDependencies": { + "@types/node": "^10.12.6", + "@types/node-int64": "^0.4.29", + "@types/q": "^1.5.1", "buffer-equals": "^1.0.4", "commander": "^2.14.1", "connect": "^3.6.6", "eslint": "^5.7.0", "eslint-config-prettier": "^3.1.0", "eslint-plugin-prettier": "^3.0.0", - "istanbul": "^0.4.5", - "jsdoc": "^3.5.5", + "html-validator-cli": "^4.1.4", + "nyc": "^15.0.0", + "jsdoc": "^3.6.3", + "json-int64": "^1.0.2", "prettier": "^1.14.3", "tape": "^4.9.0", - "utf-8-validate": "^4.0.0", "typescript": "^3.1.6", - "@types/node": "^10.12.6", - "@types/q": "^1.5.1" + "utf-8-validate": "^5.0.0" }, "scripts": { "cover": "lib/nodejs/test/testAll.sh COVER", diff --git a/pull_request_template.md b/pull_request_template.md deleted file mode 100644 index 0b6965b046a..00000000000 --- a/pull_request_template.md +++ /dev/null @@ -1,19 +0,0 @@ -Some helpful tips for a successful Apache Thrift PR: - -* Did you test your changes locally or using CI in your fork? -* Is the Apache Jira THRIFT ticket identifier in the PR title? -* Is the Apache Jira THRIFT ticket identifier in the commit message? -* Did you squash your changes to a single commit? -* Are these changes backwards compatible? (please say so in PR description) -* Do you need to update the language-specific README? - -Example ideal pull request title: - - THRIFT-9999: an example pull request title - -Example ideal commit message: - - THRIFT-9999: [summary of fix, one line if possible] - Client: [language(s) affected, comma separated, use lib/ directory names please] - -For more information about committing, see CONTRIBUTING.md diff --git a/sonar-project.properties b/sonar-project.properties index ef11517cf5f..fcda4776272 100755 --- a/sonar-project.properties +++ b/sonar-project.properties @@ -16,7 +16,7 @@ development, combines a software stack with a code generation engine to build services that work efficiently and seamlessly between all major languages. # Apache Thrift Version -sonar.projectVersion=0.12.0 +sonar.projectVersion=0.14.0 # use this to set another version string # $ sonar-runner -D sonar.projectVersion=`git rev-parse HEAD` # set projectDate in combination with projectVersion for imports of old releases @@ -31,7 +31,7 @@ sonar.language=java,js,c++,py,c sonar.sourceEncoding=UTF-8 # scm -sonar.scm.url=scm:git:https://git-wip-us.apache.org/repos/asf/thrift +sonar.scm.url=scm:git:https://github.com/apache/thrift.git # cppcheck -q --error-exitcode=0 --xml . 2> cppcheck-result.xml sonar.cxx.cppcheck.reportPath=cppcheck-result.xml @@ -54,7 +54,7 @@ module1.sonar.projectName=Apache Thrift - Java Library module1.sonar.projectBaseDir=lib/java module1.sonar.sources=src module1.sonar.tests=test -module1.sonar.binaries=build/libs/libthrift-0.12.0.jar +module1.sonar.binaries=build/libs/libthrift-0.14.0.jar module1.sonar.libraries=build/deps/*.jar module1.sonar.language=java @@ -62,7 +62,7 @@ module2.sonar.projectName=Apache Thrift - Java Tutorial module2.sonar.projectBaseDir=. module2.sonar.sources=tutorial/java/src, tutorial/java/gen-java module2.sonar.binaries=tutorial/java/tutorial.jar -module2.sonar.libraries=lib/java/build/deps/*.jar,lib/java/build/libs/libthrift-0.12.0.jar +module2.sonar.libraries=lib/java/build/deps/*.jar,lib/java/build/libs/libthrift-0.14.0.jar module2.sonar.language=java module3.sonar.projectName=Apache Thrift - JavaScript Library diff --git a/test/AnnotationTest.thrift b/test/AnnotationTest.thrift index 7e24e1ccbe7..9258322554e 100644 --- a/test/AnnotationTest.thrift +++ b/test/AnnotationTest.thrift @@ -70,3 +70,11 @@ service foo_service { void foo() ( foo = "bar" ) } (a.b="c") +service deprecate_everything { + void Foo( ) ( deprecated = "This method has neither 'x' nor \"y\"" ) + void Bar( ) ( deprecated = "Fails to deliver 中文 колбаса" ) + void Baz( ) ( deprecated = "Need this to work with tabs (\t) or Umlauts (äöüÄÖÜß) too" ) + void Deprecated() ( deprecated ) // no comment +} + + diff --git a/test/DebugProtoTest.thrift b/test/DebugProtoTest.thrift index b6a76595172..1ab0f6aea0a 100644 --- a/test/DebugProtoTest.thrift +++ b/test/DebugProtoTest.thrift @@ -164,6 +164,11 @@ struct CompactProtoTestStruct { 47: map> byte_map_map; 48: map> byte_set_map; 49: map> byte_list_map; + + // large field IDs + 500 : i64 field500; + 5000 : i64 field5000; + 20000 : i64 field20000; } // To be used to test the serialization of an empty map @@ -221,6 +226,10 @@ const CompactProtoTestStruct COMPACT_TEST = { 'byte_map_map' : {0 : {}, 1 : {1 : 1}, 2 : {1 : 1, 2 : 2}}, 'byte_set_map' : {0 : [], 1 : [1], 2 : [1, 2]}, 'byte_list_map' : {0 : [], 1 : [1], 2 : [1, 2]}, + + 'field500' : 500, + 'field5000' : 5000, + 'field20000' : 20000, } @@ -232,6 +241,10 @@ exception ExceptionWithAMap { 2: map map_field; } +exception MutableException { + 1: string msg; +} (python.immutable = "false") + service ServiceForExceptionWithAMap { void methodThatThrowsAnException() throws (1: ExceptionWithAMap xwamap); } diff --git a/test/Identifiers.thrift b/test/Identifiers.thrift new file mode 100644 index 00000000000..aa3e4eac570 --- /dev/null +++ b/test/Identifiers.thrift @@ -0,0 +1,6 @@ +// THRIFT-4953 +struct NoFieldIdentifiersTest { + string field_without_id1, + string field_without_id2, + 1: string field_with_id +} \ No newline at end of file diff --git a/lib/csharp/src/Protocol/TField.cs b/test/Int64Test.thrift similarity index 51% rename from lib/csharp/src/Protocol/TField.cs rename to test/Int64Test.thrift index 81795577ec8..348cc9c4753 100644 --- a/lib/csharp/src/Protocol/TField.cs +++ b/test/Int64Test.thrift @@ -1,4 +1,4 @@ -/** +/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information @@ -21,42 +21,24 @@ * details. */ -using System; -using System.Collections.Generic; -using System.Text; +namespace js Int64Test -namespace Thrift.Protocol -{ - public struct TField - { - private string name; - private TType type; - private short id; +const i64 SMALL_INT64 = 42 +const i64 MAX_JS_SAFE_INT64 = 9007199254740991 +const i64 MIN_JS_SAFE_INT64 = -9007199254740991 +const i64 MAX_JS_SAFE_PLUS_ONE_INT64 = 9007199254740992 +const i64 MIN_JS_SAFE_MINUS_ONE_INT64 = -9007199254740992 +const i64 MAX_SIGNED_INT64 = 9223372036854775807 +const i64 MIN_SIGNED_INT64 = -9223372036854775808 - public TField(string name, TType type, short id) - :this() - { - this.name = name; - this.type = type; - this.id = id; - } +const list INT64_LIST = [SMALL_INT64, MAX_JS_SAFE_INT64, MIN_JS_SAFE_INT64, MAX_JS_SAFE_PLUS_ONE_INT64, MIN_JS_SAFE_MINUS_ONE_INT64, MAX_SIGNED_INT64, MIN_SIGNED_INT64] - public string Name - { - get { return name; } - set { name = value; } - } - - public TType Type - { - get { return type; } - set { type = value; } - } - - public short ID - { - get { return id; } - set { id = value; } - } +const map INT64_2_INT64_MAP = { + SMALL_INT64: SMALL_INT64, + MAX_JS_SAFE_INT64: MAX_JS_SAFE_INT64, + MIN_JS_SAFE_INT64: MIN_JS_SAFE_INT64, + MAX_JS_SAFE_PLUS_ONE_INT64: MAX_JS_SAFE_PLUS_ONE_INT64, + MIN_JS_SAFE_MINUS_ONE_INT64: MIN_JS_SAFE_MINUS_ONE_INT64, + MAX_SIGNED_INT64: MAX_SIGNED_INT64, + MIN_SIGNED_INT64: MIN_SIGNED_INT64 } -} diff --git a/lib/cocoa/src/transport/TSSLSocketTransportError.m b/test/JavaBinaryDefault.thrift similarity index 87% rename from lib/cocoa/src/transport/TSSLSocketTransportError.m rename to test/JavaBinaryDefault.thrift index bcf941cc47d..5517802c73a 100644 --- a/lib/cocoa/src/transport/TSSLSocketTransportError.m +++ b/test/JavaBinaryDefault.thrift @@ -17,7 +17,9 @@ * under the License. */ -#import "TSSLSocketTransportError.h" +namespace java thrift.test - -NSString *TSSLSocketTransportErrorDomain = @"TSSLSocketTransportErrorDomain"; +struct StringAndBinary { + 1: optional string strval = "" + 2: optional binary binval = "" +} diff --git a/test/JavaTypes.thrift b/test/JavaTypes.thrift index 8c733ada8b5..5553340826f 100644 --- a/test/JavaTypes.thrift +++ b/test/JavaTypes.thrift @@ -27,6 +27,10 @@ struct String { 1: string val } +struct Binary { + 1: binary val +} + struct Boolean { 1: bool val } diff --git a/test/Makefile.am b/test/Makefile.am index 68f198615fe..4ef12e06a71 100755 --- a/test/Makefile.am +++ b/test/Makefile.am @@ -30,11 +30,6 @@ SUBDIRS += cl PRECROSS_TARGET += precross-cl endif -if WITH_MONO -SUBDIRS += csharp -PRECROSS_TARGET += precross-csharp -endif - if WITH_CPP SUBDIRS += cpp PRECROSS_TARGET += precross-cpp @@ -77,8 +72,8 @@ if WITH_HAXE SUBDIRS += haxe endif -if WITH_DOTNETCORE -SUBDIRS += netcore +if WITH_DOTNET +SUBDIRS += netstd endif if WITH_GO @@ -102,31 +97,45 @@ PRECROSS_TARGET += precross-rs endif # -# generate html for ThriftTest.thrift +# generate html for ThriftTest.thrift AND validate it! # +if WITH_NODEJS +check-local: + $(top_builddir)/compiler/cpp/thrift --gen html -r $(top_srcdir)/test/ThriftTest.thrift + $(top_builddir)/node_modules/.bin/html-validator --file=gen-html/index.html --verbose + $(top_builddir)/node_modules/.bin/html-validator --file=gen-html/ThriftTest.html --verbose +else check-local: $(top_builddir)/compiler/cpp/thrift --gen html -r $(top_srcdir)/test/ThriftTest.thrift +endif clean-local: - rm -rf $(top_srcdir)/test/gen-html + $(RM) -r $(top_srcdir)/test/gen-html/ + find . -type d -name "__pycache__" | xargs rm -rf + find . -type f -name "*.pyc" | xargs rm -f + +dist-hook: + $(RM) -r $(distdir)/gen-html/ + find $(distdir) -type d -name "__pycache__" | xargs rm -rf + find $(distdir) -type f -name "*.pyc" | xargs rm -f EXTRA_DIST = \ audit \ - crossrunner \ - keys \ c_glib \ cl \ cpp \ + crossrunner \ dart \ erl \ hs \ + keys \ lua \ ocaml \ perl \ php \ py \ - py.twisted \ py.tornado \ + py.twisted \ rb \ rs \ threads \ @@ -134,13 +143,17 @@ EXTRA_DIST = \ BrokenConstants.thrift \ ConstantsDemo.thrift \ DebugProtoTest.thrift \ - DoubleConstantsTest.thrift \ DenseLinkingTest.thrift \ DocTest.thrift \ + DoubleConstantsTest.thrift \ + EnumContainersTest.thrift \ EnumTest.thrift \ FullCamelTest.thrift \ Include.thrift \ + Identifiers.thrift \ + Int64Test.thrift \ JavaBeansTest.thrift \ + JavaBinaryDefault.thrift \ JavaDeepCopyTest.thrift \ JavaTypes.thrift \ JsDeepConstructorTest.thrift \ @@ -150,12 +163,14 @@ EXTRA_DIST = \ OptionalRequiredTest.thrift \ Recursive.thrift \ ReuseObjects.thrift \ - EnumContainersTest.thrift \ SmallTest.thrift \ StressTest.thrift \ ThriftTest.thrift \ TypedefTest.thrift \ + Types.thrift \ UnsafeTypes.thrift \ + Service.thrift \ + SpecificNameTest.thrift \ known_failures_Linux.json \ test.py \ tests.json \ diff --git a/test/README.md b/test/README.md index 0682f5d9835..5f6be15fd16 100755 --- a/test/README.md +++ b/test/README.md @@ -26,7 +26,7 @@ For example, if you changed something in `nodejs` library and need to verify the patch, you can skip everything except `nodejs` itself and some reference implementation (currently `cpp` and `java` are recommended) like this: - ./configure --without-c_glib -without-csharp --without-erlang --without-lua ... + ./configure --without-c_glib --without-erlang --without-lua ... make precross -j8 test/test.py --server cpp,java --client nodejs test/test.py --server nodejs --client cpp,java @@ -121,37 +121,45 @@ line interface: **Server command line interface:** - $ ./cpp/TestServer -h + $ ./TestServer -h Allowed options: - -h [ --help ] produce help message - --port arg (=9090) Port number to listen - --domain-socket arg Unix Domain Socket (e.g. /tmp/ThriftTest.thrift) - --named-pipe arg Windows Named Pipe (e.g. MyThriftPipe) - --server-type arg (=simple) type of server, "simple", "thread-pool", - "threaded", or "nonblocking" - --transport arg (=buffered) transport: buffered, framed, http, anonpipe - --protocol arg (=binary) protocol: binary, compact, json - --ssl Encrypted Transport using SSL - --processor-events processor-events - -n [ --workers ] arg (=4) Number of thread pools workers. Only valid for - thread-pool server type + -h | --help produce help message + --port=arg (9090) Port number to listen + --domain-socket=arg Unix Domain Socket (e.g. /tmp/ThriftTest.thrift) + --pipe=arg Windows Named Pipe (e.g. MyThriftPipe) + --server-type=arg (simple) type of server, "simple", "thread-pool", + "threaded", or "nonblocking" + --transport=arg (buffered) transport: buffered, framed, http, anonpipe, zlib + --protocol=arg (binary) protocol: binary, compact, header, json + --multiplex Add TMultiplexedProtocol service name "ThriftTest" + --abstract-namespace Create the domain socket in the Abstract Namespace + (no connection with filesystem pathnames) + --ssl Encrypted Transport using SSL + --zlib Wrapped Transport using Zlib + --processor-events processor-events + -n=arg | --workers=arg (=4) Number of thread pools workers. Only valid for + thread-pool server type **Client command line interface:** - $ ./cpp/TestClient -h + $ ./TestClient -h Allowed options: - -h [ --help ] produce help message - --host arg (=localhost) Host to connect - --port arg (=9090) Port number to connect - --domain-socket arg Domain Socket (e.g. /tmp/ThriftTest.thrift), - instead of host and port - --named-pipe arg Windows Named Pipe (e.g. MyThriftPipe) - --anon-pipes hRead hWrite Windows Anonymous Pipes pair (handles) - --transport arg (=buffered) Transport: buffered, framed, http, evhttp - --protocol arg (=binary) Protocol: binary, compact, json - --ssl Encrypted Transport using SSL - -n [ --testloops ] arg (=1) Number of Tests - -t [ --threads ] arg (=1) Number of Test threads + -h | --help produce help message + --host=arg (localhost) Host to connect + --port=arg (9090) Port number to connect + --domain-socket=arg Domain Socket (e.g. /tmp/ThriftTest.thrift), + instead of host and port + --pipe=arg Windows Named Pipe (e.g. MyThriftPipe) + --anon-pipes hRead hWrite Windows Anonymous Pipes pair (handles) + --abstract-namespace Create the domain socket in the Abstract Namespace + (no connection with filesystem pathnames) + --transport=arg (buffered) Transport: buffered, framed, http, evhttp, zlib + --protocol=arg (binary) Protocol: binary, compact, header, json + --multiplex Add TMultiplexedProtocol service name "ThriftTest" + --ssl Encrypted Transport using SSL + --zlib Wrap Transport with Zlib + -n=arg | --testloops=arg (1) Number of Tests + -t=arg | --threads=arg (1) Number of Test threads If you have executed the **make check** or **make cross** then you will be able to browse [gen-html/ThriftTest.html](gen-html/ThriftTest.html) with the test documentation. @@ -166,7 +174,7 @@ failing tests: #define TEST_STRUCTS 2 // 0000 0010 #define TEST_CONTAINERS 4 // 0000 0100 #define TEST_EXCEPTIONS 8 // 0000 1000 - #define TEST_UNKNOWN 64 // 0100 0000 (Failed to prepare environemt etc.) + #define TEST_UNKNOWN 64 // 0100 0000 (Failed to prepare environment etc.) #define TEST_TIMEOUT 128 // 1000 0000 #define TEST_NOTUSED 48 // 0011 0000 (reserved bits) diff --git a/lib/cocoa/src/TError.h b/test/Service.thrift similarity index 90% rename from lib/cocoa/src/TError.h rename to test/Service.thrift index abb72c7ad1f..6f4c9c7a3d7 100644 --- a/lib/cocoa/src/TError.h +++ b/test/Service.thrift @@ -17,7 +17,8 @@ * under the License. */ -#import +include "Types.thrift" - -extern NSString *TErrorDomain; +service Service { + Types.Type1 testEpisode(1:Types.Type1 arg) +} diff --git a/lib/cocoa/src/TStruct.swift b/test/SpecificNameTest.thrift similarity index 80% rename from lib/cocoa/src/TStruct.swift rename to test/SpecificNameTest.thrift index cea72e752b7..165fc31aeea 100644 --- a/lib/cocoa/src/TStruct.swift +++ b/test/SpecificNameTest.thrift @@ -17,15 +17,19 @@ * under the License. */ -import Foundation +namespace cpp test.specificname - -public protocol TStruct : TSerializable { +struct a { + 1: i32 number, + 2: string message, } +struct b { + 1: i32 number, + 2: string message, +} -public extension TStruct { - - public static var thriftType : TType { return TType.STRUCT } - +service EchoService { + void echoA(1: a arg), + void echoB(2: b arg), } diff --git a/test/ThriftTest.thrift b/test/ThriftTest.thrift index 3499ab5f862..ac49aee01c8 100644 --- a/test/ThriftTest.thrift +++ b/test/ThriftTest.thrift @@ -22,22 +22,20 @@ */ namespace c_glib TTest -namespace java thrift.test namespace cpp thrift.test -namespace rb Thrift.Test -namespace perl ThriftTest -namespace csharp Thrift.Test +namespace delphi Thrift.Test +namespace go thrifttest +namespace java thrift.test namespace js ThriftTest -namespace st ThriftTest +namespace lua ThriftTest +namespace netstd ThriftTest +namespace perl ThriftTest +namespace php ThriftTest namespace py ThriftTest namespace py.twisted ThriftTest -namespace go thrifttest -namespace php ThriftTest -namespace delphi Thrift.Test -namespace cocoa ThriftTest -namespace lua ThriftTest +namespace rb Thrift.Test +namespace st ThriftTest namespace xsd test (uri = 'http://thrift.apache.org/ns/ThriftTest') -namespace netcore ThriftTest // Presence of namespaces and sub-namespaces for which there is // no generator should compile with warnings only @@ -212,7 +210,7 @@ service ThriftTest Xtruct2 testNest(1: Xtruct2 thing), /** - * Prints 'testMap("{%s")' where thing has been formatted into a string of 'key => value' pairs + * Prints 'testMap("{%s")' where thing has been formatted into a string of 'key => value' pairs * separated by commas and new lines * @param map thing - the map to print * @return map - returns the map 'thing' @@ -220,7 +218,7 @@ service ThriftTest map testMap(1: map thing), /** - * Prints 'testStringMap("{%s}")' where thing has been formatted into a string of 'key => value' pairs + * Prints 'testStringMap("{%s}")' where thing has been formatted into a string of 'key => value' pairs * separated by commas and new lines * @param map thing - the map to print * @return map - returns the map 'thing' @@ -228,7 +226,7 @@ service ThriftTest map testStringMap(1: map thing), /** - * Prints 'testSet("{%s}")' where thing has been formatted into a string of values + * Prints 'testSet("{%s}")' where thing has been formatted into a string of values * separated by commas and new lines * @param set thing - the set to print * @return set - returns the set 'thing' @@ -236,7 +234,7 @@ service ThriftTest set testSet(1: set thing), /** - * Prints 'testList("{%s}")' where thing has been formatted into a string of values + * Prints 'testList("{%s}")' where thing has been formatted into a string of values * separated by commas and new lines * @param list thing - the list to print * @return list - returns the list 'thing' @@ -244,7 +242,7 @@ service ThriftTest list testList(1: list thing), /** - * Prints 'testEnum("%d")' where thing has been formatted into it's numeric value + * Prints 'testEnum("%d")' where thing has been formatted into its numeric value * @param Numberz thing - the Numberz to print * @return Numberz - returns the Numberz 'thing' */ @@ -266,9 +264,9 @@ service ThriftTest map> testMapMap(1: i32 hello), /** - * So you think you've got this all worked, out eh? + * So you think you've got this all worked out, eh? * - * Creates a the returned map with these values and prints it out: + * Creates a map with these values and prints it out: * { 1 => { 2 => argument, * 3 => argument, * }, @@ -295,16 +293,16 @@ service ThriftTest * Print 'testException(%s)' with arg as '%s' * @param string arg - a string indication what type of exception to throw * if arg == "Xception" throw Xception with errorCode = 1001 and message = arg - * elsen if arg == "TException" throw TException + * else if arg == "TException" throw TException * else do not throw anything */ void testException(1: string arg) throws(1: Xception err1), /** * Print 'testMultiException(%s, %s)' with arg0 as '%s' and arg1 as '%s' - * @param string arg - a string indication what type of exception to throw + * @param string arg - a string indicating what type of exception to throw * if arg0 == "Xception" throw Xception with errorCode = 1001 and message = "This is an Xception" - * elsen if arg0 == "Xception2" throw Xception2 with errorCode = 2002 and struct_thing.string_thing = "This is an Xception2" + * else if arg0 == "Xception2" throw Xception2 with errorCode = 2002 and struct_thing.string_thing = "This is an Xception2" * else do not throw anything * @return Xtruct - an Xtruct with string_thing = arg1 */ @@ -409,3 +407,7 @@ struct StructB { 1: optional StructA aa; 2: required StructA ab; } + +struct OptionalSetDefaultTest { + 1: optional set with_default = [ "test" ] +} diff --git a/lib/cocoa/src/TError.m b/test/Types.thrift similarity index 92% rename from lib/cocoa/src/TError.m rename to test/Types.thrift index df7f92dc590..11069d998a7 100644 --- a/lib/cocoa/src/TError.m +++ b/test/Types.thrift @@ -17,7 +17,7 @@ * under the License. */ -#import "TError.h" - - -NSString *TErrorDomain = @"TErrorDomain"; +struct Type1 { + 1: i32 number, + 2: string message, +} \ No newline at end of file diff --git a/test/c_glib/CMakeLists.txt b/test/c_glib/CMakeLists.txt new file mode 100644 index 00000000000..7a7daadc561 --- /dev/null +++ b/test/c_glib/CMakeLists.txt @@ -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. +# + +# Contains the thrift specific LINK_AGAINST_THRIFT_LIBRARY +include(ThriftMacros) + +find_package(GLIB REQUIRED COMPONENTS gobject) +include_directories(SYSTEM "${GLIB_INCLUDE_DIR}") +include_directories(SYSTEM "${GLIBCONFIG_INCLUDE_DIR}") + +#Make sure gen-c_glib files can be included +include_directories("${CMAKE_CURRENT_BINARY_DIR}") +include_directories("${CMAKE_CURRENT_BINARY_DIR}/gen-c_glib") +include_directories("${PROJECT_SOURCE_DIR}/lib/c_glib/src") +include_directories(SYSTEM "${OPENSSL_INCLUDE_DIR}") + +set(crosstestgencglib_SOURCES + gen-c_glib/t_test_second_service.c + gen-c_glib/t_test_second_service.h + gen-c_glib/t_test_thrift_test.c + gen-c_glib/t_test_thrift_test.h + gen-c_glib/t_test_thrift_test_types.c + gen-c_glib/t_test_thrift_test_types.h +) +add_library(crosstestgencglib STATIC ${crosstestgencglib_SOURCES}) +LINK_AGAINST_THRIFT_LIBRARY(crosstestgencglib thrift_c_glib) + +add_executable(test_server src/test_server.c src/thrift_test_handler.c src/thrift_second_service_handler.c) +target_link_libraries(test_server crosstestgencglib) + +add_executable(test_client src/test_client.c) +target_link_libraries(test_client crosstestgencglib "${OPENSSL_LIBRARIES}") + +# +# Common thrift code generation rules +# + +add_custom_command(OUTPUT gen-c_glib/t_test_second_service.c gen-c_glib/t_test_second_service.h gen-c_glib/t_test_thrift_test.c gen-c_glib/t_test_thrift_test.h gen-c_glib/t_test_thrift_test_types.c gen-c_glib/t_test_thrift_test_types.h + COMMAND ${THRIFT_COMPILER} --gen c_glib -r ${PROJECT_SOURCE_DIR}/test/ThriftTest.thrift +) diff --git a/test/c_glib/Makefile.am b/test/c_glib/Makefile.am index 4a03d29d759..98a3734985d 100755 --- a/test/c_glib/Makefile.am +++ b/test/c_glib/Makefile.am @@ -16,7 +16,7 @@ # specific language governing permissions and limitations # under the License. # -AUTOMAKE_OPTIONS = subdir-objects serial-tests +AUTOMAKE_OPTIONS = subdir-objects serial-tests nostdinc noinst_LTLIBRARIES = libtestcglib.la nodist_libtestcglib_la_SOURCES = \ @@ -61,14 +61,22 @@ gen-c_glib/t_test_second_service.c gen-c_glib/t_test_second_service.h gen-c_gl AM_CFLAGS = -g -Wall -Wextra $(GLIB_CFLAGS) $(GOBJECT_CFLAGS) AM_CXXFLAGS = $(AM_CFLAGS) -AM_CPPFLAGS = -I$(top_srcdir)/lib/c_glib/src -Igen-c_glib +AM_CPPFLAGS = -I$(top_srcdir)/lib/c_glib/src -Igen-c_glib -I$(top_builddir)/lib/c_glib/src/thrift AM_LDFLAGS = $(GLIB_LIBS) $(GOBJECT_LIBS) @GCOV_LDFLAGS@ clean-local: - $(RM) gen-c_glib/* + $(RM) -r gen-c_glib/ + $(RM) test_client + $(RM) test_server + $(RM) libtestcglib.la + find . -type f -iname "*.o" | xargs rm -f + +dist-hook: + $(RM) -r $(distdir)/gen-c_glib/ + $(RM) $(distdir)/test_client + $(RM) $(distdir)/test_server + $(RM) $(distdir)/libtestcglib.la + find $(distdir) -type f -iname "*.o" | xargs rm -f EXTRA_DIST = \ - src/test_client.c \ - src/thrift_test_handler.c \ - src/thrift_test_handler.h \ - src/test_server.c + src diff --git a/test/c_glib/src/test_client.c b/test/c_glib/src/test_client.c index ef24ab76c6e..76e0ad746c1 100644 --- a/test/c_glib/src/test_client.c +++ b/test/c_glib/src/test_client.c @@ -75,44 +75,12 @@ gint32_compare (gconstpointer a, gconstpointer b) return result; } -/** - * It gets a multiplexed protocol which uses a concrete protocol underneath - * @param protocol_name the fully qualified protocol path (e.g. "binary:multi") - * @param transport the underlying transport - * @param service_name the single supported service name - * @todo need to allow multiple services to fully test multiplexed - * @return a multiplexed protocol wrapping the correct underlying protocol - */ -ThriftProtocol * -get_multiplexed_protocol(gchar *protocol_name, ThriftTransport *transport, gchar *service_name) -{ - ThriftProtocol * multiplexed_protocol = NULL; - - if ( strncmp(protocol_name, "binary:", 7) == 0) { - multiplexed_protocol = g_object_new (THRIFT_TYPE_BINARY_PROTOCOL, - "transport", transport, - NULL); - } else if ( strncmp(protocol_name, "compact:", 8) == 0) { - multiplexed_protocol = g_object_new (THRIFT_TYPE_COMPACT_PROTOCOL, - "transport", transport, - NULL); - } else { - fprintf(stderr, "Unknown multiplex protocol name: %s\n", protocol_name); - return NULL; - } - - return g_object_new (THRIFT_TYPE_MULTIPLEXED_PROTOCOL, - "transport", transport, - "protocol", multiplexed_protocol, - "service-name", service_name, - NULL); -} - int main (int argc, char **argv) { static gchar * host = NULL; static gint port = 9090; + static gchar * path = NULL; static gboolean ssl = FALSE; static gchar * transport_option = NULL; static gchar * protocol_option = NULL; @@ -124,6 +92,8 @@ main (int argc, char **argv) "Host to connect (=localhost)", NULL }, { "port", 'p', 0, G_OPTION_ARG_INT, &port, "Port number to connect (=9090)", NULL }, + { "domain-socket", 0, 0, G_OPTION_ARG_STRING, &path, + "Unix socket domain path to connect", NULL }, { "ssl", 's', 0, G_OPTION_ARG_NONE, &ssl, "Enable SSL", NULL }, { "transport", 't', 0, G_OPTION_ARG_STRING, &transport_option, @@ -148,6 +118,7 @@ main (int argc, char **argv) ThriftTransport *transport = NULL; ThriftProtocol *protocol = NULL; ThriftProtocol *protocol2 = NULL; // for multiplexed tests + ThriftProtocol *multiplexed_protocol = NULL; TTestThriftTestIf *test_client = NULL; TTestSecondServiceIf *second_service = NULL; // for multiplexed tests @@ -176,6 +147,8 @@ main (int argc, char **argv) &argv, &error)) { fprintf (stderr, "%s\n", error->message); + g_clear_error (&error); + g_option_context_free (option_context); return 255; } g_option_context_free (option_context); @@ -227,12 +200,20 @@ main (int argc, char **argv) if (!options_valid) return 254; - printf ("Connecting (%s/%s) to: %s/%s:%d\n", - transport_name, - protocol_name, - socket_name, - host, - port); + if (path) { + printf ("Connecting (%s/%s) to: %s/%s\n", + transport_name, + protocol_name, + socket_name, + path); + } else { + printf ("Connecting (%s/%s) to: %s/%s:%d\n", + transport_name, + protocol_name, + socket_name, + host, + port); + } /* Install our SIGPIPE handler, which outputs an error message to standard error before exiting so testers can know what @@ -247,10 +228,16 @@ main (int argc, char **argv) } /* Establish all our connection objects */ - socket = g_object_new (socket_type, - "hostname", host, - "port", port, - NULL); + if (path) { + socket = g_object_new (socket_type, + "path", path, + NULL); + } else { + socket = g_object_new (socket_type, + "hostname", host, + "port", port, + NULL); + } if (ssl && !thrift_ssl_load_cert_from_file(THRIFT_SSL_SOCKET(socket), "../keys/CA.pem")) { fprintf(stderr, "Unable to load validation certificate ../keys/CA.pem - did you run in the test/c_glib directory?\n"); @@ -262,24 +249,47 @@ main (int argc, char **argv) "transport", socket, NULL); - if(protocol_type==THRIFT_TYPE_MULTIPLEXED_PROTOCOL) { + if (protocol_type == THRIFT_TYPE_MULTIPLEXED_PROTOCOL) { // TODO: A multiplexed test should also test "Second" (see Java TestServer) // The context comes from the name of the thrift file. If multiple thrift // schemas are used we have to redo the way this is done. - protocol = get_multiplexed_protocol(protocol_name, transport, "ThriftTest"); - if (NULL == protocol) { + if (strncmp(protocol_name, "binary:", 7) == 0) { + multiplexed_protocol = g_object_new (THRIFT_TYPE_BINARY_PROTOCOL, + "transport", transport, + NULL); + } else if (strncmp(protocol_name, "compact:", 8) == 0) { + multiplexed_protocol = g_object_new (THRIFT_TYPE_COMPACT_PROTOCOL, + "transport", transport, + NULL); + } else { + fprintf(stderr, "Unknown multiplex protocol name: %s\n", protocol_name); g_clear_object (&transport); g_clear_object (&socket); return 252; } + protocol = g_object_new (THRIFT_TYPE_MULTIPLEXED_PROTOCOL, + "transport", transport, + "protocol", multiplexed_protocol, + "service-name", "ThriftTest", + NULL);; + if (NULL == protocol) { + g_clear_object (&multiplexed_protocol); + g_clear_object (&transport); + g_clear_object (&socket); + return 251; + } // Make a second protocol and client running on the same multiplexed transport - protocol2 = get_multiplexed_protocol(protocol_name, transport, "SecondService"); - second_service = g_object_new (T_TEST_TYPE_SECOND_SERVICE_CLIENT, - "input_protocol", protocol2, - "output_protocol", protocol2, - NULL); + protocol2 = g_object_new (THRIFT_TYPE_MULTIPLEXED_PROTOCOL, + "transport", transport, + "protocol", multiplexed_protocol, + "service-name", "SecondService", + NULL); + second_service = g_object_new (T_TEST_TYPE_SECOND_SERVICE_CLIENT, + "input_protocol", protocol2, + "output_protocol", protocol2, + NULL); }else{ protocol = g_object_new (protocol_type, "transport", transport, @@ -336,7 +346,11 @@ main (int argc, char **argv) gboolean first; gint32 i, j; - printf ("Test #%d, connect %s:%d\n", test_num + 1, host, port); + if (path) { + printf ("Test #%d, connect %s\n", test_num + 1, path); + } else { + printf ("Test #%d, connect %s:%d\n", test_num + 1, host, port); + } gettimeofday (&time_start, NULL); /* These test routines have been ported from the C++ test @@ -1353,6 +1367,8 @@ main (int argc, char **argv) byte_thing, i32_thing, i64_thing); + if (string != NULL) + g_free (string); } printf ("}"); g_ptr_array_unref (xtructs); @@ -1775,7 +1791,7 @@ main (int argc, char **argv) g_error_free (error); error = NULL; - return 1; + goto out; } } @@ -1789,10 +1805,12 @@ main (int argc, char **argv) printf ("Max time: %" PRIu64 " us\n", time_max_usec); printf ("Avg time: %" PRIu64 " us\n", time_avg_usec); +out: g_clear_object(&second_service); g_clear_object(&protocol2); g_clear_object(&test_client); g_clear_object(&protocol); + g_clear_object(&multiplexed_protocol); g_clear_object(&transport); g_clear_object(&socket); diff --git a/test/c_glib/src/test_server.c b/test/c_glib/src/test_server.c index 2d716ec2e6f..c949530de01 100644 --- a/test/c_glib/src/test_server.c +++ b/test/c_glib/src/test_server.c @@ -69,6 +69,7 @@ int main (int argc, char **argv) { static gint port = 9090; + static gchar *path_option = NULL; static gchar *server_type_option = NULL; static gchar *transport_option = NULL; static gchar *protocol_option = NULL; @@ -79,6 +80,8 @@ main (int argc, char **argv) GOptionEntry option_entries[] = { { "port", 0, 0, G_OPTION_ARG_INT, &port, "Port number to connect (=9090)", NULL }, + { "domain-socket", 0, 0, G_OPTION_ARG_STRING, &path_option, + "Unix socket domain path to connect", NULL }, { "server-type", 0, 0, G_OPTION_ARG_STRING, &server_type_option, "Type of server: simple (=simple)", NULL }, { "transport", 0, 0, G_OPTION_ARG_STRING, &transport_option, @@ -128,6 +131,8 @@ main (int argc, char **argv) &argv, &error) == FALSE) { fprintf (stderr, "%s\n", error->message); + g_clear_error (&error); + g_option_context_free (option_context); return 255; } g_option_context_free (option_context); @@ -218,9 +223,15 @@ main (int argc, char **argv) "handler", handler, NULL); } - server_transport = g_object_new (THRIFT_TYPE_SERVER_SOCKET, - "port", port, - NULL); + if (path_option) { + server_transport = g_object_new (THRIFT_TYPE_SERVER_SOCKET, + "path", path_option, + NULL); + } else { + server_transport = g_object_new (THRIFT_TYPE_SERVER_SOCKET, + "port", port, + NULL); + } transport_factory = g_object_new (transport_factory_type, NULL); @@ -250,11 +261,19 @@ main (int argc, char **argv) sigint_action.sa_flags = SA_RESETHAND; sigaction (SIGINT, &sigint_action, NULL); - printf ("Starting \"%s\" server (%s/%s) listen on: %d\n", - server_name, - transport_name, - protocol_name, - port); + if (path_option) { + printf ("Starting \"%s\" server (%s/%s) listen on: %s\n", + server_name, + transport_name, + protocol_name, + path_option); + } else { + printf ("Starting \"%s\" server (%s/%s) listen on: %d\n", + server_name, + transport_name, + protocol_name, + port); + } fflush (stdout); /* Serve clients until SIGINT is received (Ctrl-C is pressed) */ @@ -265,11 +284,11 @@ main (int argc, char **argv) if (!sigint_received) { g_message ("thrift_server_serve: %s", error != NULL ? error->message : "(null)"); - g_clear_error (&error); } puts ("done."); + g_clear_error (&error); g_object_unref (server); g_object_unref (protocol_factory); g_object_unref (transport_factory); diff --git a/test/c_glib/src/thrift_test_handler.c b/test/c_glib/src/thrift_test_handler.c index 1d8bcb25a6d..ccfd6c279bf 100644 --- a/test/c_glib/src/thrift_test_handler.c +++ b/test/c_glib/src/thrift_test_handler.c @@ -30,7 +30,7 @@ G_DEFINE_TYPE (ThriftTestHandler, thrift_test_handler, - T_TEST_TYPE_THRIFT_TEST_HANDLER); + T_TEST_TYPE_THRIFT_TEST_HANDLER) gboolean thrift_test_handler_test_void (TTestThriftTestIf *iface, @@ -367,7 +367,7 @@ thrift_test_handler_test_list (TTestThriftTestIf *iface, printf ("testList({"); for (i = 0; i < thing->len; i += 1) { gint32 value; - gint32 *new_value; + gint32 new_value; if (first) first = FALSE; @@ -377,9 +377,8 @@ thrift_test_handler_test_list (TTestThriftTestIf *iface, value = g_array_index (thing, gint32, i); printf ("%d", value); - new_value = g_malloc (sizeof *new_value); - *new_value = value; - g_array_append_val (*_return, *new_value); + new_value = value; + g_array_append_val (*_return, new_value); } printf ("})\n"); @@ -591,6 +590,9 @@ thrift_test_handler_test_insanity (TTestThriftTestIf *iface, byte_thing, i32_thing, i64_thing); + if (string_thing != NULL) { + g_free (string_thing); + } } printf ("}"); g_ptr_array_unref (xtructs); @@ -624,10 +626,10 @@ thrift_test_handler_test_multi (TTestThriftTestIf *iface, printf ("testMulti()\n"); g_object_set (*_return, - "string_thing", g_strdup ("Hello2"), - "byte_thing", arg0, - "i32_thing", arg1, - "i64_thing", arg2, + "string_thing", "Hello2", + "byte_thing", arg0, + "i32_thing", arg1, + "i64_thing", arg2, NULL); return TRUE; @@ -654,7 +656,7 @@ thrift_test_handler_test_exception (TTestThriftTestIf *iface, argument, set *error to NULL and return FALSE */ *err1 = g_object_new (T_TEST_TYPE_XCEPTION, "errorCode", 1001, - "message", g_strdup (arg), + "message", arg, NULL); *error = NULL; result = FALSE; @@ -676,7 +678,7 @@ thrift_test_handler_test_exception (TTestThriftTestIf *iface, /* This code is duplicated from the C++ test suite, though it appears to serve no purpose */ xtruct = g_object_new (T_TEST_TYPE_XTRUCT, - "string_thing", g_strdup (arg), + "string_thing", arg, NULL); g_object_unref (xtruct); @@ -709,7 +711,7 @@ thrift_test_handler_test_multi_exception (TTestThriftTestIf *iface, if (strncmp (arg0, "Xception", 8) == 0 && strlen(arg0) == 8) { *err1 = g_object_new (T_TEST_TYPE_XCEPTION, "errorCode", 1001, - "message", g_strdup ("This is an Xception"), + "message", "This is an Xception", NULL); result = FALSE; } @@ -722,7 +724,7 @@ thrift_test_handler_test_multi_exception (TTestThriftTestIf *iface, "struct_thing", &struct_thing, NULL); g_object_set (struct_thing, - "string_thing", g_strdup ("This is an Xception2"), + "string_thing", "This is an Xception2", NULL); g_object_set (*err2, "struct_thing", struct_thing, @@ -733,7 +735,7 @@ thrift_test_handler_test_multi_exception (TTestThriftTestIf *iface, } else { g_object_set (*_return, - "string_thing", g_strdup (arg1), + "string_thing", arg1, NULL); result = TRUE; } diff --git a/test/cpp/CMakeLists.txt b/test/cpp/CMakeLists.txt index 95d2991f8b8..9ecc1718305 100755 --- a/test/cpp/CMakeLists.txt +++ b/test/cpp/CMakeLists.txt @@ -17,11 +17,15 @@ # under the License. # +# The test executables still depend on Boost +include(BoostMacros) +REQUIRE_BOOST_HEADERS() +set(BOOST_COMPONENTS filesystem program_options random) +REQUIRE_BOOST_LIBRARIES(BOOST_COMPONENTS) + # Contains the thrift specific LINK_AGAINST_THRIFT_LIBRARY include(ThriftMacros) -include_directories(SYSTEM "${Boost_INCLUDE_DIRS}") - find_package(OpenSSL REQUIRED) include_directories(SYSTEM "${OPENSSL_INCLUDE_DIR}") @@ -48,12 +52,17 @@ LINK_AGAINST_THRIFT_LIBRARY(crosstestgencpp thrift) set(crossstressgencpp_SOURCES gen-cpp/Service.cpp - gen-cpp/StressTest_types.cpp - gen-cpp/StressTest_constants.cpp ) add_library(crossstressgencpp STATIC ${crossstressgencpp_SOURCES}) LINK_AGAINST_THRIFT_LIBRARY(crossstressgencpp thrift) +set(crossspecificnamegencpp_SOURCES + gen-cpp/EchoService.cpp + gen-cpp/SpecificNameTest_types.cpp +) +add_library(crossspecificnamegencpp STATIC ${crossspecificnamegencpp_SOURCES}) +LINK_AGAINST_THRIFT_LIBRARY(crossspecificnamegencpp thrift) + add_executable(TestServer src/TestServer.cpp) target_link_libraries(TestServer crosstestgencpp ${Boost_LIBRARIES} ${LIBEVENT_LIB}) LINK_AGAINST_THRIFT_LIBRARY(TestServer thrift) @@ -71,13 +80,24 @@ target_link_libraries(StressTest crossstressgencpp ${Boost_LIBRARIES} ${LIBEVENT LINK_AGAINST_THRIFT_LIBRARY(StressTest thrift) LINK_AGAINST_THRIFT_LIBRARY(StressTest thriftnb) add_test(NAME StressTest COMMAND StressTest) +add_test(NAME StressTestConcurrent COMMAND StressTest --client-type=concurrent) -add_executable(StressTestNonBlocking src/StressTestNonBlocking.cpp) -target_link_libraries(StressTestNonBlocking crossstressgencpp ${Boost_LIBRARIES} ${LIBEVENT_LIB}) -LINK_AGAINST_THRIFT_LIBRARY(StressTestNonBlocking thrift) -LINK_AGAINST_THRIFT_LIBRARY(StressTestNonBlocking thriftnb) -LINK_AGAINST_THRIFT_LIBRARY(StressTestNonBlocking thriftz) -add_test(NAME StressTestNonBlocking COMMAND StressTestNonBlocking) +# As of https://jira.apache.org/jira/browse/THRIFT-4282, StressTestNonBlocking +# is broken on Windows. Contributions welcome. +if (NOT WIN32 AND NOT CYGWIN) + add_executable(StressTestNonBlocking src/StressTestNonBlocking.cpp) + target_link_libraries(StressTestNonBlocking crossstressgencpp ${Boost_LIBRARIES} ${LIBEVENT_LIB}) + LINK_AGAINST_THRIFT_LIBRARY(StressTestNonBlocking thrift) + LINK_AGAINST_THRIFT_LIBRARY(StressTestNonBlocking thriftnb) + LINK_AGAINST_THRIFT_LIBRARY(StressTestNonBlocking thriftz) + add_test(NAME StressTestNonBlocking COMMAND StressTestNonBlocking) +endif() + +add_executable(SpecificNameTest src/SpecificNameTest.cpp) +target_link_libraries(SpecificNameTest crossspecificnamegencpp ${Boost_LIBRARIES} ${LIBEVENT_LIB}) +LINK_AGAINST_THRIFT_LIBRARY(SpecificNameTest thrift) +LINK_AGAINST_THRIFT_LIBRARY(SpecificNameTest thriftnb) +add_test(NAME SpecificNameTest COMMAND SpecificNameTest) # # Common thrift code generation rules @@ -87,6 +107,10 @@ add_custom_command(OUTPUT gen-cpp/SecondService.cpp gen-cpp/SecondService.h gen- COMMAND ${THRIFT_COMPILER} --gen cpp:templates,cob_style -r ${PROJECT_SOURCE_DIR}/test/ThriftTest.thrift ) -add_custom_command(OUTPUT gen-cpp/StressTest_types.cpp gen-cpp/StressTest_constants.cpp gen-cpp/Service.cpp +add_custom_command(OUTPUT gen-cpp/Service.cpp COMMAND ${THRIFT_COMPILER} --gen cpp ${PROJECT_SOURCE_DIR}/test/StressTest.thrift ) + +add_custom_command(OUTPUT gen-cpp/EchoService.cpp gen-cpp/SpecificNameTest_types.cpp + COMMAND ${THRIFT_COMPILER} --gen cpp ${PROJECT_SOURCE_DIR}/test/SpecificNameTest.thrift +) diff --git a/test/cpp/Makefile.am b/test/cpp/Makefile.am index e8be80a3a17..d3754f478cf 100755 --- a/test/cpp/Makefile.am +++ b/test/cpp/Makefile.am @@ -16,14 +16,12 @@ # specific language governing permissions and limitations # under the License. # -AUTOMAKE_OPTIONS = subdir-objects serial-tests +AUTOMAKE_OPTIONS = subdir-objects serial-tests nostdinc BUILT_SOURCES = gen-cpp/ThriftTest.cpp \ gen-cpp/ThriftTest_types.cpp \ gen-cpp/ThriftTest_constants.cpp \ gen-cpp/SecondService.cpp \ - gen-cpp/StressTest_types.cpp \ - gen-cpp/StressTest_constants.cpp \ gen-cpp/Service.cpp noinst_LTLIBRARIES = libtestgencpp.la libstresstestgencpp.la @@ -44,9 +42,6 @@ nodist_libtestgencpp_la_SOURCES = \ libtestgencpp_la_LIBADD = $(top_builddir)/lib/cpp/libthrift.la nodist_libstresstestgencpp_la_SOURCES = \ - gen-cpp/StressTest_constants.cpp \ - gen-cpp/StressTest_types.cpp \ - gen-cpp/StressTest_constants.h \ gen-cpp/StressTest_types.h \ gen-cpp/Service.cpp \ gen-cpp/Service.h @@ -105,15 +100,18 @@ StressTestNonBlocking_LDADD = \ gen-cpp/ThriftTest.cpp gen-cpp/ThriftTest_types.cpp gen-cpp/ThriftTest_constants.cpp gen-cpp/SecondService.cpp gen-cpp/SecondService.h gen-cpp/SecondService.tcc: $(top_srcdir)/test/ThriftTest.thrift $(THRIFT) $(THRIFT) --gen cpp:templates,cob_style -r $< -gen-cpp/StressTest_types.cpp gen-cpp/StressTest_constants.cpp gen-cpp/Service.cpp: $(top_srcdir)/test/StressTest.thrift $(THRIFT) +gen-cpp/Service.cpp: $(top_srcdir)/test/StressTest.thrift $(THRIFT) $(THRIFT) --gen cpp $< -AM_CPPFLAGS = $(BOOST_CPPFLAGS) $(LIBEVENT_CPPFLAGS) -I$(top_srcdir)/lib/cpp/src -Igen-cpp +gen-cpp/SpecificNameTest_types.cpp gen-cpp/EchoService.cpp: $(top_srcdir)/test/SpecificName.thrift $(THRIFT) + $(THRIFT) --gen cpp $< + +AM_CPPFLAGS = $(BOOST_CPPFLAGS) $(LIBEVENT_CPPFLAGS) -I$(top_srcdir)/lib/cpp/src -Igen-cpp -I. AM_CXXFLAGS = -Wall -Wextra -pedantic -D__STDC_FORMAT_MACROS -D__STDC_LIMIT_MACROS AM_LDFLAGS = $(BOOST_LDFLAGS) $(LIBEVENT_LDFLAGS) $(ZLIB_LIBS) clean-local: - $(RM) gen-cpp/* + $(RM) -r gen-cpp/ style-local: $(CPPSTYLE_CMD) diff --git a/lib/cocoa/src/TEnum.swift b/test/cpp/src/SpecificNameTest.cpp similarity index 78% rename from lib/cocoa/src/TEnum.swift rename to test/cpp/src/SpecificNameTest.cpp index 562a53ade9e..9c31ac10f3d 100644 --- a/lib/cocoa/src/TEnum.swift +++ b/test/cpp/src/SpecificNameTest.cpp @@ -17,15 +17,13 @@ * under the License. */ -import Foundation - - -public protocol TEnum : TSerializable { +int main(int argc, char** argv) { -} + //Empty in main function, + //because we just want to test + //whether the generated source code + //(IDL file contains struct named as + //'a' or 'b') can be compiled. -public extension TEnum { - - public static var thriftType : TType { return TType.I32 } - + return 0; } diff --git a/test/cpp/src/StressTest.cpp b/test/cpp/src/StressTest.cpp index 5ff5e44f95d..79a708e8f57 100644 --- a/test/cpp/src/StressTest.cpp +++ b/test/cpp/src/StressTest.cpp @@ -18,9 +18,8 @@ */ #include -#include +#include #include -#include #include #include #include @@ -31,7 +30,6 @@ #include #include #include -#include #include "Service.h" #include @@ -46,6 +44,7 @@ using namespace std; using namespace apache::thrift; +using namespace apache::thrift::async; using namespace apache::thrift::protocol; using namespace apache::thrift::transport; using namespace apache::thrift::server; @@ -66,7 +65,7 @@ typedef map count_map; class Server : public ServiceIf { public: - Server() {} + Server() = default; void count(const char* method) { Guard m(lock_); @@ -74,7 +73,7 @@ class Server : public ServiceIf { counts_[method] = ++ct; } - void echoVoid() { + void echoVoid() override { count("echoVoid"); return; } @@ -84,18 +83,18 @@ class Server : public ServiceIf { return counts_; } - int8_t echoByte(const int8_t arg) { return arg; } - int32_t echoI32(const int32_t arg) { return arg; } - int64_t echoI64(const int64_t arg) { return arg; } - void echoString(string& out, const string& arg) { + int8_t echoByte(const int8_t arg) override { return arg; } + int32_t echoI32(const int32_t arg) override { return arg; } + int64_t echoI64(const int64_t arg) override { return arg; } + void echoString(string& out, const string& arg) override { if (arg != "hello") { T_ERROR_ABORT("WRONG STRING (%s)!!!!", arg.c_str()); } out = arg; } - void echoList(vector& out, const vector& arg) { out = arg; } - void echoSet(set& out, const set& arg) { out = arg; } - void echoMap(map& out, const map& arg) { out = arg; } + void echoList(vector& out, const vector& arg) override { out = arg; } + void echoSet(set& out, const set& arg) override { out = arg; } + void echoMap(map& out, const map& arg) override { out = arg; } private: count_map counts_; @@ -108,8 +107,8 @@ enum TransportOpenCloseBehavior { }; class ClientThread : public Runnable { public: - ClientThread(stdcxx::shared_ptr transport, - stdcxx::shared_ptr client, + ClientThread(std::shared_ptr transport, + std::shared_ptr client, Monitor& monitor, size_t& workerCount, size_t loopCount, @@ -123,7 +122,7 @@ class ClientThread : public Runnable { _loopType(loopType), _behavior(behavior) {} - void run() { + void run() override { // Wait for all worker threads to start @@ -134,7 +133,7 @@ class ClientThread : public Runnable { } } - _startTime = Util::currentTime(); + _startTime = std::chrono::duration_cast(std::chrono::steady_clock::now().time_since_epoch()).count(); if(_behavior == OpenAndCloseTransportInThread) { _transport->open(); } @@ -160,7 +159,7 @@ class ClientThread : public Runnable { break; } - _endTime = Util::currentTime(); + _endTime = std::chrono::duration_cast(std::chrono::steady_clock::now().time_since_epoch()).count(); if(_behavior == OpenAndCloseTransportInThread) { _transport->close(); @@ -225,8 +224,8 @@ class ClientThread : public Runnable { } } - stdcxx::shared_ptr _transport; - stdcxx::shared_ptr _client; + std::shared_ptr _transport; + std::shared_ptr _client; Monitor& _monitor; size_t& _workerCount; size_t _loopCount; @@ -241,7 +240,7 @@ class ClientThread : public Runnable { class TStartObserver : public apache::thrift::server::TServerEventHandler { public: TStartObserver() : awake_(false) {} - virtual void preServe() { + void preServe() override { apache::thrift::concurrency::Synchronized s(m_); awake_ = true; m_.notifyAll(); @@ -266,8 +265,8 @@ int main(int argc, char** argv) { string clientType = "regular"; string serverType = "thread-pool"; string protocolType = "binary"; - size_t workerCount = 4; - size_t clientCount = 20; + size_t workerCount = 8; + size_t clientCount = 4; size_t loopCount = 50000; TType loopType = T_VOID; string callName = "echoVoid"; @@ -391,24 +390,24 @@ int main(int argc, char** argv) { cerr << usage.str(); } - stdcxx::shared_ptr threadFactory - = stdcxx::shared_ptr(new PlatformThreadFactory()); + std::shared_ptr threadFactory + = std::shared_ptr(new ThreadFactory()); // Dispatcher - stdcxx::shared_ptr serviceHandler(new Server()); + std::shared_ptr serviceHandler(new Server()); if (replayRequests) { - stdcxx::shared_ptr serviceHandler(new Server()); - stdcxx::shared_ptr serviceProcessor(new ServiceProcessor(serviceHandler)); + std::shared_ptr serviceHandler(new Server()); + std::shared_ptr serviceProcessor(new ServiceProcessor(serviceHandler)); // Transports - stdcxx::shared_ptr fileTransport(new TFileTransport(requestLogPath)); + std::shared_ptr fileTransport(new TFileTransport(requestLogPath)); fileTransport->setChunkSize(2 * 1024 * 1024); fileTransport->setMaxEventSize(1024 * 16); fileTransport->seekToEnd(); // Protocol Factory - stdcxx::shared_ptr protocolFactory(new TBinaryProtocolFactory()); + std::shared_ptr protocolFactory(new TBinaryProtocolFactory()); TFileProcessor fileProcessor(serviceProcessor, protocolFactory, fileTransport); @@ -418,28 +417,28 @@ int main(int argc, char** argv) { if (runServer) { - stdcxx::shared_ptr serviceProcessor(new ServiceProcessor(serviceHandler)); + std::shared_ptr serviceProcessor(new ServiceProcessor(serviceHandler)); // Transport - stdcxx::shared_ptr serverSocket(new TServerSocket(port)); + std::shared_ptr serverSocket(new TServerSocket(port)); // Transport Factory - stdcxx::shared_ptr transportFactory(new TBufferedTransportFactory()); + std::shared_ptr transportFactory(new TBufferedTransportFactory()); // Protocol Factory - stdcxx::shared_ptr protocolFactory(new TBinaryProtocolFactory()); + std::shared_ptr protocolFactory(new TBinaryProtocolFactory()); if (logRequests) { // initialize the log file - stdcxx::shared_ptr fileTransport(new TFileTransport(requestLogPath)); + std::shared_ptr fileTransport(new TFileTransport(requestLogPath)); fileTransport->setChunkSize(2 * 1024 * 1024); fileTransport->setMaxEventSize(1024 * 16); transportFactory - = stdcxx::shared_ptr(new TPipedTransportFactory(fileTransport)); + = std::shared_ptr(new TPipedTransportFactory(fileTransport)); } - stdcxx::shared_ptr server; + std::shared_ptr server; if (serverType == "simple") { @@ -453,7 +452,7 @@ int main(int argc, char** argv) { } else if (serverType == "thread-pool") { - stdcxx::shared_ptr threadManager + std::shared_ptr threadManager = ThreadManager::newSimpleThreadManager(workerCount); threadManager->threadFactory(threadFactory); @@ -465,9 +464,9 @@ int main(int argc, char** argv) { threadManager)); } - stdcxx::shared_ptr observer(new TStartObserver); + std::shared_ptr observer(new TStartObserver); server->setServerEventHandler(observer); - stdcxx::shared_ptr serverThread = threadFactory->newThread(server); + std::shared_ptr serverThread = threadFactory->newThread(server); cerr << "Starting the server on port " << port << endl; @@ -486,7 +485,7 @@ int main(int argc, char** argv) { size_t threadCount = 0; - set > clientThreads; + set > clientThreads; if (callName == "echoVoid") { loopType = T_VOID; @@ -505,28 +504,28 @@ int main(int argc, char** argv) { if(clientType == "regular") { for (size_t ix = 0; ix < clientCount; ix++) { - stdcxx::shared_ptr socket(new TSocket("127.0.0.1", port)); - stdcxx::shared_ptr bufferedSocket(new TBufferedTransport(socket, 2048)); - stdcxx::shared_ptr protocol(new TBinaryProtocol(bufferedSocket)); - stdcxx::shared_ptr serviceClient(new ServiceClient(protocol)); + std::shared_ptr socket(new TSocket("127.0.0.1", port)); + std::shared_ptr bufferedSocket(new TBufferedTransport(socket, 2048)); + std::shared_ptr protocol(new TBinaryProtocol(bufferedSocket)); + std::shared_ptr serviceClient(new ServiceClient(protocol)); - clientThreads.insert(threadFactory->newThread(stdcxx::shared_ptr( + clientThreads.insert(threadFactory->newThread(std::shared_ptr( new ClientThread(socket, serviceClient, monitor, threadCount, loopCount, loopType, OpenAndCloseTransportInThread)))); } } else if(clientType == "concurrent") { - stdcxx::shared_ptr socket(new TSocket("127.0.0.1", port)); - stdcxx::shared_ptr bufferedSocket(new TBufferedTransport(socket, 2048)); - stdcxx::shared_ptr protocol(new TBinaryProtocol(bufferedSocket)); - //stdcxx::shared_ptr serviceClient(new ServiceClient(protocol)); - stdcxx::shared_ptr serviceClient(new ServiceConcurrentClient(protocol)); + std::shared_ptr socket(new TSocket("127.0.0.1", port)); + std::shared_ptr bufferedSocket(new TBufferedTransport(socket, 2048)); + std::shared_ptr protocol(new TBinaryProtocol(bufferedSocket)); + auto sync = std::make_shared(); + std::shared_ptr serviceClient(new ServiceConcurrentClient(protocol, sync)); socket->open(); for (size_t ix = 0; ix < clientCount; ix++) { - clientThreads.insert(threadFactory->newThread(stdcxx::shared_ptr( + clientThreads.insert(threadFactory->newThread(std::shared_ptr( new ClientThread(socket, serviceClient, monitor, threadCount, loopCount, loopType, DontOpenAndCloseTransportInThread)))); } } - for (std::set >::const_iterator thread = clientThreads.begin(); + for (auto thread = clientThreads.begin(); thread != clientThreads.end(); thread++) { (*thread)->start(); @@ -541,7 +540,7 @@ int main(int argc, char** argv) { cerr << "Launch " << clientCount << " " << clientType << " client threads" << endl; - time00 = Util::currentTime(); + time00 = std::chrono::duration_cast(std::chrono::steady_clock::now().time_since_epoch()).count(); monitor.notifyAll(); @@ -549,7 +548,7 @@ int main(int argc, char** argv) { monitor.wait(); } - time01 = Util::currentTime(); + time01 = std::chrono::duration_cast(std::chrono::steady_clock::now().time_since_epoch()).count(); } int64_t firstTime = 9223372036854775807LL; @@ -559,12 +558,12 @@ int main(int argc, char** argv) { int64_t minTime = 9223372036854775807LL; int64_t maxTime = 0; - for (set >::iterator ix = clientThreads.begin(); + for (auto ix = clientThreads.begin(); ix != clientThreads.end(); ix++) { - stdcxx::shared_ptr client - = stdcxx::dynamic_pointer_cast((*ix)->runnable()); + std::shared_ptr client + = std::dynamic_pointer_cast((*ix)->runnable()); int64_t delta = client->_endTime - client->_startTime; diff --git a/test/cpp/src/StressTestNonBlocking.cpp b/test/cpp/src/StressTestNonBlocking.cpp index e68988f41b3..e94ecb2db11 100644 --- a/test/cpp/src/StressTestNonBlocking.cpp +++ b/test/cpp/src/StressTestNonBlocking.cpp @@ -18,9 +18,8 @@ */ #include -#include +#include #include -#include #include #include #include @@ -33,7 +32,6 @@ #include #include #include -#include #include "Service.h" @@ -69,7 +67,7 @@ typedef map count_map; class Server : public ServiceIf { public: - Server() {} + Server() = default; void count(const char* method) { Guard m(lock_); @@ -77,7 +75,7 @@ class Server : public ServiceIf { counts_[method] = ++ct; } - void echoVoid() { + void echoVoid() override { count("echoVoid"); // Sleep to simulate work THRIFT_SLEEP_USEC(1); @@ -89,18 +87,18 @@ class Server : public ServiceIf { return counts_; } - int8_t echoByte(const int8_t arg) { return arg; } - int32_t echoI32(const int32_t arg) { return arg; } - int64_t echoI64(const int64_t arg) { return arg; } - void echoString(string& out, const string& arg) { + int8_t echoByte(const int8_t arg) override { return arg; } + int32_t echoI32(const int32_t arg) override { return arg; } + int64_t echoI64(const int64_t arg) override { return arg; } + void echoString(string& out, const string& arg) override { if (arg != "hello") { T_ERROR_ABORT("WRONG STRING (%s)!!!!", arg.c_str()); } out = arg; } - void echoList(vector& out, const vector& arg) { out = arg; } - void echoSet(set& out, const set& arg) { out = arg; } - void echoMap(map& out, const map& arg) { out = arg; } + void echoList(vector& out, const vector& arg) override { out = arg; } + void echoSet(set& out, const set& arg) override { out = arg; } + void echoMap(map& out, const map& arg) override { out = arg; } private: count_map counts_; @@ -109,8 +107,8 @@ class Server : public ServiceIf { class ClientThread : public Runnable { public: - ClientThread(stdcxx::shared_ptr transport, - stdcxx::shared_ptr client, + ClientThread(std::shared_ptr transport, + std::shared_ptr client, Monitor& monitor, size_t& workerCount, size_t loopCount, @@ -122,7 +120,7 @@ class ClientThread : public Runnable { _loopCount(loopCount), _loopType(loopType) {} - void run() { + void run() override { // Wait for all worker threads to start @@ -133,7 +131,7 @@ class ClientThread : public Runnable { } } - _startTime = Util::currentTime(); + _startTime = std::chrono::duration_cast(std::chrono::steady_clock::now().time_since_epoch()).count(); _transport->open(); @@ -158,7 +156,7 @@ class ClientThread : public Runnable { break; } - _endTime = Util::currentTime(); + _endTime = std::chrono::duration_cast(std::chrono::steady_clock::now().time_since_epoch()).count(); _transport->close(); @@ -221,8 +219,8 @@ class ClientThread : public Runnable { } } - stdcxx::shared_ptr _transport; - stdcxx::shared_ptr _client; + std::shared_ptr _transport; + std::shared_ptr _client; Monitor& _monitor; size_t& _workerCount; size_t _loopCount; @@ -344,24 +342,24 @@ int main(int argc, char** argv) { cerr << usage.str(); } - stdcxx::shared_ptr threadFactory - = stdcxx::shared_ptr(new PlatformThreadFactory()); + std::shared_ptr threadFactory + = std::shared_ptr(new ThreadFactory()); // Dispatcher - stdcxx::shared_ptr serviceHandler(new Server()); + std::shared_ptr serviceHandler(new Server()); if (replayRequests) { - stdcxx::shared_ptr serviceHandler(new Server()); - stdcxx::shared_ptr serviceProcessor(new ServiceProcessor(serviceHandler)); + std::shared_ptr serviceHandler(new Server()); + std::shared_ptr serviceProcessor(new ServiceProcessor(serviceHandler)); // Transports - stdcxx::shared_ptr fileTransport(new TFileTransport(requestLogPath)); + std::shared_ptr fileTransport(new TFileTransport(requestLogPath)); fileTransport->setChunkSize(2 * 1024 * 1024); fileTransport->setMaxEventSize(1024 * 16); fileTransport->seekToEnd(); // Protocol Factory - stdcxx::shared_ptr protocolFactory(new TBinaryProtocolFactory()); + std::shared_ptr protocolFactory(new TBinaryProtocolFactory()); TFileProcessor fileProcessor(serviceProcessor, protocolFactory, fileTransport); @@ -371,50 +369,50 @@ int main(int argc, char** argv) { if (runServer) { - stdcxx::shared_ptr serviceProcessor(new ServiceProcessor(serviceHandler)); + std::shared_ptr serviceProcessor(new ServiceProcessor(serviceHandler)); // Protocol Factory - stdcxx::shared_ptr protocolFactory(new TBinaryProtocolFactory()); + std::shared_ptr protocolFactory(new TBinaryProtocolFactory()); // Transport Factory - stdcxx::shared_ptr transportFactory; + std::shared_ptr transportFactory; if (logRequests) { // initialize the log file - stdcxx::shared_ptr fileTransport(new TFileTransport(requestLogPath)); + std::shared_ptr fileTransport(new TFileTransport(requestLogPath)); fileTransport->setChunkSize(2 * 1024 * 1024); fileTransport->setMaxEventSize(1024 * 16); transportFactory - = stdcxx::shared_ptr(new TPipedTransportFactory(fileTransport)); + = std::shared_ptr(new TPipedTransportFactory(fileTransport)); } - stdcxx::shared_ptr serverThread; - stdcxx::shared_ptr serverThread2; - stdcxx::shared_ptr nbSocket1; - stdcxx::shared_ptr nbSocket2; + std::shared_ptr serverThread; + std::shared_ptr serverThread2; + std::shared_ptr nbSocket1; + std::shared_ptr nbSocket2; if (serverType == "simple") { nbSocket1.reset(new transport::TNonblockingServerSocket(port)); - serverThread = threadFactory->newThread(stdcxx::shared_ptr( + serverThread = threadFactory->newThread(std::shared_ptr( new TNonblockingServer(serviceProcessor, protocolFactory, nbSocket1))); nbSocket2.reset(new transport::TNonblockingServerSocket(port + 1)); - serverThread2 = threadFactory->newThread(stdcxx::shared_ptr( + serverThread2 = threadFactory->newThread(std::shared_ptr( new TNonblockingServer(serviceProcessor, protocolFactory, nbSocket2))); } else if (serverType == "thread-pool") { - stdcxx::shared_ptr threadManager + std::shared_ptr threadManager = ThreadManager::newSimpleThreadManager(workerCount); threadManager->threadFactory(threadFactory); threadManager->start(); nbSocket1.reset(new transport::TNonblockingServerSocket(port)); - serverThread = threadFactory->newThread(stdcxx::shared_ptr( + serverThread = threadFactory->newThread(std::shared_ptr( new TNonblockingServer(serviceProcessor, protocolFactory, nbSocket1, threadManager))); nbSocket2.reset(new transport::TNonblockingServerSocket(port + 1)); - serverThread2 = threadFactory->newThread(stdcxx::shared_ptr( + serverThread2 = threadFactory->newThread(std::shared_ptr( new TNonblockingServer(serviceProcessor, protocolFactory, nbSocket2, threadManager))); } @@ -437,7 +435,7 @@ int main(int argc, char** argv) { size_t threadCount = 0; - set > clientThreads; + set > clientThreads; if (callName == "echoVoid") { loopType = T_VOID; @@ -455,16 +453,16 @@ int main(int argc, char** argv) { for (uint32_t ix = 0; ix < clientCount; ix++) { - stdcxx::shared_ptr socket(new TSocket("127.0.0.1", port + (ix % 2))); - stdcxx::shared_ptr framedSocket(new TFramedTransport(socket)); - stdcxx::shared_ptr protocol(new TBinaryProtocol(framedSocket)); - stdcxx::shared_ptr serviceClient(new ServiceClient(protocol)); + std::shared_ptr socket(new TSocket("127.0.0.1", port + (ix % 2))); + std::shared_ptr framedSocket(new TFramedTransport(socket)); + std::shared_ptr protocol(new TBinaryProtocol(framedSocket)); + std::shared_ptr serviceClient(new ServiceClient(protocol)); - clientThreads.insert(threadFactory->newThread(stdcxx::shared_ptr( + clientThreads.insert(threadFactory->newThread(std::shared_ptr( new ClientThread(socket, serviceClient, monitor, threadCount, loopCount, loopType)))); } - for (std::set >::const_iterator thread = clientThreads.begin(); + for (auto thread = clientThreads.begin(); thread != clientThreads.end(); thread++) { (*thread)->start(); @@ -479,7 +477,7 @@ int main(int argc, char** argv) { cerr << "Launch " << clientCount << " client threads" << endl; - time00 = Util::currentTime(); + time00 = std::chrono::duration_cast(std::chrono::steady_clock::now().time_since_epoch()).count(); monitor.notifyAll(); @@ -487,7 +485,7 @@ int main(int argc, char** argv) { monitor.wait(); } - time01 = Util::currentTime(); + time01 = std::chrono::duration_cast(std::chrono::steady_clock::now().time_since_epoch()).count(); } int64_t firstTime = 9223372036854775807LL; @@ -497,12 +495,12 @@ int main(int argc, char** argv) { int64_t minTime = 9223372036854775807LL; int64_t maxTime = 0; - for (set >::iterator ix = clientThreads.begin(); + for (auto ix = clientThreads.begin(); ix != clientThreads.end(); ix++) { - stdcxx::shared_ptr client - = stdcxx::dynamic_pointer_cast((*ix)->runnable()); + std::shared_ptr client + = std::dynamic_pointer_cast((*ix)->runnable()); int64_t delta = client->_endTime - client->_startTime; diff --git a/test/cpp/src/TestClient.cpp b/test/cpp/src/TestClient.cpp index ca213260e55..c4146cc5cf5 100644 --- a/test/cpp/src/TestClient.cpp +++ b/test/cpp/src/TestClient.cpp @@ -46,7 +46,6 @@ #include #include #include -#include #if _WIN32 #include #endif @@ -61,12 +60,59 @@ using namespace apache::thrift::protocol; using namespace apache::thrift::transport; using namespace thrift::test; +// +// A pedantic protocol that checks to make sure the response sequence ID +// is the same as the sent sequence ID. lib/cpp always sends zero for +// synchronous clients, so this bumps the number to make sure it gets +// returned properly from the remote server. Any server that does not +// respond with the same sequence number is violating the sequence ID +// agreement between client and server. +// + +template +class TPedanticProtocol : public Proto +{ + public: + TPedanticProtocol(std::shared_ptr& transport) + : Proto(transport), m_last_seqid((std::numeric_limits::max)() - 10) { } + + virtual uint32_t writeMessageBegin_virt(const std::string& name, + const TMessageType messageType, + const int32_t in_seqid) override + { + int32_t seqid = in_seqid; + if (!seqid) { // this is typical for normal cpp generated code + seqid = ++m_last_seqid; + } + + return Proto::writeMessageBegin_virt(name, messageType, seqid); + } + + virtual uint32_t readMessageBegin_virt(std::string& name, + TMessageType& messageType, + int32_t& seqid) override + { + uint32_t result = Proto::readMessageBegin_virt(name, messageType, seqid); + if (seqid != m_last_seqid) { + std::stringstream ss; + ss << "ERROR: send request with seqid " << m_last_seqid << " and got reply with seqid " << seqid; + throw std::logic_error(ss.str()); + } /* else { + std::cout << "verified seqid " << m_last_seqid << " round trip OK" << std::endl; + } */ + return result; + } + + private: + int32_t m_last_seqid; +}; + // Current time, microseconds since the epoch uint64_t now() { int64_t ret; struct timeval tv; - THRIFT_GETTIMEOFDAY(&tv, NULL); + THRIFT_GETTIMEOFDAY(&tv, nullptr); ret = tv.tv_sec; ret = ret * 1000 * 1000 + tv.tv_usec; return ret; @@ -98,10 +144,10 @@ static void testVoid_clientReturn(event_base* base, ThriftTestCobClient* client) for (int testNr = 0; testNr < 10; ++testNr) { std::ostringstream os; os << "test" << testNr; - client->testString(stdcxx::bind(testString_clientReturn, + client->testString(std::bind(testString_clientReturn, base, testNr, - stdcxx::placeholders::_1), + std::placeholders::_1), os.str()); } } catch (TException& exn) { @@ -254,18 +300,18 @@ int main(int argc, char** argv) { } // THRIFT-4164: The factory MUST outlive any sockets it creates for correct behavior! - stdcxx::shared_ptr factory; - stdcxx::shared_ptr socket; - stdcxx::shared_ptr transport; - stdcxx::shared_ptr protocol; - stdcxx::shared_ptr protocol2; // SecondService for multiplexed + std::shared_ptr factory; + std::shared_ptr socket; + std::shared_ptr transport; + std::shared_ptr protocol; + std::shared_ptr protocol2; // SecondService for multiplexed if (ssl) { cout << "Client Certificate File: " << certPath << endl; cout << "Client Key File: " << keyPath << endl; cout << "CA File: " << caPath << endl; - factory = stdcxx::shared_ptr(new TSSLSocketFactory()); + factory = std::shared_ptr(new TSSLSocketFactory()); factory->ciphers("ALL:!ADH:!LOW:!EXP:!MD5:@STRENGTH"); factory->loadTrustedCertificates(caPath.c_str()); factory->loadCertificate(certPath.c_str()); @@ -277,42 +323,46 @@ int main(int argc, char** argv) { if (abstract_namespace) { std::string abstract_socket("\0", 1); abstract_socket += domain_socket; - socket = stdcxx::shared_ptr(new TSocket(abstract_socket)); + socket = std::shared_ptr(new TSocket(abstract_socket)); } else { - socket = stdcxx::shared_ptr(new TSocket(domain_socket)); + socket = std::shared_ptr(new TSocket(domain_socket)); } port = 0; } else { - socket = stdcxx::shared_ptr(new TSocket(host, port)); + socket = std::shared_ptr(new TSocket(host, port)); } } if (transport_type.compare("http") == 0) { - transport = stdcxx::make_shared(socket, host, "/service"); + transport = std::make_shared(socket, host, "/service"); } else if (transport_type.compare("framed") == 0) { - transport = stdcxx::make_shared(socket); + transport = std::make_shared(socket); } else { - transport = stdcxx::make_shared(socket); + transport = std::make_shared(socket); } if (zlib) { - transport = stdcxx::make_shared(transport); + transport = std::make_shared(transport); } if (protocol_type == "json" || protocol_type == "multij") { - protocol = stdcxx::make_shared(transport); + typedef TPedanticProtocol TPedanticJSONProtocol; + protocol = std::make_shared(transport); } else if (protocol_type == "compact" || protocol_type == "multic") { - protocol = stdcxx::make_shared(transport); + typedef TPedanticProtocol TPedanticCompactProtocol; + protocol = std::make_shared(transport); } else if (protocol_type == "header" || protocol_type == "multih") { - protocol = stdcxx::make_shared(transport); + typedef TPedanticProtocol TPedanticHeaderProtocol; + protocol = std::make_shared(transport); } else { - protocol = stdcxx::make_shared(transport); + typedef TPedanticProtocol TPedanticBinaryProtocol; + protocol = std::make_shared(transport); } if (boost::starts_with(protocol_type, "multi")) { - protocol2 = stdcxx::make_shared(protocol, "SecondService"); - // we don't need access to the original protocol any more, so... - protocol = stdcxx::make_shared(protocol, "ThriftTest"); + protocol2 = std::make_shared(protocol, "SecondService"); + // we don't need access to the original protocol any more, so... + protocol = std::make_shared(protocol, "ThriftTest"); } // Connection info @@ -334,14 +384,14 @@ int main(int argc, char** argv) { cout << "Libevent Features: 0x" << hex << event_base_get_features(base) << endl; #endif - stdcxx::shared_ptr protocolFactory(new TBinaryProtocolFactory()); + std::shared_ptr protocolFactory(new TBinaryProtocolFactory()); - stdcxx::shared_ptr channel( + std::shared_ptr channel( new TEvhttpClientChannel(host.c_str(), "/", host.c_str(), port, base)); ThriftTestCobClient* client = new ThriftTestCobClient(channel, protocolFactory.get()); - client->testVoid(stdcxx::bind(testVoid_clientReturn, + client->testVoid(std::bind(testVoid_clientReturn, base, - stdcxx::placeholders::_1)); + std::placeholders::_1)); event_base_loop(base, 0); return 0; @@ -943,11 +993,11 @@ int main(int argc, char** argv) { if (it1 == whoa.end()) { failed = true; } else { - map::const_iterator it12 = it1->second.find(Numberz::TWO); + auto it12 = it1->second.find(Numberz::TWO); if (it12 == it1->second.end() || it12->second != insane) { failed = true; } - map::const_iterator it13 = it1->second.find(Numberz::THREE); + auto it13 = it1->second.find(Numberz::THREE); if (it13 == it1->second.end() || it13->second != insane) { failed = true; } @@ -956,7 +1006,7 @@ int main(int argc, char** argv) { if (it2 == whoa.end()) { failed = true; } else { - map::const_iterator it26 = it2->second.find(Numberz::SIX); + auto it26 = it2->second.find(Numberz::SIX); if (it26 == it2->second.end() || it26->second != Insanity()) { failed = true; } diff --git a/test/cpp/src/TestServer.cpp b/test/cpp/src/TestServer.cpp index 323f873547a..65317f89c13 100644 --- a/test/cpp/src/TestServer.cpp +++ b/test/cpp/src/TestServer.cpp @@ -20,7 +20,7 @@ #include #include #include -#include +#include #include #include #include @@ -39,6 +39,7 @@ #include #include #include +#include #include #include "SecondService.h" @@ -61,7 +62,6 @@ #include #include #include -#include #if _WIN32 #include @@ -91,48 +91,48 @@ void signal_handler(int signum) class TestHandler : public ThriftTestIf { public: - TestHandler() {} + TestHandler() = default; - void testVoid() { printf("testVoid()\n"); } + void testVoid() override { printf("testVoid()\n"); } - void testString(string& out, const string& thing) { + void testString(string& out, const string& thing) override { printf("testString(\"%s\")\n", thing.c_str()); out = thing; } - bool testBool(const bool thing) { + bool testBool(const bool thing) override { printf("testBool(%s)\n", thing ? "true" : "false"); return thing; } - int8_t testByte(const int8_t thing) { + int8_t testByte(const int8_t thing) override { printf("testByte(%d)\n", (int)thing); return thing; } - int32_t testI32(const int32_t thing) { + int32_t testI32(const int32_t thing) override { printf("testI32(%d)\n", thing); return thing; } - int64_t testI64(const int64_t thing) { + int64_t testI64(const int64_t thing) override { printf("testI64(%" PRId64 ")\n", thing); return thing; } - double testDouble(const double thing) { + double testDouble(const double thing) override { printf("testDouble(%f)\n", thing); return thing; } - void testBinary(std::string& _return, const std::string& thing) { + void testBinary(std::string& _return, const std::string& thing) override { std::ostringstream hexstr; hexstr << std::hex << thing; printf("testBinary(%lu: %s)\n", safe_numeric_cast(thing.size()), hexstr.str().c_str()); _return = thing; } - void testStruct(Xtruct& out, const Xtruct& thing) { + void testStruct(Xtruct& out, const Xtruct& thing) override { printf("testStruct({\"%s\", %d, %d, %" PRId64 "})\n", thing.string_thing.c_str(), (int)thing.byte_thing, @@ -141,7 +141,7 @@ class TestHandler : public ThriftTestIf { out = thing; } - void testNest(Xtruct2& out, const Xtruct2& nest) { + void testNest(Xtruct2& out, const Xtruct2& nest) override { const Xtruct& thing = nest.struct_thing; printf("testNest({%d, {\"%s\", %d, %d, %" PRId64 "}, %d})\n", (int)nest.byte_thing, @@ -153,7 +153,7 @@ class TestHandler : public ThriftTestIf { out = nest; } - void testMap(map& out, const map& thing) { + void testMap(map& out, const map& thing) override { printf("testMap({"); map::const_iterator m_iter; bool first = true; @@ -170,7 +170,7 @@ class TestHandler : public ThriftTestIf { } void testStringMap(map& out, - const map& thing) { + const map& thing) override { printf("testMap({"); map::const_iterator m_iter; bool first = true; @@ -186,7 +186,7 @@ class TestHandler : public ThriftTestIf { out = thing; } - void testSet(set& out, const set& thing) { + void testSet(set& out, const set& thing) override { printf("testSet({"); set::const_iterator s_iter; bool first = true; @@ -202,7 +202,7 @@ class TestHandler : public ThriftTestIf { out = thing; } - void testList(vector& out, const vector& thing) { + void testList(vector& out, const vector& thing) override { printf("testList({"); vector::const_iterator l_iter; bool first = true; @@ -218,17 +218,17 @@ class TestHandler : public ThriftTestIf { out = thing; } - Numberz::type testEnum(const Numberz::type thing) { + Numberz::type testEnum(const Numberz::type thing) override { printf("testEnum(%d)\n", thing); return thing; } - UserId testTypedef(const UserId thing) { + UserId testTypedef(const UserId thing) override { printf("testTypedef(%" PRId64 ")\n", thing); return thing; } - void testMapMap(map >& mapmap, const int32_t hello) { + void testMapMap(map >& mapmap, const int32_t hello) override { printf("testMapMap(%d)\n", hello); map pos; @@ -242,7 +242,7 @@ class TestHandler : public ThriftTestIf { mapmap.insert(make_pair(-4, neg)); } - void testInsanity(map >& insane, const Insanity& argument) { + void testInsanity(map >& insane, const Insanity& argument) override { printf("testInsanity()\n"); Insanity looney; @@ -298,7 +298,7 @@ class TestHandler : public ThriftTestIf { const int64_t arg2, const std::map& arg3, const Numberz::type arg4, - const UserId arg5) { + const UserId arg5) override { (void)arg3; (void)arg4; (void)arg5; @@ -311,7 +311,7 @@ class TestHandler : public ThriftTestIf { hello.i64_thing = (int64_t)arg2; } - void testException(const std::string& arg) { + void testException(const std::string& arg) override { printf("testException(%s)\n", arg.c_str()); if (arg.compare("Xception") == 0) { Xception e; @@ -330,7 +330,7 @@ class TestHandler : public ThriftTestIf { void testMultiException(Xtruct& result, const std::string& arg0, - const std::string& arg1) { + const std::string& arg1) override { printf("testMultiException(%s, %s)\n", arg0.c_str(), arg1.c_str()); @@ -350,7 +350,7 @@ class TestHandler : public ThriftTestIf { } } - void testOneway(const int32_t aNum) { + void testOneway(const int32_t aNum) override { printf("testOneway(%d): call received\n", aNum); } }; @@ -358,33 +358,33 @@ class TestHandler : public ThriftTestIf { class SecondHandler : public SecondServiceIf { public: - void secondtestString(std::string& result, const std::string& thing) + void secondtestString(std::string& result, const std::string& thing) override { result = "testString(\"" + thing + "\")"; } }; class TestProcessorEventHandler : public TProcessorEventHandler { - virtual void* getContext(const char* fn_name, void* serverContext) { + void* getContext(const char* fn_name, void* serverContext) override { (void)serverContext; return new std::string(fn_name); } - virtual void freeContext(void* ctx, const char* fn_name) { + void freeContext(void* ctx, const char* fn_name) override { (void)fn_name; delete static_cast(ctx); } - virtual void preRead(void* ctx, const char* fn_name) { communicate("preRead", ctx, fn_name); } - virtual void postRead(void* ctx, const char* fn_name, uint32_t bytes) { + void preRead(void* ctx, const char* fn_name) override { communicate("preRead", ctx, fn_name); } + void postRead(void* ctx, const char* fn_name, uint32_t bytes) override { (void)bytes; communicate("postRead", ctx, fn_name); } - virtual void preWrite(void* ctx, const char* fn_name) { communicate("preWrite", ctx, fn_name); } - virtual void postWrite(void* ctx, const char* fn_name, uint32_t bytes) { + void preWrite(void* ctx, const char* fn_name) override { communicate("preWrite", ctx, fn_name); } + void postWrite(void* ctx, const char* fn_name, uint32_t bytes) override { (void)bytes; communicate("postWrite", ctx, fn_name); } - virtual void asyncComplete(void* ctx, const char* fn_name) { + void asyncComplete(void* ctx, const char* fn_name) override { communicate("asyncComplete", ctx, fn_name); } - virtual void handlerError(void* ctx, const char* fn_name) { + void handlerError(void* ctx, const char* fn_name) override { communicate("handlerError", ctx, fn_name); } @@ -395,137 +395,137 @@ class TestProcessorEventHandler : public TProcessorEventHandler { class TestHandlerAsync : public ThriftTestCobSvIf { public: - TestHandlerAsync(stdcxx::shared_ptr& handler) : _delegate(handler) {} - virtual ~TestHandlerAsync() {} + TestHandlerAsync(std::shared_ptr& handler) : _delegate(handler) {} + ~TestHandlerAsync() override = default; - virtual void testVoid(stdcxx::function cob) { + void testVoid(std::function cob) override { _delegate->testVoid(); cob(); } - virtual void testString(stdcxx::function cob, - const std::string& thing) { + void testString(std::function cob, + const std::string& thing) override { std::string res; _delegate->testString(res, thing); cob(res); } - virtual void testBool(stdcxx::function cob, const bool thing) { + void testBool(std::function cob, const bool thing) override { bool res = _delegate->testBool(thing); cob(res); } - virtual void testByte(stdcxx::function cob, const int8_t thing) { + void testByte(std::function cob, const int8_t thing) override { int8_t res = _delegate->testByte(thing); cob(res); } - virtual void testI32(stdcxx::function cob, const int32_t thing) { + void testI32(std::function cob, const int32_t thing) override { int32_t res = _delegate->testI32(thing); cob(res); } - virtual void testI64(stdcxx::function cob, const int64_t thing) { + void testI64(std::function cob, const int64_t thing) override { int64_t res = _delegate->testI64(thing); cob(res); } - virtual void testDouble(stdcxx::function cob, const double thing) { + void testDouble(std::function cob, const double thing) override { double res = _delegate->testDouble(thing); cob(res); } - virtual void testBinary(stdcxx::function cob, - const std::string& thing) { + void testBinary(std::function cob, + const std::string& thing) override { std::string res; _delegate->testBinary(res, thing); cob(res); } - virtual void testStruct(stdcxx::function cob, const Xtruct& thing) { + void testStruct(std::function cob, const Xtruct& thing) override { Xtruct res; _delegate->testStruct(res, thing); cob(res); } - virtual void testNest(stdcxx::function cob, const Xtruct2& thing) { + void testNest(std::function cob, const Xtruct2& thing) override { Xtruct2 res; _delegate->testNest(res, thing); cob(res); } - virtual void testMap(stdcxx::function const& _return)> cob, - const std::map& thing) { + void testMap(std::function const& _return)> cob, + const std::map& thing) override { std::map res; _delegate->testMap(res, thing); cob(res); } - virtual void testStringMap( - stdcxx::function const& _return)> cob, - const std::map& thing) { + void testStringMap( + std::function const& _return)> cob, + const std::map& thing) override { std::map res; _delegate->testStringMap(res, thing); cob(res); } - virtual void testSet(stdcxx::function const& _return)> cob, - const std::set& thing) { + void testSet(std::function const& _return)> cob, + const std::set& thing) override { std::set res; _delegate->testSet(res, thing); cob(res); } - virtual void testList(stdcxx::function const& _return)> cob, - const std::vector& thing) { + void testList(std::function const& _return)> cob, + const std::vector& thing) override { std::vector res; _delegate->testList(res, thing); cob(res); } - virtual void testEnum(stdcxx::function cob, - const Numberz::type thing) { + void testEnum(std::function cob, + const Numberz::type thing) override { Numberz::type res = _delegate->testEnum(thing); cob(res); } - virtual void testTypedef(stdcxx::function cob, const UserId thing) { + void testTypedef(std::function cob, const UserId thing) override { UserId res = _delegate->testTypedef(thing); cob(res); } - virtual void testMapMap( - stdcxx::function > const& _return)> cob, - const int32_t hello) { + void testMapMap( + std::function > const& _return)> cob, + const int32_t hello) override { std::map > res; _delegate->testMapMap(res, hello); cob(res); } - virtual void testInsanity( - stdcxx::function > const& _return)> cob, - const Insanity& argument) { + void testInsanity( + std::function > const& _return)> cob, + const Insanity& argument) override { std::map > res; _delegate->testInsanity(res, argument); cob(res); } - virtual void testMulti(stdcxx::function cob, + void testMulti(std::function cob, const int8_t arg0, const int32_t arg1, const int64_t arg2, const std::map& arg3, const Numberz::type arg4, - const UserId arg5) { + const UserId arg5) override { Xtruct res; _delegate->testMulti(res, arg0, arg1, arg2, arg3, arg4, arg5); cob(res); } - virtual void testException( - stdcxx::function cob, - stdcxx::function exn_cob, - const std::string& arg) { + void testException( + std::function cob, + std::function exn_cob, + const std::string& arg) override { try { _delegate->testException(arg); } catch (const apache::thrift::TException& e) { @@ -535,11 +535,11 @@ class TestHandlerAsync : public ThriftTestCobSvIf { cob(); } - virtual void testMultiException( - stdcxx::function cob, - stdcxx::function exn_cob, + void testMultiException( + std::function cob, + std::function exn_cob, const std::string& arg0, - const std::string& arg1) { + const std::string& arg1) override { Xtruct res; try { _delegate->testMultiException(res, arg0, arg1); @@ -550,13 +550,13 @@ class TestHandlerAsync : public ThriftTestCobSvIf { cob(res); } - virtual void testOneway(stdcxx::function cob, const int32_t secondsToSleep) { + void testOneway(std::function cob, const int32_t secondsToSleep) override { _delegate->testOneway(secondsToSleep); cob(); } protected: - stdcxx::shared_ptr _delegate; + std::shared_ptr _delegate; }; namespace po = boost::program_options; @@ -589,7 +589,7 @@ int main(int argc, char** argv) { ("domain-socket", po::value(&domain_socket) ->default_value(domain_socket), "Unix Domain Socket (e.g. /tmp/ThriftTest.thrift)") ("abstract-namespace", "Create the domain socket in the Abstract Namespace (no connection with filesystem pathnames)") ("server-type", po::value(&server_type)->default_value(server_type), "type of server, \"simple\", \"thread-pool\", \"threaded\", or \"nonblocking\"") - ("transport", po::value(&transport_type)->default_value(transport_type), "transport: buffered, framed, http, zlib") + ("transport", po::value(&transport_type)->default_value(transport_type), "transport: buffered, framed, http, websocket, zlib") ("protocol", po::value(&protocol_type)->default_value(protocol_type), "protocol: binary, compact, header, json, multi, multic, multih, multij") ("ssl", "Encrypted Transport using SSL") ("zlib", "Wrapped Transport using Zlib") @@ -636,6 +636,7 @@ int main(int argc, char** argv) { if (transport_type == "buffered") { } else if (transport_type == "framed") { } else if (transport_type == "http") { + } else if (transport_type == "websocket") { } else if (transport_type == "zlib") { // crosstester will pass zlib as a flag and a transport right now... } else { @@ -668,76 +669,82 @@ int main(int argc, char** argv) { } // Dispatcher - stdcxx::shared_ptr protocolFactory; + std::shared_ptr protocolFactory; if (protocol_type == "json" || protocol_type == "multij") { - stdcxx::shared_ptr jsonProtocolFactory(new TJSONProtocolFactory()); + std::shared_ptr jsonProtocolFactory(new TJSONProtocolFactory()); protocolFactory = jsonProtocolFactory; } else if (protocol_type == "compact" || protocol_type == "multic") { - TCompactProtocolFactoryT *compactProtocolFactory = new TCompactProtocolFactoryT(); + auto *compactProtocolFactory = new TCompactProtocolFactoryT(); compactProtocolFactory->setContainerSizeLimit(container_limit); compactProtocolFactory->setStringSizeLimit(string_limit); protocolFactory.reset(compactProtocolFactory); } else if (protocol_type == "header" || protocol_type == "multih") { - stdcxx::shared_ptr headerProtocolFactory(new THeaderProtocolFactory()); + std::shared_ptr headerProtocolFactory(new THeaderProtocolFactory()); protocolFactory = headerProtocolFactory; } else { - TBinaryProtocolFactoryT* binaryProtocolFactory = new TBinaryProtocolFactoryT(); + auto* binaryProtocolFactory = new TBinaryProtocolFactoryT(); binaryProtocolFactory->setContainerSizeLimit(container_limit); binaryProtocolFactory->setStringSizeLimit(string_limit); protocolFactory.reset(binaryProtocolFactory); } // Processors - stdcxx::shared_ptr testHandler(new TestHandler()); - stdcxx::shared_ptr testProcessor(new ThriftTestProcessor(testHandler)); + std::shared_ptr testHandler(new TestHandler()); + std::shared_ptr testProcessor(new ThriftTestProcessor(testHandler)); if (vm.count("processor-events")) { testProcessor->setEventHandler( - stdcxx::shared_ptr(new TestProcessorEventHandler())); + std::shared_ptr(new TestProcessorEventHandler())); } // Transport - stdcxx::shared_ptr sslSocketFactory; - stdcxx::shared_ptr serverSocket; + std::shared_ptr sslSocketFactory; + std::shared_ptr serverSocket; if (ssl) { - sslSocketFactory = stdcxx::shared_ptr(new TSSLSocketFactory()); + sslSocketFactory = std::shared_ptr(new TSSLSocketFactory()); sslSocketFactory->loadCertificate(certPath.c_str()); sslSocketFactory->loadPrivateKey(keyPath.c_str()); sslSocketFactory->ciphers("ALL:!ADH:!LOW:!EXP:!MD5:@STRENGTH"); if (server_type != "nonblocking") { - serverSocket = stdcxx::shared_ptr(new TSSLServerSocket(port, sslSocketFactory)); + serverSocket = std::shared_ptr(new TSSLServerSocket(port, sslSocketFactory)); } } else { if (domain_socket != "") { if (abstract_namespace) { std::string abstract_socket("\0", 1); abstract_socket += domain_socket; - serverSocket = stdcxx::shared_ptr(new TServerSocket(abstract_socket)); + serverSocket = std::shared_ptr(new TServerSocket(abstract_socket)); } else { unlink(domain_socket.c_str()); - serverSocket = stdcxx::shared_ptr(new TServerSocket(domain_socket)); + serverSocket = std::shared_ptr(new TServerSocket(domain_socket)); } port = 0; } else { - serverSocket = stdcxx::shared_ptr(new TServerSocket(port)); + serverSocket = std::shared_ptr(new TServerSocket(port)); } } // Factory - stdcxx::shared_ptr transportFactory; + std::shared_ptr transportFactory; if (transport_type == "http" && server_type != "nonblocking") { - transportFactory = stdcxx::make_shared(); + transportFactory = std::make_shared(); + } else if (transport_type == "websocket" && server_type != "nonblocking") { + if (protocol_type == "json" || protocol_type == "multij") { + transportFactory = std::make_shared(); + } else { + transportFactory = std::make_shared(); + } } else if (transport_type == "framed") { - transportFactory = stdcxx::make_shared(); + transportFactory = std::make_shared(); } else { - transportFactory = stdcxx::make_shared(); + transportFactory = std::make_shared(); } if (zlib) { - // hmm.. doesn't seem to be a way to make it wrap the others... - transportFactory = stdcxx::make_shared(); + // currently TZlibTransportFactory is the only factory than can wrap another: + transportFactory = std::make_shared(transportFactory); } // Server Info @@ -754,27 +761,27 @@ int main(int argc, char** argv) { // Multiplexed Processor if needed if (boost::starts_with(protocol_type, "multi")) { - stdcxx::shared_ptr secondHandler(new SecondHandler()); - stdcxx::shared_ptr secondProcessor(new SecondServiceProcessor(secondHandler)); + std::shared_ptr secondHandler(new SecondHandler()); + std::shared_ptr secondProcessor(new SecondServiceProcessor(secondHandler)); - stdcxx::shared_ptr multiplexedProcessor(new TMultiplexedProcessor()); + std::shared_ptr multiplexedProcessor(new TMultiplexedProcessor()); multiplexedProcessor->registerDefault(testProcessor); // non-multi clients go to the default processor (multi:binary, multic:compact, ...) multiplexedProcessor->registerProcessor("ThriftTest", testProcessor); multiplexedProcessor->registerProcessor("SecondService", secondProcessor); - testProcessor = stdcxx::dynamic_pointer_cast(multiplexedProcessor); + testProcessor = std::dynamic_pointer_cast(multiplexedProcessor); } // Server - stdcxx::shared_ptr server; + std::shared_ptr server; if (server_type == "simple") { server.reset(new TSimpleServer(testProcessor, serverSocket, transportFactory, protocolFactory)); } else if (server_type == "thread-pool") { - stdcxx::shared_ptr threadFactory - = stdcxx::shared_ptr(new PlatformThreadFactory()); + std::shared_ptr threadFactory + = std::shared_ptr(new ThreadFactory()); - stdcxx::shared_ptr threadManager = ThreadManager::newSimpleThreadManager(workers); + std::shared_ptr threadManager = ThreadManager::newSimpleThreadManager(workers); threadManager->threadFactory(threadFactory); threadManager->start(); @@ -788,10 +795,10 @@ int main(int argc, char** argv) { new TThreadedServer(testProcessor, serverSocket, transportFactory, protocolFactory)); } else if (server_type == "nonblocking") { if (transport_type == "http") { - stdcxx::shared_ptr testHandlerAsync(new TestHandlerAsync(testHandler)); - stdcxx::shared_ptr testProcessorAsync( + std::shared_ptr testHandlerAsync(new TestHandlerAsync(testHandler)); + std::shared_ptr testProcessorAsync( new ThriftTestAsyncProcessor(testHandlerAsync)); - stdcxx::shared_ptr testBufferProcessor( + std::shared_ptr testBufferProcessor( new TAsyncProtocolProcessor(testProcessorAsync, protocolFactory)); // not loading nonblockingServer into "server" because @@ -800,7 +807,7 @@ int main(int argc, char** argv) { TEvhttpServer nonblockingServer(testBufferProcessor, port); nonblockingServer.serve(); } else if (transport_type == "framed") { - stdcxx::shared_ptr nbSocket; + std::shared_ptr nbSocket; nbSocket.reset( ssl ? new transport::TNonblockingSSLServerSocket(port, sslSocketFactory) : new transport::TNonblockingServerSocket(port)); @@ -811,17 +818,17 @@ int main(int argc, char** argv) { } } - if (server.get() != NULL) { + if (server.get() != nullptr) { if (protocol_type == "header") { // Tell the server to use the same protocol for input / output // if using header - server->setOutputProtocolFactory(stdcxx::shared_ptr()); + server->setOutputProtocolFactory(std::shared_ptr()); } - - apache::thrift::concurrency::PlatformThreadFactory factory; + + apache::thrift::concurrency::ThreadFactory factory; factory.setDetached(false); - stdcxx::shared_ptr serverThreadRunner(server); - stdcxx::shared_ptr thread + std::shared_ptr serverThreadRunner(server); + std::shared_ptr thread = factory.newThread(serverThreadRunner); #ifdef HAVE_SIGNAL_H @@ -830,7 +837,7 @@ int main(int argc, char** argv) { thread->start(); gMonitor.waitForever(); // wait for a shutdown signal - + #ifdef HAVE_SIGNAL_H signal(SIGINT, SIG_DFL); #endif @@ -843,4 +850,3 @@ int main(int argc, char** argv) { cout << "done." << endl; return 0; } - diff --git a/test/crossrunner/compat.py b/test/crossrunner/compat.py index f1ca91bb35f..932a48cd6ad 100644 --- a/test/crossrunner/compat.py +++ b/test/crossrunner/compat.py @@ -8,9 +8,9 @@ def path_join(*args): bin_args = map(lambda a: a.decode(_ENCODE), args) return os.path.join(*bin_args).encode(_ENCODE) - def str_join(s, l): - bin_args = map(lambda a: a.decode(_ENCODE), l) - b = s.decode(_ENCODE) + def str_join(left, right): + bin_args = map(lambda a: a.decode(_ENCODE), right) + b = left.decode(_ENCODE) return b.join(bin_args).encode(_ENCODE) logfile_open = open diff --git a/test/crossrunner/run.py b/test/crossrunner/run.py index ef8fb60f3ca..bb06d25efc5 100644 --- a/test/crossrunner/run.py +++ b/test/crossrunner/run.py @@ -259,7 +259,7 @@ def ensure_socket_open(sv, port, test): raise logger.warn('Error executing [%s]', test.name, exc_info=True) return (retry_count, RESULT_ERROR) - except: + except Exception: logger.info('Interrupted execution', exc_info=True) if not async_mode: raise diff --git a/test/csharp/Makefile.am b/test/csharp/Makefile.am deleted file mode 100644 index ad166e38408..00000000000 --- a/test/csharp/Makefile.am +++ /dev/null @@ -1,95 +0,0 @@ -# -# Licensed to the Apache Software Foundation (ASF) under one -# or more contributor license agreements. See the NOTICE file -# distributed with this work for additional information -# regarding copyright ownership. The ASF licenses this file -# to you under the Apache License, Version 2.0 (the -# "License"); you may not use this file except in compliance -# with the License. You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, -# software distributed under the License is distributed on an -# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -# KIND, either express or implied. See the License for the -# specific language governing permissions and limitations -# under the License. -# - -GENERATED = \ - gen-csharp/Thrift/Test/Bonk.cs \ - gen-csharp/Thrift/Test/Bools.cs \ - gen-csharp/Thrift/Test/BoolTest.cs \ - gen-csharp/Thrift/Test/CrazyNesting.cs \ - gen-csharp/Thrift/Test/EmptyStruct.cs \ - gen-csharp/Thrift/Test/GuessProtocolStruct.cs \ - gen-csharp/Thrift/Test/Insanity.cs \ - gen-csharp/Thrift/Test/LargeDeltas.cs \ - gen-csharp/Thrift/Test/ListBonks.cs \ - gen-csharp/Thrift/Test/ListTypeVersioningV1.cs \ - gen-csharp/Thrift/Test/ListTypeVersioningV2.cs \ - gen-csharp/Thrift/Test/NestedListsBonk.cs \ - gen-csharp/Thrift/Test/NestedListsI32x2.cs \ - gen-csharp/Thrift/Test/NestedListsI32x3.cs \ - gen-csharp/Thrift/Test/NestedMixedx2.cs \ - gen-csharp/Thrift/Test/Numberz.cs \ - gen-csharp/Thrift/Test/OneField.cs \ - gen-csharp/Thrift/Test/SecondService.cs \ - gen-csharp/Thrift/Test/StructA.cs \ - gen-csharp/Thrift/Test/StructB.cs \ - gen-csharp/Thrift/Test/ThriftTest.Constants.cs \ - gen-csharp/Thrift/Test/ThriftTest.cs \ - gen-csharp/Thrift/Test/VersioningTestV1.cs \ - gen-csharp/Thrift/Test/VersioningTestV2.cs \ - gen-csharp/Thrift/Test/Xception.cs \ - gen-csharp/Thrift/Test/Xception2.cs \ - gen-csharp/Thrift/Test/Xtruct.cs \ - gen-csharp/Thrift/Test/Xtruct2.cs \ - gen-csharp/Thrift/Test/Xtruct3.cs - -BUILT_SOURCES = $(GENERATED) - -if MONO_MCS -CSC = mcs -else -CSC = gmcs -endif - -if NET_2_0 -CSC_DEFINES = -d:NET_2_0 -endif - -LIBDIR = $(top_builddir)/lib/csharp - -THRIFT = $(top_builddir)/compiler/cpp/thrift - -$(GENERATED): $(top_srcdir)/test/ThriftTest.thrift $(THRIFT) - $(THRIFT) --gen csharp -o . $< - -precross: TestClientServer.exe - -ThriftImpl.dll: $(GENERATED) $(LIBDIR)/Thrift.dll - $(CSC) $(CSC_DEFINES) -t:library -out:$@ -reference:$(LIBDIR)/Thrift.dll $(GENERATED) - -SRCS = TestClient.cs TestServer.cs Program.cs - -TestClientServer.exe: $(SRCS) ThriftImpl.dll - $(CSC) $(CSC_DEFINES) -out:$@ -reference:$(LIBDIR)/Thrift.dll -reference:ThriftImpl.dll $(SRCS) - -clean-local: - $(RM) -rf gen-csharp *.exe *.dll - -TESTPORT = 9500 -check-local: TestClientServer.exe - MONO_PATH=$(LIBDIR) timeout 10 mono TestClientServer.exe server --port=$(TESTPORT) & - sleep 1 - MONO_PATH=$(LIBDIR) mono TestClientServer.exe client --port=$(TESTPORT) - -EXTRA_DIST = \ - Properties/AssemblyInfo.cs \ - ThriftTest.csproj \ - ThriftTest.sln \ - Program.cs \ - TestServer.cs \ - TestClient.cs diff --git a/test/csharp/Program.cs b/test/csharp/Program.cs deleted file mode 100644 index 8ec00e300b1..00000000000 --- a/test/csharp/Program.cs +++ /dev/null @@ -1,71 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -// Distributed under the Thrift Software License -// -// See accompanying file LICENSE or visit the Thrift site at: -// http://developers.facebook.com/thrift/ - -using System; -using Thrift.Transport; -using Thrift.Protocol; -using Thrift.Test; //generated code - -namespace Test -{ - class Program - { - static int Main(string[] args) - { - if (args.Length == 0) - { - Console.WriteLine("must provide 'server' or 'client' arg"); - return -1; - } - - try - { - Console.SetBufferSize(Console.BufferWidth, 4096); - } - catch (Exception) - { - Console.WriteLine("Failed to grow scroll-back buffer"); - } - - string[] subArgs = new string[args.Length - 1]; - for(int i = 1; i < args.Length; i++) - { - subArgs[i-1] = args[i]; - } - if (args[0] == "client") - { - return TestClient.Execute(subArgs); - } - else if (args[0] == "server") - { - return TestServer.Execute(subArgs) ? 0 : 1; - } - else - { - Console.WriteLine("first argument must be 'server' or 'client'"); - } - return 0; - } - } -} diff --git a/test/csharp/Properties/AssemblyInfo.cs b/test/csharp/Properties/AssemblyInfo.cs deleted file mode 100644 index d7e04b5d7c1..00000000000 --- a/test/csharp/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -using System.Reflection; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; - -// General Information about an assembly is controlled through the following -// set of attributes. Change these attribute values to modify the information -// associated with an assembly. -[assembly: AssemblyTitle("ThriftTest")] -[assembly: AssemblyDescription("")] -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("The Apache Software Foundation")] -[assembly: AssemblyProduct("Thrift")] -[assembly: AssemblyCopyright("The Apache Software Foundation")] -[assembly: AssemblyTrademark("")] -[assembly: AssemblyCulture("")] - -// Setting ComVisible to false makes the types in this assembly not visible -// to COM components. If you need to access a type in this assembly from -// COM, set the ComVisible attribute to true on that type. -[assembly: ComVisible(false)] - -// The following GUID is for the ID of the typelib if this project is exposed to COM -[assembly: Guid("f41b193b-f1ab-48ee-8843-f88e43084e26")] - -// Version information for an assembly consists of the following four values: -// -// Major Version -// Minor Version -// Build Number -// Revision -// -// You can specify all the values or you can default the Build and Revision Numbers -// by using the '*' as shown below: -// [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("0.12.0.0")] -[assembly: AssemblyFileVersion("0.12.0.0")] diff --git a/test/csharp/TestClient.cs b/test/csharp/TestClient.cs deleted file mode 100644 index 949c06e9f95..00000000000 --- a/test/csharp/TestClient.cs +++ /dev/null @@ -1,870 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -using System; -using System.Linq; -using System.Diagnostics; -using System.Collections.Generic; -using System.Threading; -using System.Security.Cryptography.X509Certificates; -using Thrift.Collections; -using Thrift.Protocol; -using Thrift.Transport; -using Thrift.Test; -using System.Security.Authentication; - -namespace Test -{ - public class TestClient - { - public class TestParams - { - public int numIterations = 1; - public string host = "localhost"; - public int port = 9090; - public string url; - public string pipe; - public bool buffered; - public bool framed; - public string protocol; - public bool encrypted = false; - public bool multiplexed = false; - protected bool _isFirstTransport = true; - - - public TTransport CreateTransport() - { - if (url == null) - { - // endpoint transport - TTransport trans = null; - if (pipe != null) - trans = new TNamedPipeClientTransport(pipe); - else - { - if (encrypted) - { - string certPath = "../keys/client.p12"; - X509Certificate cert = new X509Certificate2(certPath, "thrift"); - trans = new TTLSSocket(host, port, 0, cert, - (o, c, chain, errors) => true, - null, SslProtocols.Tls); - } - else - { - trans = new TSocket(host, port); - } - } - - // layered transport - if (buffered) - trans = new TBufferedTransport(trans); - if (framed) - trans = new TFramedTransport(trans); - - if (_isFirstTransport) - { - //ensure proper open/close of transport - trans.Open(); - trans.Close(); - _isFirstTransport = false; - } - return trans; - } - else - { - return new THttpClient(new Uri(url)); - } - } - - public TProtocol CreateProtocol(TTransport transport) - { - if (protocol == "compact") - return new TCompactProtocol(transport); - else if (protocol == "json") - return new TJSONProtocol(transport); - else - return new TBinaryProtocol(transport); - } - }; - - private const int ErrorBaseTypes = 1; - private const int ErrorStructs = 2; - private const int ErrorContainers = 4; - private const int ErrorExceptions = 8; - private const int ErrorProtocol = 16; - private const int ErrorUnknown = 64; - - private class ClientTest - { - private readonly TestParams param; - private readonly TTransport transport; - private readonly SecondService.Client second; - private readonly ThriftTest.Client client; - private readonly int numIterations; - private bool done; - - public int ReturnCode { get; set; } - - public ClientTest(TestParams paramin) - { - param = paramin; - transport = param.CreateTransport(); - TProtocol protocol = param.CreateProtocol(transport); - if (param.multiplexed) - { - second = new SecondService.Client(new TMultiplexedProtocol(protocol, "SecondService")); - } - client = new ThriftTest.Client(protocol); - numIterations = param.numIterations; - } - public void Execute() - { - if (done) - { - Console.WriteLine("Execute called more than once"); - throw new InvalidOperationException(); - } - - for (int i = 0; i < numIterations; i++) - { - try - { - if (!transport.IsOpen) - transport.Open(); - } - catch (TTransportException ex) - { - Console.WriteLine("*** FAILED ***"); - Console.WriteLine("Connect failed: " + ex.Message); - ReturnCode |= ErrorUnknown; - Console.WriteLine(ex.Message + " ST: " + ex.StackTrace); - continue; - } - - try - { - ReturnCode |= ExecuteClientTest(client, second, param); - } - catch (Exception ex) - { - Console.WriteLine("*** FAILED ***"); - Console.WriteLine(ex.Message + " ST: " + ex.StackTrace); - ReturnCode |= ErrorUnknown; - } - } - try - { - transport.Close(); - } - catch(Exception ex) - { - Console.WriteLine("Error while closing transport"); - Console.WriteLine(ex.Message + " ST: " + ex.StackTrace); - } - done = true; - } - } - - public static int Execute(string[] args) - { - try - { - TestParams param = new TestParams(); - int numThreads = 1; - try - { - for (int i = 0; i < args.Length; i++) - { - if (args[i] == "-u") - { - param.url = args[++i]; - } - else if (args[i] == "-n") - { - param.numIterations = Convert.ToInt32(args[++i]); - } - else if (args[i] == "-pipe") // -pipe - { - param.pipe = args[++i]; - Console.WriteLine("Using named pipes transport"); - } - else if (args[i].Contains("--host=")) - { - param.host = args[i].Substring(args[i].IndexOf("=") + 1); - } - else if (args[i].Contains("--port=")) - { - param.port = int.Parse(args[i].Substring(args[i].IndexOf("=")+1)); - } - else if (args[i] == "-b" || args[i] == "--buffered" || args[i] == "--transport=buffered") - { - param.buffered = true; - Console.WriteLine("Using buffered sockets"); - } - else if (args[i] == "-f" || args[i] == "--framed" || args[i] == "--transport=framed") - { - param.framed = true; - Console.WriteLine("Using framed transport"); - } - else if (args[i] == "-t") - { - numThreads = Convert.ToInt32(args[++i]); - } - else if (args[i] == "--compact" || args[i] == "--protocol=compact" || args[i] == "--protocol=multic") - { - param.protocol = "compact"; - Console.WriteLine("Using compact protocol"); - } - else if (args[i] == "--json" || args[i] == "--protocol=json" || args[i] == "--protocol=multij") - { - param.protocol = "json"; - Console.WriteLine("Using JSON protocol"); - } - else if (args[i] == "--ssl") - { - param.encrypted = true; - Console.WriteLine("Using encrypted transport"); - } - - if (args[i].StartsWith("--protocol=multi")) - { - param.multiplexed = true; - } - } - } - catch (Exception ex) - { - Console.WriteLine("*** FAILED ***"); - Console.WriteLine("Error while parsing arguments"); - Console.WriteLine(ex.Message + " ST: " + ex.StackTrace); - return ErrorUnknown; - } - - var tests = Enumerable.Range(0, numThreads).Select(_ => new ClientTest(param)).ToArray(); - //issue tests on separate threads simultaneously - var threads = tests.Select(test => new Thread(test.Execute)).ToArray(); - DateTime start = DateTime.Now; - foreach (var t in threads) - t.Start(); - foreach (var t in threads) - t.Join(); - Console.WriteLine("Total time: " + (DateTime.Now - start)); - Console.WriteLine(); - return tests.Select(t => t.ReturnCode).Aggregate((r1, r2) => r1 | r2); - } - catch (Exception outerEx) - { - Console.WriteLine("*** FAILED ***"); - Console.WriteLine("Unexpected error"); - Console.WriteLine(outerEx.Message + " ST: " + outerEx.StackTrace); - return ErrorUnknown; - } - } - - public static string BytesToHex(byte[] data) { - return BitConverter.ToString(data).Replace("-", string.Empty); - } - - public static byte[] PrepareTestData(bool randomDist, bool huge) - { - // huge = true tests for THRIFT-4372 - byte[] retval = new byte[huge ? 0x12345 : 0x100]; - int initLen = retval.Length; - - // linear distribution, unless random is requested - if (!randomDist) { - for (var i = 0; i < initLen; ++i) { - retval[i] = (byte)i; - } - return retval; - } - - // random distribution - for (var i = 0; i < initLen; ++i) { - retval[i] = (byte)0; - } - var rnd = new Random(); - for (var i = 1; i < initLen; ++i) { - while( true) { - int nextPos = rnd.Next() % initLen; - if (retval[nextPos] == 0) { - retval[nextPos] = (byte)i; - break; - } - } - } - return retval; - } - - public static int ExecuteClientTest(ThriftTest.Client client, SecondService.Client second, TestParams param) - { - int returnCode = 0; - - Console.Write("testVoid()"); - client.testVoid(); - Console.WriteLine(" = void"); - - Console.Write("testString(\"Test\")"); - string s = client.testString("Test"); - Console.WriteLine(" = \"" + s + "\""); - if ("Test" != s) - { - Console.WriteLine("*** FAILED ***"); - returnCode |= ErrorBaseTypes; - } - - if (param.multiplexed) - { - Console.WriteLine("secondTestString(\"Test2\")"); - s = second.secondtestString("Test2"); - Console.WriteLine(" = \"" + s + "\""); - if ("testString(\"Test2\")" != s) - { - Console.WriteLine("*** FAILED ***"); - returnCode |= ErrorProtocol; - } - } - - Console.Write("testBool(true)"); - bool t = client.testBool((bool)true); - Console.WriteLine(" = " + t); - if (!t) - { - Console.WriteLine("*** FAILED ***"); - returnCode |= ErrorBaseTypes; - } - Console.Write("testBool(false)"); - bool f = client.testBool((bool)false); - Console.WriteLine(" = " + f); - if (f) - { - Console.WriteLine("*** FAILED ***"); - returnCode |= ErrorBaseTypes; - } - - Console.Write("testByte(1)"); - sbyte i8 = client.testByte((sbyte)1); - Console.WriteLine(" = " + i8); - if (1 != i8) - { - Console.WriteLine("*** FAILED ***"); - returnCode |= ErrorBaseTypes; - } - - Console.Write("testI32(-1)"); - int i32 = client.testI32(-1); - Console.WriteLine(" = " + i32); - if (-1 != i32) - { - Console.WriteLine("*** FAILED ***"); - returnCode |= ErrorBaseTypes; - } - - Console.Write("testI64(-34359738368)"); - long i64 = client.testI64(-34359738368); - Console.WriteLine(" = " + i64); - if (-34359738368 != i64) - { - Console.WriteLine("*** FAILED ***"); - returnCode |= ErrorBaseTypes; - } - - // TODO: Validate received message - Console.Write("testDouble(5.325098235)"); - double dub = client.testDouble(5.325098235); - Console.WriteLine(" = " + dub); - if (5.325098235 != dub) - { - Console.WriteLine("*** FAILED ***"); - returnCode |= ErrorBaseTypes; - } - Console.Write("testDouble(-0.000341012439638598279)"); - dub = client.testDouble(-0.000341012439638598279); - Console.WriteLine(" = " + dub); - if (-0.000341012439638598279 != dub) - { - Console.WriteLine("*** FAILED ***"); - returnCode |= ErrorBaseTypes; - } - - for (i32 = 0; i32 < 2; ++i32) - { - var huge = (i32 > 0); - byte[] binOut = PrepareTestData(false,huge); - Console.Write("testBinary(" + BytesToHex(binOut) + ")"); - try - { - byte[] binIn = client.testBinary(binOut); - Console.WriteLine(" = " + BytesToHex(binIn)); - if (binIn.Length != binOut.Length) - { - Console.WriteLine("*** FAILED ***"); - returnCode |= ErrorBaseTypes; - } - for (int ofs = 0; ofs < Math.Min(binIn.Length, binOut.Length); ++ofs) - if (binIn[ofs] != binOut[ofs]) - { - Console.WriteLine("*** FAILED ***"); - returnCode |= ErrorBaseTypes; - } - } - catch (Thrift.TApplicationException ex) - { - Console.WriteLine("*** FAILED ***"); - returnCode |= ErrorBaseTypes; - Console.WriteLine(ex.Message + " ST: " + ex.StackTrace); - } - } - - // binary equals? only with hashcode option enabled ... - Console.WriteLine("Test CrazyNesting"); - if( typeof(CrazyNesting).GetMethod("Equals").DeclaringType == typeof(CrazyNesting)) - { - CrazyNesting one = new CrazyNesting(); - CrazyNesting two = new CrazyNesting(); - one.String_field = "crazy"; - two.String_field = "crazy"; - one.Binary_field = new byte[10] { 0x00, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0xFF }; - two.Binary_field = new byte[10] { 0x00, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0xFF }; - if (!one.Equals(two)) - { - Console.WriteLine("*** FAILED ***"); - returnCode |= ErrorContainers; - throw new Exception("CrazyNesting.Equals failed"); - } - } - - // TODO: Validate received message - Console.Write("testStruct({\"Zero\", 1, -3, -5})"); - Xtruct o = new Xtruct(); - o.String_thing = "Zero"; - o.Byte_thing = (sbyte)1; - o.I32_thing = -3; - o.I64_thing = -5; - Xtruct i = client.testStruct(o); - Console.WriteLine(" = {\"" + i.String_thing + "\", " + i.Byte_thing + ", " + i.I32_thing + ", " + i.I64_thing + "}"); - - // TODO: Validate received message - Console.Write("testNest({1, {\"Zero\", 1, -3, -5}, 5})"); - Xtruct2 o2 = new Xtruct2(); - o2.Byte_thing = (sbyte)1; - o2.Struct_thing = o; - o2.I32_thing = 5; - Xtruct2 i2 = client.testNest(o2); - i = i2.Struct_thing; - Console.WriteLine(" = {" + i2.Byte_thing + ", {\"" + i.String_thing + "\", " + i.Byte_thing + ", " + i.I32_thing + ", " + i.I64_thing + "}, " + i2.I32_thing + "}"); - - Dictionary mapout = new Dictionary(); - for (int j = 0; j < 5; j++) - { - mapout[j] = j - 10; - } - Console.Write("testMap({"); - bool first = true; - foreach (int key in mapout.Keys) - { - if (first) - { - first = false; - } - else - { - Console.Write(", "); - } - Console.Write(key + " => " + mapout[key]); - } - Console.Write("})"); - - Dictionary mapin = client.testMap(mapout); - - Console.Write(" = {"); - first = true; - foreach (int key in mapin.Keys) - { - if (first) - { - first = false; - } - else - { - Console.Write(", "); - } - Console.Write(key + " => " + mapin[key]); - } - Console.WriteLine("}"); - - // TODO: Validate received message - List listout = new List(); - for (int j = -2; j < 3; j++) - { - listout.Add(j); - } - Console.Write("testList({"); - first = true; - foreach (int j in listout) - { - if (first) - { - first = false; - } - else - { - Console.Write(", "); - } - Console.Write(j); - } - Console.Write("})"); - - List listin = client.testList(listout); - - Console.Write(" = {"); - first = true; - foreach (int j in listin) - { - if (first) - { - first = false; - } - else - { - Console.Write(", "); - } - Console.Write(j); - } - Console.WriteLine("}"); - - //set - // TODO: Validate received message - THashSet setout = new THashSet(); - for (int j = -2; j < 3; j++) - { - setout.Add(j); - } - Console.Write("testSet({"); - first = true; - foreach (int j in setout) - { - if (first) - { - first = false; - } - else - { - Console.Write(", "); - } - Console.Write(j); - } - Console.Write("})"); - - THashSet setin = client.testSet(setout); - - Console.Write(" = {"); - first = true; - foreach (int j in setin) - { - if (first) - { - first = false; - } - else - { - Console.Write(", "); - } - Console.Write(j); - } - Console.WriteLine("}"); - - - Console.Write("testEnum(ONE)"); - Numberz ret = client.testEnum(Numberz.ONE); - Console.WriteLine(" = " + ret); - if (Numberz.ONE != ret) - { - Console.WriteLine("*** FAILED ***"); - returnCode |= ErrorStructs; - } - - Console.Write("testEnum(TWO)"); - ret = client.testEnum(Numberz.TWO); - Console.WriteLine(" = " + ret); - if (Numberz.TWO != ret) - { - Console.WriteLine("*** FAILED ***"); - returnCode |= ErrorStructs; - } - - Console.Write("testEnum(THREE)"); - ret = client.testEnum(Numberz.THREE); - Console.WriteLine(" = " + ret); - if (Numberz.THREE != ret) - { - Console.WriteLine("*** FAILED ***"); - returnCode |= ErrorStructs; - } - - Console.Write("testEnum(FIVE)"); - ret = client.testEnum(Numberz.FIVE); - Console.WriteLine(" = " + ret); - if (Numberz.FIVE != ret) - { - Console.WriteLine("*** FAILED ***"); - returnCode |= ErrorStructs; - } - - Console.Write("testEnum(EIGHT)"); - ret = client.testEnum(Numberz.EIGHT); - Console.WriteLine(" = " + ret); - if (Numberz.EIGHT != ret) - { - Console.WriteLine("*** FAILED ***"); - returnCode |= ErrorStructs; - } - - Console.Write("testTypedef(309858235082523)"); - long uid = client.testTypedef(309858235082523L); - Console.WriteLine(" = " + uid); - if (309858235082523L != uid) - { - Console.WriteLine("*** FAILED ***"); - returnCode |= ErrorStructs; - } - - // TODO: Validate received message - Console.Write("testMapMap(1)"); - Dictionary> mm = client.testMapMap(1); - Console.Write(" = {"); - foreach (int key in mm.Keys) - { - Console.Write(key + " => {"); - Dictionary m2 = mm[key]; - foreach (int k2 in m2.Keys) - { - Console.Write(k2 + " => " + m2[k2] + ", "); - } - Console.Write("}, "); - } - Console.WriteLine("}"); - - // TODO: Validate received message - Insanity insane = new Insanity(); - insane.UserMap = new Dictionary(); - insane.UserMap[Numberz.FIVE] = 5000L; - Xtruct truck = new Xtruct(); - truck.String_thing = "Truck"; - truck.Byte_thing = (sbyte)8; - truck.I32_thing = 8; - truck.I64_thing = 8; - insane.Xtructs = new List(); - insane.Xtructs.Add(truck); - Console.Write("testInsanity()"); - Dictionary> whoa = client.testInsanity(insane); - Console.Write(" = {"); - foreach (long key in whoa.Keys) - { - Dictionary val = whoa[key]; - Console.Write(key + " => {"); - - foreach (Numberz k2 in val.Keys) - { - Insanity v2 = val[k2]; - - Console.Write(k2 + " => {"); - Dictionary userMap = v2.UserMap; - - Console.Write("{"); - if (userMap != null) - { - foreach (Numberz k3 in userMap.Keys) - { - Console.Write(k3 + " => " + userMap[k3] + ", "); - } - } - else - { - Console.Write("null"); - } - Console.Write("}, "); - - List xtructs = v2.Xtructs; - - Console.Write("{"); - if (xtructs != null) - { - foreach (Xtruct x in xtructs) - { - Console.Write("{\"" + x.String_thing + "\", " + x.Byte_thing + ", " + x.I32_thing + ", " + x.I32_thing + "}, "); - } - } - else - { - Console.Write("null"); - } - Console.Write("}"); - - Console.Write("}, "); - } - Console.Write("}, "); - } - Console.WriteLine("}"); - - sbyte arg0 = 1; - int arg1 = 2; - long arg2 = long.MaxValue; - Dictionary multiDict = new Dictionary(); - multiDict[1] = "one"; - Numberz arg4 = Numberz.FIVE; - long arg5 = 5000000; - Console.Write("Test Multi(" + arg0 + "," + arg1 + "," + arg2 + "," + multiDict + "," + arg4 + "," + arg5 + ")"); - Xtruct multiResponse = client.testMulti(arg0, arg1, arg2, multiDict, arg4, arg5); - Console.Write(" = Xtruct(byte_thing:" + multiResponse.Byte_thing + ",String_thing:" + multiResponse.String_thing - + ",i32_thing:" + multiResponse.I32_thing + ",i64_thing:" + multiResponse.I64_thing + ")\n"); - - try - { - Console.WriteLine("testException(\"Xception\")"); - client.testException("Xception"); - Console.WriteLine("*** FAILED ***"); - returnCode |= ErrorExceptions; - } - catch (Xception ex) - { - if (ex.ErrorCode != 1001 || ex.Message != "Xception") - { - Console.WriteLine("*** FAILED ***"); - returnCode |= ErrorExceptions; - } - } - catch (Exception ex) - { - Console.WriteLine("*** FAILED ***"); - returnCode |= ErrorExceptions; - Console.WriteLine(ex.Message + " ST: " + ex.StackTrace); - } - try - { - Console.WriteLine("testException(\"TException\")"); - client.testException("TException"); - Console.WriteLine("*** FAILED ***"); - returnCode |= ErrorExceptions; - } - catch (Thrift.TException) - { - // OK - } - catch (Exception ex) - { - Console.WriteLine("*** FAILED ***"); - returnCode |= ErrorExceptions; - Console.WriteLine(ex.Message + " ST: " + ex.StackTrace); - } - try - { - Console.WriteLine("testException(\"ok\")"); - client.testException("ok"); - // OK - } - catch (Exception ex) - { - Console.WriteLine("*** FAILED ***"); - returnCode |= ErrorExceptions; - Console.WriteLine(ex.Message + " ST: " + ex.StackTrace); - } - - try - { - Console.WriteLine("testMultiException(\"Xception\", ...)"); - client.testMultiException("Xception", "ignore"); - Console.WriteLine("*** FAILED ***"); - returnCode |= ErrorExceptions; - } - catch (Xception ex) - { - if (ex.ErrorCode != 1001 || ex.Message != "This is an Xception") - { - Console.WriteLine("*** FAILED ***"); - returnCode |= ErrorExceptions; - } - } - catch (Exception ex) - { - Console.WriteLine("*** FAILED ***"); - returnCode |= ErrorExceptions; - Console.WriteLine(ex.Message + " ST: " + ex.StackTrace); - } - try - { - Console.WriteLine("testMultiException(\"Xception2\", ...)"); - client.testMultiException("Xception2", "ignore"); - Console.WriteLine("*** FAILED ***"); - returnCode |= ErrorExceptions; - } - catch (Xception2 ex) - { - if (ex.ErrorCode != 2002 || ex.Struct_thing.String_thing != "This is an Xception2") - { - Console.WriteLine("*** FAILED ***"); - returnCode |= ErrorExceptions; - } - } - catch (Exception ex) - { - Console.WriteLine("*** FAILED ***"); - returnCode |= ErrorExceptions; - Console.WriteLine(ex.Message + " ST: " + ex.StackTrace); - } - try - { - Console.WriteLine("testMultiException(\"success\", \"OK\")"); - if ("OK" != client.testMultiException("success", "OK").String_thing) - { - Console.WriteLine("*** FAILED ***"); - returnCode |= ErrorExceptions; - } - } - catch (Exception ex) - { - Console.WriteLine("*** FAILED ***"); - returnCode |= ErrorExceptions; - Console.WriteLine(ex.Message + " ST: " + ex.StackTrace); - } - - Stopwatch sw = new Stopwatch(); - sw.Start(); - Console.WriteLine("Test Oneway(1)"); - client.testOneway(1); - sw.Stop(); - if (sw.ElapsedMilliseconds > 1000) - { - Console.WriteLine("*** FAILED ***"); - returnCode |= ErrorBaseTypes; - } - - Console.Write("Test Calltime()"); - var times = 50; - sw.Reset(); - sw.Start(); - for (int k = 0; k < times; ++k) - client.testVoid(); - sw.Stop(); - Console.WriteLine(" = {0} ms a testVoid() call", sw.ElapsedMilliseconds / times); - return returnCode; - } - } -} diff --git a/test/csharp/TestServer.cs b/test/csharp/TestServer.cs deleted file mode 100644 index bf645c26cf7..00000000000 --- a/test/csharp/TestServer.cs +++ /dev/null @@ -1,535 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -// Distributed under the Thrift Software License -// -// See accompanying file LICENSE or visit the Thrift site at: -// http://developers.facebook.com/thrift/ -using System; -using System.Collections.Generic; -using System.Security.Cryptography.X509Certificates; -using Thrift.Collections; -using Thrift.Test; //generated code -using Thrift.Transport; -using Thrift.Protocol; -using Thrift.Server; -using Thrift; -using System.Threading; -using System.Text; -using System.Security.Authentication; - -namespace Test -{ - public class TestServer - { - public static int _clientID = -1; - public delegate void TestLogDelegate(string msg, params object[] values); - - public class TradeServerEventHandler : TServerEventHandler - { - public int callCount = 0; - public void preServe() - { - callCount++; - } - public Object createContext(Thrift.Protocol.TProtocol input, Thrift.Protocol.TProtocol output) - { - callCount++; - return null; - } - public void deleteContext(Object serverContext, Thrift.Protocol.TProtocol input, Thrift.Protocol.TProtocol output) - { - callCount++; - } - public void processContext(Object serverContext, Thrift.Transport.TTransport transport) - { - callCount++; - } - }; - - - public class TestHandler : ThriftTest.Iface, Thrift.TControllingHandler - { - public TServer server { get; set; } - private int handlerID; - private StringBuilder reusableStringBuilder = new StringBuilder(); - private TestLogDelegate testLogDelegate; - - public TestHandler() - { - handlerID = Interlocked.Increment(ref _clientID); - testLogDelegate += testConsoleLogger; - testLogDelegate.Invoke("New TestHandler instance created"); - } - - public void testConsoleLogger(string msg, params object[] values) - { - reusableStringBuilder.Clear(); - reusableStringBuilder.AppendFormat("handler{0:D3}:",handlerID); - reusableStringBuilder.AppendFormat(msg, values); - reusableStringBuilder.AppendLine(); - Console.Write( reusableStringBuilder.ToString() ); - } - - public void testVoid() - { - testLogDelegate.Invoke("testVoid()"); - } - - public string testString(string thing) - { - testLogDelegate.Invoke("testString({0})", thing); - return thing; - } - - public bool testBool(bool thing) - { - testLogDelegate.Invoke("testBool({0})", thing); - return thing; - } - - public sbyte testByte(sbyte thing) - { - testLogDelegate.Invoke("testByte({0})", thing); - return thing; - } - - public int testI32(int thing) - { - testLogDelegate.Invoke("testI32({0})", thing); - return thing; - } - - public long testI64(long thing) - { - testLogDelegate.Invoke("testI64({0})", thing); - return thing; - } - - public double testDouble(double thing) - { - testLogDelegate.Invoke("testDouble({0})", thing); - return thing; - } - - public byte[] testBinary(byte[] thing) - { - string hex = BitConverter.ToString(thing).Replace("-", string.Empty); - testLogDelegate.Invoke("testBinary({0:X})", hex); - return thing; - } - - public Xtruct testStruct(Xtruct thing) - { - testLogDelegate.Invoke("testStruct({{\"{0}\", {1}, {2}, {3}}})", thing.String_thing, thing.Byte_thing, thing.I32_thing, thing.I64_thing); - return thing; - } - - public Xtruct2 testNest(Xtruct2 nest) - { - Xtruct thing = nest.Struct_thing; - testLogDelegate.Invoke("testNest({{{0}, {{\"{1}\", {2}, {3}, {4}, {5}}}}})", - nest.Byte_thing, - thing.String_thing, - thing.Byte_thing, - thing.I32_thing, - thing.I64_thing, - nest.I32_thing); - return nest; - } - - public Dictionary testMap(Dictionary thing) - { - reusableStringBuilder.Clear(); - reusableStringBuilder.Append("testMap({{"); - bool first = true; - foreach (int key in thing.Keys) - { - if (first) - { - first = false; - } - else - { - reusableStringBuilder.Append(", "); - } - reusableStringBuilder.AppendFormat("{0} => {1}", key, thing[key]); - } - reusableStringBuilder.Append("}})"); - testLogDelegate.Invoke(reusableStringBuilder.ToString()); - return thing; - } - - public Dictionary testStringMap(Dictionary thing) - { - reusableStringBuilder.Clear(); - reusableStringBuilder.Append("testStringMap({{"); - bool first = true; - foreach (string key in thing.Keys) - { - if (first) - { - first = false; - } - else - { - reusableStringBuilder.Append(", "); - } - reusableStringBuilder.AppendFormat("{0} => {1}", key, thing[key]); - } - reusableStringBuilder.Append("}})"); - testLogDelegate.Invoke(reusableStringBuilder.ToString()); - return thing; - } - - public THashSet testSet(THashSet thing) - { - reusableStringBuilder.Clear(); - reusableStringBuilder.Append("testSet({{"); - bool first = true; - foreach (int elem in thing) - { - if (first) - { - first = false; - } - else - { - reusableStringBuilder.Append(", "); - } - reusableStringBuilder.AppendFormat("{0}", elem); - } - reusableStringBuilder.Append("}})"); - testLogDelegate.Invoke(reusableStringBuilder.ToString()); - return thing; - } - - public List testList(List thing) - { - reusableStringBuilder.Clear(); - reusableStringBuilder.Append("testList({{"); - bool first = true; - foreach (int elem in thing) - { - if (first) - { - first = false; - } - else - { - reusableStringBuilder.Append(", "); - } - reusableStringBuilder.AppendFormat("{0}", elem); - } - reusableStringBuilder.Append("}})"); - testLogDelegate.Invoke(reusableStringBuilder.ToString()); - return thing; - } - - public Numberz testEnum(Numberz thing) - { - testLogDelegate.Invoke("testEnum({0})", thing); - return thing; - } - - public long testTypedef(long thing) - { - testLogDelegate.Invoke("testTypedef({0})", thing); - return thing; - } - - public Dictionary> testMapMap(int hello) - { - testLogDelegate.Invoke("testMapMap({0})", hello); - Dictionary> mapmap = - new Dictionary>(); - - Dictionary pos = new Dictionary(); - Dictionary neg = new Dictionary(); - for (int i = 1; i < 5; i++) - { - pos[i] = i; - neg[-i] = -i; - } - - mapmap[4] = pos; - mapmap[-4] = neg; - - return mapmap; - } - - // Insanity - // returns: - // { 1 => { 2 => argument, - // 3 => argument, - // }, - // 2 => { 6 => , }, - // } - public Dictionary> testInsanity(Insanity argument) - { - testLogDelegate.Invoke("testInsanity()"); - - Dictionary first_map = new Dictionary(); - Dictionary second_map = new Dictionary(); ; - - first_map[Numberz.TWO] = argument; - first_map[Numberz.THREE] = argument; - - second_map[Numberz.SIX] = new Insanity(); - - Dictionary> insane = - new Dictionary>(); - insane[(long)1] = first_map; - insane[(long)2] = second_map; - - return insane; - } - - public Xtruct testMulti(sbyte arg0, int arg1, long arg2, Dictionary arg3, Numberz arg4, long arg5) - { - testLogDelegate.Invoke("testMulti()"); - - Xtruct hello = new Xtruct(); ; - hello.String_thing = "Hello2"; - hello.Byte_thing = arg0; - hello.I32_thing = arg1; - hello.I64_thing = arg2; - return hello; - } - - /** - * Print 'testException(%s)' with arg as '%s' - * @param string arg - a string indication what type of exception to throw - * if arg == "Xception" throw Xception with errorCode = 1001 and message = arg - * elsen if arg == "TException" throw TException - * else do not throw anything - */ - public void testException(string arg) - { - testLogDelegate.Invoke("testException({0})", arg); - if (arg == "Xception") - { - Xception x = new Xception(); - x.ErrorCode = 1001; - x.Message = arg; - throw x; - } - if (arg == "TException") - { - throw new Thrift.TException(); - } - return; - } - - public Xtruct testMultiException(string arg0, string arg1) - { - testLogDelegate.Invoke("testMultiException({0}, {1})", arg0,arg1); - if (arg0 == "Xception") - { - Xception x = new Xception(); - x.ErrorCode = 1001; - x.Message = "This is an Xception"; - throw x; - } - else if (arg0 == "Xception2") - { - Xception2 x = new Xception2(); - x.ErrorCode = 2002; - x.Struct_thing = new Xtruct(); - x.Struct_thing.String_thing = "This is an Xception2"; - throw x; - } - - Xtruct result = new Xtruct(); - result.String_thing = arg1; - return result; - } - - public void testStop() - { - if (server != null) - { - server.Stop(); - } - } - - public void testOneway(int arg) - { - testLogDelegate.Invoke("testOneway({0}), sleeping...", arg); - System.Threading.Thread.Sleep(arg * 1000); - testLogDelegate.Invoke("testOneway finished"); - } - - } // class TestHandler - - private enum ServerType - { - TSimpleServer, - TThreadedServer, - TThreadPoolServer, - } - - private enum ProcessorFactoryType - { - TSingletonProcessorFactory, - TPrototypeProcessorFactory, - } - - public static bool Execute(string[] args) - { - try - { - bool useBufferedSockets = false, useFramed = false, useEncryption = false, compact = false, json = false; - ServerType serverType = ServerType.TSimpleServer; - ProcessorFactoryType processorFactoryType = ProcessorFactoryType.TSingletonProcessorFactory; - int port = 9090; - string pipe = null; - for (int i = 0; i < args.Length; i++) - { - if (args[i] == "-pipe") // -pipe name - { - pipe = args[++i]; - } - else if (args[i].Contains("--port=")) - { - port = int.Parse(args[i].Substring(args[i].IndexOf("=") + 1)); - } - else if (args[i] == "-b" || args[i] == "--buffered" || args[i] == "--transport=buffered") - { - useBufferedSockets = true; - } - else if (args[i] == "-f" || args[i] == "--framed" || args[i] == "--transport=framed") - { - useFramed = true; - } - else if (args[i] == "--compact" || args[i] == "--protocol=compact") - { - compact = true; - } - else if (args[i] == "--json" || args[i] == "--protocol=json") - { - json = true; - } - else if (args[i] == "--threaded" || args[i] == "--server-type=threaded") - { - serverType = ServerType.TThreadedServer; - } - else if (args[i] == "--threadpool" || args[i] == "--server-type=threadpool") - { - serverType = ServerType.TThreadPoolServer; - } - else if (args[i] == "--prototype" || args[i] == "--processor=prototype") - { - processorFactoryType = ProcessorFactoryType.TPrototypeProcessorFactory; - } - else if (args[i] == "--ssl") - { - useEncryption = true; - } - } - - // Transport - TServerTransport trans; - if (pipe != null) - { - trans = new TNamedPipeServerTransport(pipe); - } - else - { - if (useEncryption) - { - string certPath = "../keys/server.p12"; - trans = new TTLSServerSocket(port, 0, useBufferedSockets, new X509Certificate2(certPath, "thrift"), - null, - null, SslProtocols.Tls); - } - else - { - trans = new TServerSocket(port, 0, useBufferedSockets); - } - } - - TProtocolFactory proto; - if (compact) - proto = new TCompactProtocol.Factory(); - else if (json) - proto = new TJSONProtocol.Factory(); - else - proto = new TBinaryProtocol.Factory(); - - TProcessorFactory processorFactory; - if (processorFactoryType == ProcessorFactoryType.TPrototypeProcessorFactory) - { - processorFactory = new TPrototypeProcessorFactory(); - } - else - { - // Processor - TestHandler testHandler = new TestHandler(); - ThriftTest.Processor testProcessor = new ThriftTest.Processor(testHandler); - processorFactory = new TSingletonProcessorFactory(testProcessor); - } - - TTransportFactory transFactory; - if (useFramed) - transFactory = new TFramedTransport.Factory(); - else - transFactory = new TTransportFactory(); - - TServer serverEngine; - switch (serverType) - { - case ServerType.TThreadPoolServer: - serverEngine = new TThreadPoolServer(processorFactory, trans, transFactory, proto); - break; - case ServerType.TThreadedServer: - serverEngine = new TThreadedServer(processorFactory, trans, transFactory, proto); - break; - default: - serverEngine = new TSimpleServer(processorFactory, trans, transFactory, proto); - break; - } - - //Server event handler - TradeServerEventHandler serverEvents = new TradeServerEventHandler(); - serverEngine.setEventHandler(serverEvents); - - // Run it - string where = (pipe != null ? "on pipe " + pipe : "on port " + port); - Console.WriteLine("Starting the " + serverType.ToString() + " " + where + - (processorFactoryType == ProcessorFactoryType.TPrototypeProcessorFactory ? " with processor prototype factory " : "") + - (useBufferedSockets ? " with buffered socket" : "") + - (useFramed ? " with framed transport" : "") + - (useEncryption ? " with encryption" : "") + - (compact ? " with compact protocol" : "") + - (json ? " with json protocol" : "") + - "..."); - serverEngine.Serve(); - - } - catch (Exception x) - { - Console.Error.Write(x); - return false; - } - Console.WriteLine("done."); - return true; - } - } -} diff --git a/test/csharp/ThriftTest.csproj b/test/csharp/ThriftTest.csproj deleted file mode 100644 index 363627fdfe2..00000000000 --- a/test/csharp/ThriftTest.csproj +++ /dev/null @@ -1,141 +0,0 @@ - - - - - Debug - AnyCPU - 9.0.21022 - 2.0 - {48DD757F-CA95-4DD7-BDA4-58DB6F108C2C} - Exe - Properties - ThriftTest - ThriftTest - v3.5 - 512 - false - - - 3.5 - - publish\ - true - Disk - false - Foreground - 7 - Days - false - false - true - 0 - 0.12.0.%2a - false - true - - - true - full - false - bin\Debug\ - DEBUG;TRACE - prompt - 4 - AllRules.ruleset - - - pdbonly - true - bin\Release\ - TRACE - prompt - 4 - AllRules.ruleset - - - - - False - .\ThriftImpl.dll - - - - - - - - - - - False - .NET Framework 3.5 SP1 Client Profile - false - - - False - .NET Framework 2.0 %28x86%29 - false - - - False - .NET Framework 3.0 %28x86%29 - false - - - False - .NET Framework 3.5 - true - - - False - .NET Framework 3.5 SP1 - false - - - False - Windows Installer 3.1 - true - - - - - {499EB63C-D74C-47E8-AE48-A2FC94538E9D} - Thrift - - - - - - rmdir /s /q "$(ProjectDir)gen-csharp" -del /f /q "$(ProjectDir)ThriftImpl.dll" -SET OUTPUT_DIR=$(ProjectDir) -SET THRIFT_FILE=$(ProjectDir)\..\ThriftTest.thrift -for %25%25I in ("%25OUTPUT_DIR%25") do set SHORT_DIR=%25%25~fsI -for %25%25I in ("%25THRIFT_FILE%25") do set THRIFT_SHORT=%25%25~fsI -"$(ProjectDir)\..\..\compiler\cpp\thrift.exe" --gen csharp -o %25SHORT_DIR%25 %25THRIFT_SHORT%25 -$(MSBuildToolsPath)\Csc.exe /t:library /out:"$(ProjectDir)ThriftImpl.dll" /recurse:"$(ProjectDir)gen-csharp"\* /reference:"$(ProjectDir)..\..\lib\csharp\bin\Debug\Thrift.dll" - - diff --git a/test/csharp/ThriftTest.sln b/test/csharp/ThriftTest.sln deleted file mode 100644 index 1765a03ad47..00000000000 --- a/test/csharp/ThriftTest.sln +++ /dev/null @@ -1,17 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 11.00 -# Visual Studio 2010 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ThriftTest", "ThriftTest.csproj", "{48DD757F-CA95-4DD7-BDA4-58DB6F108C2C}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Release|Any CPU = Release|Any CPU - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {48DD757F-CA95-4DD7-BDA4-58DB6F108C2C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {48DD757F-CA95-4DD7-BDA4-58DB6F108C2C}.Debug|Any CPU.Build.0 = Debug|Any CPU - {48DD757F-CA95-4DD7-BDA4-58DB6F108C2C}.Release|Any CPU.ActiveCfg = Release|Any CPU - {48DD757F-CA95-4DD7-BDA4-58DB6F108C2C}.Release|Any CPU.Build.0 = Release|Any CPU - EndGlobalSection -EndGlobal diff --git a/test/dart/Makefile.am b/test/dart/Makefile.am index 9750ec236d2..27fdc099f43 100644 --- a/test/dart/Makefile.am +++ b/test/dart/Makefile.am @@ -33,10 +33,20 @@ precross: stubs check: stubs clean-local: - $(RM) -r gen-dart test_client/.pub + $(RM) -r gen-dart/ test_client/.pub + find . -type d -name ".dart_tool" | xargs $(RM) -r find . -type d -name "packages" | xargs $(RM) -r find . -type f -name ".packages" | xargs $(RM) find . -type f -name "pubspec.lock" | xargs $(RM) +dist-hook: + $(RM) -r $(distdir)/gen-dart/ $(distdir)/test_client/.pub + find $(distdir) -type d -name ".dart_tool" | xargs $(RM) -r + find $(distdir) -type d -name "packages" | xargs $(RM) -r + find $(distdir) -type f -name ".packages" | xargs $(RM) + client: stubs ${DART} test_client/bin/main.dart + +EXTRA_DIST = \ + test_client diff --git a/test/dart/test_client/bin/main.dart b/test/dart/test_client/bin/main.dart index 996844b5bf4..feba6129930 100644 --- a/test/dart/test_client/bin/main.dart +++ b/test/dart/test_client/bin/main.dart @@ -120,7 +120,7 @@ ArgResults _parseArgs(List args) { 'compact': 'TCompactProtocol', 'json': 'TJsonProtocol' }); - parser.addFlag('verbose', defaultsTo: false); + parser.addFlag('verbose', defaultsTo: true); ArgResults results; try { diff --git a/test/dart/test_client/pubspec.yaml b/test/dart/test_client/pubspec.yaml index 63428b77982..648196214df 100644 --- a/test/dart/test_client/pubspec.yaml +++ b/test/dart/test_client/pubspec.yaml @@ -16,16 +16,16 @@ # under the License. name: thrift_test_client -version: 0.12.0 +version: 0.14.0 description: A client integration test for the Dart Thrift library author: Apache Thrift Developers homepage: http://thrift.apache.org environment: - sdk: ">=1.13.0 <2.0.0" + sdk: ">=1.24.3 <3.0.0" dependencies: - args: ^0.13.0 + args: ">=0.13.0 <2.0.0" http: ^0.11.0 thrift: path: ../../../lib/dart @@ -33,4 +33,4 @@ dependencies: path: ../gen-dart/thrift_test dev_dependencies: - test: "^0.12.0" + test: ">=0.12.30 <2.0.0" diff --git a/test/erl/Makefile.am b/test/erl/Makefile.am index ff25e89f9c0..81913eefd76 100644 --- a/test/erl/Makefile.am +++ b/test/erl/Makefile.am @@ -35,7 +35,17 @@ endif precross: .generated $(REBAR) compile +maintainer-clean-local: + $(RM) -r ebin/ + clean: - rm -f .generated - rm -rf src/gen-erl $(REBAR) clean + $(RM) .generated + $(RM) -r .rebar/ + $(RM) -r src/gen-erl/ + +dist-hook: + $(RM) $(distdir)/.generated + $(RM) -r $(distdir)/.rebar/ + $(RM) -r $(distdir)/ebin/ + $(RM) -r $(distdir)/src/gen-erl/ diff --git a/test/erl/src/thrift_test.app.src b/test/erl/src/thrift_test.app.src index f166cc48a47..b6242b3eef2 100644 --- a/test/erl/src/thrift_test.app.src +++ b/test/erl/src/thrift_test.app.src @@ -22,7 +22,7 @@ {description, "Thrift cross language test"}, % The version of the applicaton - {vsn, "0.12.0"}, + {vsn, "0.14.0"}, % All modules used by the application. {modules, [ diff --git a/test/features/known_failures_Linux.json b/test/features/known_failures_Linux.json index 83769682f5f..06fa959e5e5 100644 --- a/test/features/known_failures_Linux.json +++ b/test/features/known_failures_Linux.json @@ -7,10 +7,11 @@ "cpp-theader_framed_compact_multih-header_buffered-ip", "cpp-theader_unframed_binary_multih-header_buffered-ip", "cpp-theader_unframed_compact_multih-header_buffered-ip", - "csharp-limit_container_length_binary_buffered-ip", - "csharp-limit_container_length_compact_buffered-ip", - "csharp-limit_string_length_binary_buffered-ip", - "csharp-limit_string_length_compact_buffered-ip", + "netstd-limit_container_length_binary_buffered-ip", + "netstd-limit_container_length_compact_buffered-ip", + "netstd-limit_string_length_binary_buffered-ip", + "netstd-limit_string_length_compact_buffered-ip", + "netstd-tls_binary_buffered-ip-ssl", "d-limit_container_length_binary_buffered-ip", "d-limit_container_length_compact_buffered-ip", "d-limit_string_length_binary_buffered-ip", @@ -45,6 +46,8 @@ "rs-limit_string_length_binary_buffered-ip", "rs-limit_string_length_compact_buffered-ip", "rs-limit_string_length_multic-compact_buffered-ip", - "netcore-limit_string_length_compact_buffered-ip", - "netcore-limit_container_length_compact_buffered-ip" + "netstd-limit_string_length_compact_buffered-ip", + "netstd-limit_container_length_compact_buffered-ip", + "nodejs-theader_framed_binary_header_buffered-ip", + "nodejs-theader_framed_compact_header_buffered-ip" ] diff --git a/test/features/tls.sh b/test/features/tls.sh index dada6ab95d6..6fd90a5fcde 100755 --- a/test/features/tls.sh +++ b/test/features/tls.sh @@ -32,6 +32,7 @@ declare -A EXPECT_NEGOTIATE EXPECT_NEGOTIATE[tls1]=1 EXPECT_NEGOTIATE[tls1_1]=1 EXPECT_NEGOTIATE[tls1_2]=1 +EXPECT_NEGOTIATE[tls1_3]=1 failures=0 @@ -62,10 +63,10 @@ function tls tls -if [[ $failures -eq 3 ]]; then - echo "[fail] At least one of TLSv1.0, TLSv1.1, or TLSv1.2 needs to work, but does not" +if [[ $failures -eq 4 ]]; then + echo "[fail] At least one of TLSv1.0, TLSv1.1, TLSv1.2, or TLSv1.3 needs to work, but does not" exit $failures fi -echo "[pass] At least one of TLSv1.0, TLSv1.1, or TLSv1.2 worked" +echo "[pass] At least one of TLSv1.0, TLSv1.1, TLSv1.2, or TLSv1.3 worked" exit 0 diff --git a/test/go/Makefile.am b/test/go/Makefile.am index 6da83394bea..eae153c2944 100644 --- a/test/go/Makefile.am +++ b/test/go/Makefile.am @@ -58,9 +58,9 @@ check: gopath genmock GOPATH=`pwd` $(GO) test -v common/... genmock: gopath - GOPATH=`pwd` $(GO) install github.com/golang/mock/mockgen - GOPATH=`pwd` bin/mockgen -destination=src/common/mock_handler.go -package=common gen/thrifttest ThriftTest + sh genmock.sh EXTRA_DIST = \ src/bin \ - src/common + src/common \ + genmock.sh diff --git a/test/go/genmock.sh b/test/go/genmock.sh new file mode 100644 index 00000000000..3ba41b9ed5b --- /dev/null +++ b/test/go/genmock.sh @@ -0,0 +1,15 @@ +#!/bin/sh +set -e + +export GOPATH=`pwd` +export GOBIN=`pwd`/bin +export GO111MODULE=off + +mkdir -p src/github.com/golang/mock +cd src/github.com/golang +curl -fsSL https://github.com/golang/mock/archive/v1.2.0.tar.gz -o mock.tar.gz +tar -xzvf mock.tar.gz -C mock --strip-components=1 +cd mock/mockgen +go install . +cd ../../../../../ +bin/mockgen -destination=src/common/mock_handler.go -package=common gen/thrifttest ThriftTest diff --git a/test/go/src/bin/testserver/main.go b/test/go/src/bin/testserver/main.go index ca2d967b623..6fc1185a239 100644 --- a/test/go/src/bin/testserver/main.go +++ b/test/go/src/bin/testserver/main.go @@ -32,7 +32,7 @@ var host = flag.String("host", "localhost", "Host to connect") var port = flag.Int64("port", 9090, "Port number to connect") var domain_socket = flag.String("domain-socket", "", "Domain Socket (e.g. /tmp/ThriftTest.thrift), instead of host and port") var transport = flag.String("transport", "buffered", "Transport: buffered, framed, http, zlib") -var protocol = flag.String("protocol", "binary", "Protocol: binary, compact, json") +var protocol = flag.String("protocol", "binary", "Protocol: binary, compact, json, header") var ssl = flag.Bool("ssl", false, "Encrypted Transport using SSL") var zlib = flag.Bool("zlib", false, "Wrapped Transport using Zlib") var certPath = flag.String("certPath", "keys", "Directory that contains SSL certificates") @@ -43,7 +43,7 @@ func main() { processor, serverTransport, transportFactory, protocolFactory, err := common.GetServerParams(*host, *port, *domain_socket, *transport, *protocol, *ssl, *certPath, common.PrintingHandler) if err != nil { - log.Fatalf("Unable to process server params: ", err) + log.Fatalf("Unable to process server params: %v", err) } if *transport == "http" { diff --git a/test/go/src/common/client.go b/test/go/src/common/client.go index 236ce43eaf6..ed820aeafd4 100644 --- a/test/go/src/common/client.go +++ b/test/go/src/common/client.go @@ -55,6 +55,8 @@ func StartClient( protocolFactory = thrift.NewTJSONProtocolFactory() case "binary": protocolFactory = thrift.NewTBinaryProtocolFactoryDefault() + case "header": + protocolFactory = thrift.NewTHeaderProtocolFactory() default: return nil, nil, fmt.Errorf("Invalid protocol specified %s", protocol) } diff --git a/test/go/src/common/server.go b/test/go/src/common/server.go index 5ac44000298..c6674ae7597 100644 --- a/test/go/src/common/server.go +++ b/test/go/src/common/server.go @@ -60,6 +60,8 @@ func GetServerParams( protocolFactory = thrift.NewTJSONProtocolFactory() case "binary": protocolFactory = thrift.NewTBinaryProtocolFactoryDefault() + case "header": + protocolFactory = thrift.NewTHeaderProtocolFactory() default: return nil, nil, nil, nil, fmt.Errorf("Invalid protocol specified %s", protocol) } diff --git a/test/haxe/src/Arguments.hx b/test/haxe/src/Arguments.hx index cc107498734..56e5253562c 100644 --- a/test/haxe/src/Arguments.hx +++ b/test/haxe/src/Arguments.hx @@ -103,7 +103,7 @@ class Arguments +" --port arg (=9090) Port number to listen / connect to\n" /* not supported yet +" --domain-socket arg Unix Domain Socket (e.g. /tmp/ThriftTest.thrift)\n" - +" --named-pipe arg Windows Named Pipe (e.g. MyThriftPipe)\n" + +" --pipe arg Windows Named Pipe (e.g. MyThriftPipe)\n" */ +" --protocol arg (=binary) protocol: binary, compact, json\n" /* not supported yet @@ -187,8 +187,8 @@ class Arguments // --domain-socket arg Unix Domain Socket (e.g. /tmp/ThriftTest.thrift) throw "domain sockets not supported yet"; } - else if (arg == "--named-pipe") { - // --named-pipe arg Windows Named Pipe (e.g. MyThriftPipe) + else if (arg == "--pipe") { + // --pipe arg Windows Named Pipe (e.g. MyThriftPipe) throw "named pipes not supported yet"; } else if (arg == "--protocol") { diff --git a/test/hs/DebugProtoTest_Main.hs b/test/hs/DebugProtoTest_Main.hs index fb28963f687..97d4347c2ee 100644 --- a/test/hs/DebugProtoTest_Main.hs +++ b/test/hs/DebugProtoTest_Main.hs @@ -117,7 +117,11 @@ instance SIface.Srv_Iface InheritedHandler where Types.compactProtoTestStruct_byte_map_map = Map.empty, Types.compactProtoTestStruct_byte_set_map = Map.empty, - Types.compactProtoTestStruct_byte_list_map = Map.empty } + Types.compactProtoTestStruct_byte_list_map = Map.empty, + + Types.compactProtoTestStruct_field500 = 500, + Types.compactProtoTestStruct_field5000 = 5000, + Types.compactProtoTestStruct_field20000 = 20000 } methodWithDefaultArgs _ arg = do ThriftTestUtils.serverLog $ "Got methodWithDefaultArgs: " ++ show arg diff --git a/test/hs/Makefile.am b/test/hs/Makefile.am index 17489068c7a..817070d8fa1 100644 --- a/test/hs/Makefile.am +++ b/test/hs/Makefile.am @@ -30,9 +30,18 @@ check: stubs sh run-test.sh Include clean-local: - $(RM) -r gen-hs + $(RM) -r gen-hs/ $(RM) *.hi $(RM) *.o + $(RM) TestClient + $(RM) TestServer + +dist-hook: + $(RM) -r $(distdir)/gen-hs/ + $(RM) $(distdir)/*.hi + $(RM) $(distdir)/*.o + $(RM) $(destdir)/TestClient + $(RM) $(destdir)/TestServer all-local: stubs ghc -igen-hs TestServer.hs diff --git a/test/hs/TestClient.hs b/test/hs/TestClient.hs index 93fb591b343..d014e089ab9 100644 --- a/test/hs/TestClient.hs +++ b/test/hs/TestClient.hs @@ -193,16 +193,16 @@ runClient p = do -- Enum Test putStrLn "testEnum" - numz1 <- Client.testEnum prot ONE - when (numz1 /= ONE) exitFailure + numz1 <- Client.testEnum prot Numberz_ONE + when (numz1 /= Numberz_ONE) exitFailure putStrLn "testEnum" - numz2 <- Client.testEnum prot TWO - when (numz2 /= TWO) exitFailure + numz2 <- Client.testEnum prot Numberz_TWO + when (numz2 /= Numberz_TWO) exitFailure putStrLn "testEnum" - numz5 <- Client.testEnum prot FIVE - when (numz5 /= FIVE) exitFailure + numz5 <- Client.testEnum prot Numberz_FIVE + when (numz5 /= Numberz_FIVE) exitFailure -- Typedef Test putStrLn "testTypedef" diff --git a/test/hs/TestServer.hs b/test/hs/TestServer.hs index b7731ab1c40..c37dda3159f 100644 --- a/test/hs/TestServer.hs +++ b/test/hs/TestServer.hs @@ -212,10 +212,10 @@ instance ThriftTest_Iface TestHandler where testInsanity _ x = do System.IO.putStrLn "testInsanity()" - return $ Map.fromList [ (1, Map.fromList [ (TWO , x) - , (THREE, x) + return $ Map.fromList [ (1, Map.fromList [ (Numberz_TWO , x) + , (Numberz_THREE, x) ]) - , (2, Map.fromList [ (SIX, default_Insanity) + , (2, Map.fromList [ (Numberz_SIX, default_Insanity) ]) ] diff --git a/test/hs/ThriftTest_Main.hs b/test/hs/ThriftTest_Main.hs index 670023e2985..6421c6aeb32 100644 --- a/test/hs/ThriftTest_Main.hs +++ b/test/hs/ThriftTest_Main.hs @@ -107,7 +107,7 @@ instance Iface.ThriftTest_Iface TestHandler where return (Map.fromList [(1, Map.fromList [(2, 2)])]) testInsanity _ x = do - return (Map.fromList [(1, Map.fromList [(Types.ONE, x)])]) + return (Map.fromList [(1, Map.fromList [(Types.Numberz_ONE, x)])]) testMulti _ _ _ _ _ _ _ = do return (Types.Xtruct "" 0 0 0) diff --git a/test/known_failures_Linux.json b/test/known_failures_Linux.json index 1ab2af5fde3..3ee170344eb 100644 --- a/test/known_failures_Linux.json +++ b/test/known_failures_Linux.json @@ -1,8 +1,4 @@ [ - "c_glib-rs_multi_buffered-ip", - "c_glib-rs_multi_framed-ip", - "c_glib-rs_multic_buffered-ip", - "c_glib-rs_multic_framed-ip", "cl-c_glib_binary_buffered-ip", "cl-c_glib_binary_framed-ip", "cl-c_glib_multi-binary_buffered-ip", @@ -11,6 +7,8 @@ "cl-c_glib_multi_framed-ip", "cl-go_binary_buffered-ip", "cl-go_binary_framed-ip", + "cl-netstd_binary_buffered-ip", + "cl-netstd_binary_framed-ip", "cl-rb_binary-accel_buffered-ip", "cl-rb_binary-accel_framed-ip", "cl-rb_binary_buffered-ip", @@ -21,6 +19,42 @@ "cl-rs_multi-binary_framed-ip", "cl-rs_multi_buffered-ip", "cl-rs_multi_framed-ip", + "cpp-cpp_binary_websocket-domain", + "cpp-cpp_binary_websocket-ip", + "cpp-cpp_binary_websocket-ip-ssl", + "cpp-cpp_compact_websocket-domain", + "cpp-cpp_compact_websocket-ip", + "cpp-cpp_compact_websocket-ip-ssl", + "cpp-cpp_header_websocket-domain", + "cpp-cpp_header_websocket-ip", + "cpp-cpp_header_websocket-ip-ssl", + "cpp-cpp_json_websocket-domain", + "cpp-cpp_json_websocket-ip", + "cpp-cpp_json_websocket-ip-ssl", + "cpp-cpp_multi-binary_websocket-domain", + "cpp-cpp_multi-binary_websocket-ip", + "cpp-cpp_multi-binary_websocket-ip-ssl", + "cpp-cpp_multic-compact_websocket-domain", + "cpp-cpp_multic-compact_websocket-ip", + "cpp-cpp_multic-compact_websocket-ip-ssl", + "cpp-cpp_multic_websocket-domain", + "cpp-cpp_multic_websocket-ip", + "cpp-cpp_multic_websocket-ip-ssl", + "cpp-cpp_multih-header_websocket-domain", + "cpp-cpp_multih-header_websocket-ip", + "cpp-cpp_multih-header_websocket-ip-ssl", + "cpp-cpp_multih_websocket-domain", + "cpp-cpp_multih_websocket-ip", + "cpp-cpp_multih_websocket-ip-ssl", + "cpp-cpp_multij-json_websocket-domain", + "cpp-cpp_multij-json_websocket-ip", + "cpp-cpp_multij-json_websocket-ip-ssl", + "cpp-cpp_multij_websocket-domain", + "cpp-cpp_multij_websocket-ip", + "cpp-cpp_multij_websocket-ip-ssl", + "cpp-cpp_multi_websocket-domain", + "cpp-cpp_multi_websocket-ip", + "cpp-cpp_multi_websocket-ip-ssl", "cpp-dart_binary_http-ip", "cpp-dart_compact_http-ip", "cpp-dart_json_http-ip", @@ -31,12 +65,16 @@ "cpp-go_binary_http-ip-ssl", "cpp-go_compact_http-ip", "cpp-go_compact_http-ip-ssl", + "cpp-go_header_http-ip", + "cpp-go_header_http-ip-ssl", "cpp-go_json_http-ip", "cpp-go_json_http-ip-ssl", "cpp-go_multi-binary_http-ip", "cpp-go_multi-binary_http-ip-ssl", "cpp-go_multic-compact_http-ip", "cpp-go_multic-compact_http-ip-ssl", + "cpp-go_multih-header_http-ip", + "cpp-go_multih-header_http-ip-ssl", "cpp-go_multij-json_http-ip", "cpp-go_multij-json_http-ip-ssl", "cpp-java_binary_http-ip", @@ -47,8 +85,6 @@ "cpp-java_json_http-ip-ssl", "cpp-java_multi-binary_http-ip", "cpp-java_multi-binary_http-ip-ssl", - "cpp-java_multi_http-ip", - "cpp-java_multi_http-ip-ssl", "cpp-java_multic-compact_http-ip", "cpp-java_multic-compact_http-ip-ssl", "cpp-java_multic_http-ip", @@ -57,104 +93,200 @@ "cpp-java_multij-json_http-ip-ssl", "cpp-java_multij_http-ip", "cpp-java_multij_http-ip-ssl", + "cpp-java_multi_http-ip", + "cpp-java_multi_http-ip-ssl", + "cpp-netstd_binary_buffered-ip", + "cpp-netstd_binary_buffered-ip-ssl", + "cpp-netstd_binary_framed-ip", + "cpp-netstd_binary_framed-ip-ssl", + "cpp-netstd_compact_buffered-ip", + "cpp-netstd_compact_buffered-ip-ssl", + "cpp-netstd_compact_framed-ip", + "cpp-netstd_compact_framed-ip-ssl", + "cpp-netstd_json_buffered-ip", + "cpp-netstd_json_buffered-ip-ssl", + "cpp-netstd_json_framed-ip", + "cpp-netstd_json_framed-ip-ssl", + "cpp-netstd_multi-binary_buffered-ip", + "cpp-netstd_multi-binary_buffered-ip-ssl", + "cpp-netstd_multi-binary_framed-ip", + "cpp-netstd_multi-binary_framed-ip-ssl", + "cpp-netstd_multic-compact_buffered-ip", + "cpp-netstd_multic-compact_buffered-ip-ssl", + "cpp-netstd_multic-compact_framed-ip", + "cpp-netstd_multic-compact_framed-ip-ssl", + "cpp-netstd_multij-json_buffered-ip", + "cpp-netstd_multij-json_buffered-ip-ssl", + "cpp-netstd_multij-json_framed-ip", + "cpp-netstd_multij-json_framed-ip-ssl", "cpp-nodejs_binary_http-domain", "cpp-nodejs_binary_http-ip", "cpp-nodejs_binary_http-ip-ssl", + "cpp-nodejs_binary_websocket-domain", "cpp-nodejs_compact_http-domain", "cpp-nodejs_compact_http-ip", "cpp-nodejs_compact_http-ip-ssl", + "cpp-nodejs_compact_websocket-domain", + "cpp-nodejs_header_http-domain", + "cpp-nodejs_header_http-ip", + "cpp-nodejs_header_http-ip-ssl", + "cpp-nodejs_header_websocket-domain", + "cpp-nodejs_header_websocket-ip", + "cpp-nodejs_header_websocket-ip-ssl", "cpp-nodejs_json_http-domain", "cpp-nodejs_json_http-ip", "cpp-nodejs_json_http-ip-ssl", + "cpp-nodejs_json_websocket-domain", "cpp-nodejs_multi-binary_http-domain", "cpp-nodejs_multi-binary_http-ip", "cpp-nodejs_multi-binary_http-ip-ssl", + "cpp-nodejs_multi-binary_websocket-domain", "cpp-nodejs_multic-compact_http-domain", "cpp-nodejs_multic-compact_http-ip", "cpp-nodejs_multic-compact_http-ip-ssl", + "cpp-nodejs_multic-compact_websocket-domain", + "cpp-nodejs_multih-header_http-domain", + "cpp-nodejs_multih-header_http-ip", + "cpp-nodejs_multih-header_http-ip-ssl", + "cpp-nodejs_multih-header_websocket-domain", + "cpp-nodejs_multih-header_websocket-ip", + "cpp-nodejs_multih-header_websocket-ip-ssl", "cpp-nodejs_multij-json_http-domain", "cpp-nodejs_multij-json_http-ip", "cpp-nodejs_multij-json_http-ip-ssl", + "cpp-nodejs_multij-json_websocket-domain", + "cpp-php_binary-accel_buffered-ip", + "cpp-php_binary-accel_framed-ip", + "cpp-php_json_buffered-ip", + "cpp-php_json_framed-ip", + "cpp-php_multi-accel_buffered-ip", + "cpp-php_multi-accel_framed-ip", + "cpp-php_multij-json_buffered-ip", + "cpp-php_multij-json_framed-ip", + "cpp-py3_binary-accel_http-domain", "cpp-py3_binary-accel_http-ip", "cpp-py3_binary-accel_http-ip-ssl", + "cpp-py3_binary_http-domain", "cpp-py3_binary_http-ip", "cpp-py3_binary_http-ip-ssl", + "cpp-py3_compact-accelc_http-domain", "cpp-py3_compact-accelc_http-ip", "cpp-py3_compact-accelc_http-ip-ssl", + "cpp-py3_compact_http-domain", "cpp-py3_compact_http-ip", "cpp-py3_compact_http-ip-ssl", + "cpp-py3_header_http-domain", "cpp-py3_header_http-ip", "cpp-py3_header_http-ip-ssl", + "cpp-py3_json_http-domain", "cpp-py3_json_http-ip", "cpp-py3_json_http-ip-ssl", + "cpp-py3_multi-accel_http-domain", "cpp-py3_multi-accel_http-ip", "cpp-py3_multi-accel_http-ip-ssl", + "cpp-py3_multi-binary_http-domain", "cpp-py3_multi-binary_http-ip", "cpp-py3_multi-binary_http-ip-ssl", + "cpp-py3_multi-multia_http-domain", "cpp-py3_multi-multia_http-ip", "cpp-py3_multi-multia_http-ip-ssl", - "cpp-py3_multi_http-ip", - "cpp-py3_multi_http-ip-ssl", + "cpp-py3_multic-accelc_http-domain", "cpp-py3_multic-accelc_http-ip", "cpp-py3_multic-accelc_http-ip-ssl", + "cpp-py3_multic-compact_http-domain", "cpp-py3_multic-compact_http-ip", "cpp-py3_multic-compact_http-ip-ssl", + "cpp-py3_multic-multiac_http-domain", "cpp-py3_multic-multiac_http-ip", "cpp-py3_multic-multiac_http-ip-ssl", + "cpp-py3_multic_http-domain", "cpp-py3_multic_http-ip", "cpp-py3_multic_http-ip-ssl", + "cpp-py3_multih-header_http-domain", "cpp-py3_multih-header_http-ip", "cpp-py3_multih-header_http-ip-ssl", + "cpp-py3_multih_http-domain", + "cpp-py3_multih_http-ip", + "cpp-py3_multih_http-ip-ssl", + "cpp-py3_multij-json_http-domain", "cpp-py3_multij-json_http-ip", "cpp-py3_multij-json_http-ip-ssl", + "cpp-py3_multij_http-domain", "cpp-py3_multij_http-ip", "cpp-py3_multij_http-ip-ssl", + "cpp-py3_multi_http-domain", + "cpp-py3_multi_http-ip", + "cpp-py3_multi_http-ip-ssl", + "cpp-py_binary-accel_http-domain", "cpp-py_binary-accel_http-ip", "cpp-py_binary-accel_http-ip-ssl", + "cpp-py_binary_http-domain", "cpp-py_binary_http-ip", "cpp-py_binary_http-ip-ssl", + "cpp-py_compact-accelc_http-domain", "cpp-py_compact-accelc_http-ip", "cpp-py_compact-accelc_http-ip-ssl", + "cpp-py_compact_http-domain", "cpp-py_compact_http-ip", "cpp-py_compact_http-ip-ssl", + "cpp-py_header_http-domain", "cpp-py_header_http-ip", "cpp-py_header_http-ip-ssl", + "cpp-py_json_http-domain", "cpp-py_json_http-ip", "cpp-py_json_http-ip-ssl", + "cpp-py_multi-accel_http-domain", "cpp-py_multi-accel_http-ip", "cpp-py_multi-accel_http-ip-ssl", + "cpp-py_multi-binary_http-domain", "cpp-py_multi-binary_http-ip", "cpp-py_multi-binary_http-ip-ssl", + "cpp-py_multi-multia_http-domain", "cpp-py_multi-multia_http-ip", "cpp-py_multi-multia_http-ip-ssl", - "cpp-py_multi_http-ip", - "cpp-py_multi_http-ip-ssl", + "cpp-py_multic-accelc_http-domain", "cpp-py_multic-accelc_http-ip", "cpp-py_multic-accelc_http-ip-ssl", + "cpp-py_multic-compact_http-domain", "cpp-py_multic-compact_http-ip", "cpp-py_multic-compact_http-ip-ssl", + "cpp-py_multic-multiac_http-domain", "cpp-py_multic-multiac_http-ip", "cpp-py_multic-multiac_http-ip-ssl", + "cpp-py_multic_http-domain", "cpp-py_multic_http-ip", "cpp-py_multic_http-ip-ssl", + "cpp-py_multih-header_http-domain", "cpp-py_multih-header_http-ip", "cpp-py_multih-header_http-ip-ssl", + "cpp-py_multih_http-domain", + "cpp-py_multih_http-ip", + "cpp-py_multih_http-ip-ssl", + "cpp-py_multij-json_http-domain", "cpp-py_multij-json_http-ip", "cpp-py_multij-json_http-ip-ssl", + "cpp-py_multij_http-domain", "cpp-py_multij_http-ip", "cpp-py_multij_http-ip-ssl", - "cpp-rs_multi_buffered-ip", - "cpp-rs_multi_framed-ip", + "cpp-py_multi_http-domain", + "cpp-py_multi_http-ip", + "cpp-py_multi_http-ip-ssl", "cpp-rs_multic_buffered-ip", "cpp-rs_multic_framed-ip", - "csharp-rb_binary-accel_buffered-ip-ssl", - "csharp-rb_binary-accel_framed-ip-ssl", - "csharp-rb_binary_buffered-ip-ssl", - "csharp-rb_binary_framed-ip-ssl", - "csharp-rb_compact_buffered-ip-ssl", - "csharp-rb_compact_framed-ip-ssl", - "csharp-rb_json_buffered-ip-ssl", - "csharp-rb_json_framed-ip-ssl", + "cpp-rs_multi_buffered-ip", + "cpp-rs_multi_framed-ip", + "c_glib-netstd_binary_buffered-ip", + "c_glib-netstd_binary_framed-ip", + "c_glib-netstd_compact_buffered-ip", + "c_glib-netstd_compact_framed-ip", + "c_glib-netstd_multi-binary_buffered-ip", + "c_glib-netstd_multi-binary_framed-ip", + "c_glib-netstd_multic-compact_buffered-ip", + "c_glib-netstd_multic-compact_framed-ip", + "c_glib-rs_multic_buffered-ip", + "c_glib-rs_multic_framed-ip", + "c_glib-rs_multi_buffered-ip", + "c_glib-rs_multi_framed-ip", "d-cl_binary_buffered-ip", "d-cl_binary_framed-ip", "d-cpp_binary_buffered-ip", @@ -163,24 +295,30 @@ "d-cpp_binary_framed-ip-ssl", "d-cpp_binary_http-ip", "d-cpp_binary_http-ip-ssl", + "d-cpp_binary_zlib-ip", + "d-cpp_binary_zlib-ip-ssl", "d-cpp_compact_buffered-ip", "d-cpp_compact_buffered-ip-ssl", "d-cpp_compact_framed-ip", "d-cpp_compact_framed-ip-ssl", "d-cpp_compact_http-ip", "d-cpp_compact_http-ip-ssl", + "d-cpp_compact_zlib-ip", + "d-cpp_compact_zlib-ip-ssl", "d-cpp_json_buffered-ip", "d-cpp_json_buffered-ip-ssl", "d-cpp_json_framed-ip", "d-cpp_json_framed-ip-ssl", "d-cpp_json_http-ip", "d-cpp_json_http-ip-ssl", - "d-d_binary_http-ip", - "d-d_compact_http-ip", - "d-d_json_http-ip", + "d-cpp_json_zlib-ip", + "d-cpp_json_zlib-ip-ssl", "d-dart_binary_http-ip", "d-dart_compact_http-ip", "d-dart_json_http-ip", + "d-d_binary_http-ip", + "d-d_compact_http-ip", + "d-d_json_http-ip", "d-go_binary_http-ip", "d-go_binary_http-ip-ssl", "d-go_compact_http-ip", @@ -194,6 +332,18 @@ "d-java_json_http-ip", "d-java_json_http-ip-ssl", "d-js_json_http-ip", + "d-netstd_binary_buffered-ip", + "d-netstd_binary_buffered-ip-ssl", + "d-netstd_binary_framed-ip", + "d-netstd_binary_framed-ip-ssl", + "d-netstd_compact_buffered-ip", + "d-netstd_compact_buffered-ip-ssl", + "d-netstd_compact_framed-ip", + "d-netstd_compact_framed-ip-ssl", + "d-netstd_json_buffered-ip", + "d-netstd_json_buffered-ip-ssl", + "d-netstd_json_framed-ip", + "d-netstd_json_framed-ip-ssl", "d-nodejs_binary_buffered-ip", "d-nodejs_binary_buffered-ip-ssl", "d-nodejs_binary_framed-ip", @@ -219,64 +369,90 @@ "d-py3_binary-accel_framed-ip-ssl", "d-py3_binary-accel_http-ip", "d-py3_binary-accel_http-ip-ssl", + "d-py3_binary-accel_zlib-ip", + "d-py3_binary-accel_zlib-ip-ssl", "d-py3_binary_buffered-ip", "d-py3_binary_buffered-ip-ssl", "d-py3_binary_framed-ip", "d-py3_binary_framed-ip-ssl", "d-py3_binary_http-ip", "d-py3_binary_http-ip-ssl", + "d-py3_binary_zlib-ip", + "d-py3_binary_zlib-ip-ssl", "d-py3_compact-accelc_buffered-ip", "d-py3_compact-accelc_buffered-ip-ssl", "d-py3_compact-accelc_framed-ip", "d-py3_compact-accelc_framed-ip-ssl", "d-py3_compact-accelc_http-ip", "d-py3_compact-accelc_http-ip-ssl", + "d-py3_compact-accelc_zlib-ip", + "d-py3_compact-accelc_zlib-ip-ssl", "d-py3_compact_buffered-ip", "d-py3_compact_buffered-ip-ssl", "d-py3_compact_framed-ip", "d-py3_compact_framed-ip-ssl", "d-py3_compact_http-ip", "d-py3_compact_http-ip-ssl", + "d-py3_compact_zlib-ip", + "d-py3_compact_zlib-ip-ssl", "d-py3_json_buffered-ip", "d-py3_json_buffered-ip-ssl", "d-py3_json_framed-ip", "d-py3_json_framed-ip-ssl", "d-py3_json_http-ip", "d-py3_json_http-ip-ssl", + "d-py3_json_zlib-ip", + "d-py3_json_zlib-ip-ssl", "d-py_binary-accel_buffered-ip", "d-py_binary-accel_buffered-ip-ssl", "d-py_binary-accel_framed-ip", "d-py_binary-accel_framed-ip-ssl", "d-py_binary-accel_http-ip", "d-py_binary-accel_http-ip-ssl", + "d-py_binary-accel_zlib-ip", + "d-py_binary-accel_zlib-ip-ssl", "d-py_binary_buffered-ip", "d-py_binary_buffered-ip-ssl", "d-py_binary_framed-ip", "d-py_binary_framed-ip-ssl", "d-py_binary_http-ip", "d-py_binary_http-ip-ssl", + "d-py_binary_zlib-ip", + "d-py_binary_zlib-ip-ssl", "d-py_compact-accelc_buffered-ip", "d-py_compact-accelc_buffered-ip-ssl", "d-py_compact-accelc_framed-ip", "d-py_compact-accelc_framed-ip-ssl", "d-py_compact-accelc_http-ip", "d-py_compact-accelc_http-ip-ssl", + "d-py_compact-accelc_zlib-ip", + "d-py_compact-accelc_zlib-ip-ssl", "d-py_compact_buffered-ip", "d-py_compact_buffered-ip-ssl", "d-py_compact_framed-ip", "d-py_compact_framed-ip-ssl", "d-py_compact_http-ip", "d-py_compact_http-ip-ssl", + "d-py_compact_zlib-ip", + "d-py_compact_zlib-ip-ssl", "d-py_json_buffered-ip", "d-py_json_buffered-ip-ssl", "d-py_json_framed-ip", "d-py_json_framed-ip-ssl", "d-py_json_http-ip", "d-py_json_http-ip-ssl", + "d-py_json_zlib-ip", + "d-py_json_zlib-ip-ssl", "erl-cpp_binary_buffered-ip", "erl-cpp_compact_buffered-ip", - "erl-csharp_binary_buffered-ip", - "erl-csharp_compact_buffered-ip", + "erl-netstd_binary_buffered-ip", + "erl-netstd_binary_buffered-ip-ssl", + "erl-netstd_binary_framed-ip", + "erl-netstd_binary_framed-ip-ssl", + "erl-netstd_compact_buffered-ip", + "erl-netstd_compact_buffered-ip-ssl", + "erl-netstd_compact_framed-ip", + "erl-netstd_compact_framed-ip-ssl", "erl-nodejs_binary_buffered-ip", "erl-nodejs_compact_buffered-ip", "erl-nodets_binary_buffered-ip", @@ -296,58 +472,343 @@ "go-cpp_binary_http-ip-ssl", "go-cpp_compact_http-ip", "go-cpp_compact_http-ip-ssl", + "go-cpp_header_http-ip", + "go-cpp_header_http-ip-ssl", "go-cpp_json_http-ip", "go-cpp_json_http-ip-ssl", + "go-dart_binary_http-ip", + "go-dart_compact_http-ip", + "go-dart_json_http-ip", "go-d_binary_http-ip", "go-d_binary_http-ip-ssl", "go-d_compact_http-ip", "go-d_compact_http-ip-ssl", "go-d_json_http-ip", "go-d_json_http-ip-ssl", - "go-dart_binary_http-ip", - "go-dart_compact_http-ip", - "go-dart_json_http-ip", "go-java_binary_http-ip", "go-java_binary_http-ip-ssl", "go-java_compact_http-ip", "go-java_compact_http-ip-ssl", "go-java_json_http-ip", "go-java_json_http-ip-ssl", + "go-netstd_binary_buffered-ip", + "go-netstd_binary_buffered-ip-ssl", + "go-netstd_binary_framed-ip", + "go-netstd_binary_framed-ip-ssl", + "go-netstd_compact_buffered-ip", + "go-netstd_compact_buffered-ip-ssl", + "go-netstd_compact_framed-ip", + "go-netstd_compact_framed-ip-ssl", + "go-netstd_json_buffered-ip", + "go-netstd_json_buffered-ip-ssl", + "go-netstd_json_framed-ip", + "go-netstd_json_framed-ip-ssl", "go-py3_binary-accel_zlib-ip-ssl", "go-py3_compact-accelc_zlib-ip-ssl", "go-py_binary-accel_zlib-ip-ssl", "go-py_compact-accelc_zlib-ip-ssl", - "hs-csharp_binary_buffered-ip", - "hs-csharp_binary_framed-ip", - "hs-csharp_compact_buffered-ip", - "hs-csharp_compact_framed-ip", + "hs-netstd_binary_buffered-ip", + "hs-netstd_binary_framed-ip", + "hs-netstd_compact_buffered-ip", + "hs-netstd_compact_framed-ip", + "hs-netstd_json_buffered-ip", + "hs-netstd_json_framed-ip", + "hs-php_binary-accel_buffered-ip", + "hs-php_binary-accel_framed-ip", + "hs-php_json_buffered-ip", + "hs-php_json_framed-ip", + "java-erl_binary_buffered-ip-ssl", + "java-erl_binary_fastframed-framed-ip-ssl", + "java-erl_binary_framed-ip-ssl", + "java-erl_compact_buffered-ip-ssl", + "java-erl_compact_fastframed-framed-ip-ssl", + "java-erl_compact_framed-ip-ssl", + "java-erl_multi-binary_buffered-ip-ssl", + "java-erl_multi-binary_fastframed-framed-ip-ssl", + "java-erl_multi-binary_framed-ip-ssl", + "java-erl_multic-compact_buffered-ip-ssl", + "java-erl_multic-compact_fastframed-framed-ip-ssl", + "java-erl_multic-compact_framed-ip-ssl", + "java-netstd_binary_buffered-ip", + "java-netstd_binary_buffered-ip-ssl", + "java-netstd_binary_fastframed-framed-ip", + "java-netstd_binary_fastframed-framed-ip-ssl", + "java-netstd_binary_framed-ip", + "java-netstd_binary_framed-ip-ssl", + "java-netstd_compact_buffered-ip", + "java-netstd_compact_buffered-ip-ssl", + "java-netstd_compact_fastframed-framed-ip", + "java-netstd_compact_fastframed-framed-ip-ssl", + "java-netstd_compact_framed-ip", + "java-netstd_compact_framed-ip-ssl", + "java-netstd_json_buffered-ip", + "java-netstd_json_buffered-ip-ssl", + "java-netstd_json_fastframed-framed-ip", + "java-netstd_json_fastframed-framed-ip-ssl", + "java-netstd_json_framed-ip", + "java-netstd_json_framed-ip-ssl", + "java-netstd_multi-binary_buffered-ip", + "java-netstd_multi-binary_buffered-ip-ssl", + "java-netstd_multi-binary_fastframed-framed-ip", + "java-netstd_multi-binary_fastframed-framed-ip-ssl", + "java-netstd_multi-binary_framed-ip", + "java-netstd_multi-binary_framed-ip-ssl", + "java-netstd_multic-compact_buffered-ip", + "java-netstd_multic-compact_buffered-ip-ssl", + "java-netstd_multic-compact_fastframed-framed-ip", + "java-netstd_multic-compact_fastframed-framed-ip-ssl", + "java-netstd_multic-compact_framed-ip", + "java-netstd_multic-compact_framed-ip-ssl", + "java-netstd_multij-json_buffered-ip", + "java-netstd_multij-json_buffered-ip-ssl", + "java-netstd_multij-json_fastframed-framed-ip", + "java-netstd_multij-json_fastframed-framed-ip-ssl", + "java-netstd_multij-json_framed-ip", + "java-netstd_multij-json_framed-ip-ssl", + "java-php_binary-accel_buffered-ip", + "java-php_binary-accel_fastframed-framed-ip", + "java-php_binary-accel_framed-ip", + "java-php_json_buffered-ip", + "java-php_json_fastframed-framed-ip", + "java-php_json_framed-ip", + "java-php_multi-accel_buffered-ip", + "java-php_multi-accel_fastframed-framed-ip", + "java-php_multi-accel_framed-ip", + "java-php_multij-json_buffered-ip", + "java-php_multij-json_fastframed-framed-ip", + "java-php_multij-json_framed-ip", + "netstd-cl_binary_buffered-ip", + "netstd-cl_binary_framed-ip", + "netstd-cpp_binary_buffered-ip", + "netstd-cpp_binary_buffered-ip-ssl", + "netstd-cpp_binary_framed-ip", + "netstd-cpp_binary_framed-ip-ssl", + "netstd-cpp_compact_buffered-ip", + "netstd-cpp_compact_buffered-ip-ssl", + "netstd-cpp_compact_framed-ip", + "netstd-cpp_compact_framed-ip-ssl", + "netstd-cpp_json_buffered-ip", + "netstd-cpp_json_buffered-ip-ssl", + "netstd-cpp_json_framed-ip", + "netstd-cpp_json_framed-ip-ssl", + "netstd-c_glib_binary_buffered-ip", + "netstd-c_glib_binary_buffered-ip-ssl", + "netstd-c_glib_binary_framed-ip", + "netstd-c_glib_binary_framed-ip-ssl", + "netstd-c_glib_compact_buffered-ip", + "netstd-c_glib_compact_buffered-ip-ssl", + "netstd-c_glib_compact_framed-ip", + "netstd-c_glib_compact_framed-ip-ssl", + "netstd-dart_binary_buffered-ip", + "netstd-dart_binary_framed-ip", + "netstd-dart_compact_buffered-ip", + "netstd-dart_compact_framed-ip", + "netstd-dart_json_buffered-ip", + "netstd-dart_json_framed-ip", + "netstd-d_binary_buffered-ip", + "netstd-d_binary_buffered-ip-ssl", + "netstd-d_binary_framed-ip", + "netstd-d_binary_framed-ip-ssl", + "netstd-d_compact_buffered-ip", + "netstd-d_compact_buffered-ip-ssl", + "netstd-d_compact_framed-ip", + "netstd-d_compact_framed-ip-ssl", + "netstd-d_json_buffered-ip", + "netstd-d_json_buffered-ip-ssl", + "netstd-d_json_framed-ip", + "netstd-d_json_framed-ip-ssl", + "netstd-erl_binary_buffered-ip", + "netstd-erl_binary_buffered-ip-ssl", + "netstd-erl_binary_framed-ip", + "netstd-erl_binary_framed-ip-ssl", + "netstd-erl_compact_buffered-ip", + "netstd-erl_compact_buffered-ip-ssl", + "netstd-erl_compact_framed-ip", + "netstd-erl_compact_framed-ip-ssl", + "netstd-go_binary_buffered-ip", + "netstd-go_binary_buffered-ip-ssl", + "netstd-go_binary_framed-ip", + "netstd-go_binary_framed-ip-ssl", + "netstd-go_compact_buffered-ip", + "netstd-go_compact_buffered-ip-ssl", + "netstd-go_compact_framed-ip", + "netstd-go_compact_framed-ip-ssl", + "netstd-go_json_buffered-ip", + "netstd-go_json_buffered-ip-ssl", + "netstd-go_json_framed-ip", + "netstd-go_json_framed-ip-ssl", + "netstd-hs_binary_buffered-ip", + "netstd-hs_binary_framed-ip", + "netstd-hs_compact_buffered-ip", + "netstd-hs_compact_framed-ip", + "netstd-hs_json_buffered-ip", + "netstd-hs_json_framed-ip", + "netstd-java_binary_buffered-ip", + "netstd-java_binary_buffered-ip-ssl", + "netstd-java_binary_framed-fastframed-ip", + "netstd-java_binary_framed-fastframed-ip-ssl", + "netstd-java_binary_framed-ip", + "netstd-java_binary_framed-ip-ssl", + "netstd-java_compact_buffered-ip", + "netstd-java_compact_buffered-ip-ssl", + "netstd-java_compact_framed-fastframed-ip", + "netstd-java_compact_framed-fastframed-ip-ssl", + "netstd-java_compact_framed-ip", + "netstd-java_compact_framed-ip-ssl", + "netstd-java_json_buffered-ip", + "netstd-java_json_buffered-ip-ssl", + "netstd-java_json_framed-fastframed-ip", + "netstd-java_json_framed-fastframed-ip-ssl", + "netstd-java_json_framed-ip", + "netstd-java_json_framed-ip-ssl", + "netstd-lua_binary_buffered-ip", + "netstd-lua_binary_framed-ip", + "netstd-lua_compact_buffered-ip", + "netstd-lua_compact_framed-ip", + "netstd-lua_json_buffered-ip", + "netstd-lua_json_framed-ip", + "netstd-netstd_binary_buffered-ip", + "netstd-netstd_binary_buffered-ip-ssl", + "netstd-netstd_binary_framed-ip", + "netstd-netstd_binary_framed-ip-ssl", + "netstd-netstd_compact_buffered-ip", + "netstd-netstd_compact_buffered-ip-ssl", + "netstd-netstd_compact_framed-ip", + "netstd-netstd_compact_framed-ip-ssl", + "netstd-netstd_json_buffered-ip", + "netstd-netstd_json_buffered-ip-ssl", + "netstd-netstd_json_framed-ip", + "netstd-netstd_json_framed-ip-ssl", + "netstd-nodejs_binary_buffered-ip", + "netstd-nodejs_binary_buffered-ip-ssl", + "netstd-nodejs_binary_framed-ip", + "netstd-nodejs_binary_framed-ip-ssl", + "netstd-nodejs_compact_buffered-ip", + "netstd-nodejs_compact_buffered-ip-ssl", + "netstd-nodejs_compact_framed-ip", + "netstd-nodejs_compact_framed-ip-ssl", + "netstd-nodejs_json_buffered-ip", + "netstd-nodejs_json_buffered-ip-ssl", + "netstd-nodejs_json_framed-ip", + "netstd-nodejs_json_framed-ip-ssl", + "netstd-nodets_binary_buffered-ip", + "netstd-perl_binary_buffered-ip", + "netstd-perl_binary_buffered-ip-ssl", + "netstd-perl_binary_framed-ip", + "netstd-perl_binary_framed-ip-ssl", + "netstd-php_binary-accel_buffered-ip", + "netstd-php_binary-accel_framed-ip", + "netstd-php_binary_buffered-ip", + "netstd-php_binary_framed-ip", + "netstd-php_compact_buffered-ip", + "netstd-php_compact_framed-ip", + "netstd-php_json_buffered-ip", + "netstd-php_json_framed-ip", + "netstd-py3_binary-accel_buffered-ip", + "netstd-py3_binary-accel_buffered-ip-ssl", + "netstd-py3_binary-accel_framed-ip", + "netstd-py3_binary-accel_framed-ip-ssl", + "netstd-py3_binary_buffered-ip", + "netstd-py3_binary_buffered-ip-ssl", + "netstd-py3_binary_framed-ip", + "netstd-py3_binary_framed-ip-ssl", + "netstd-py3_compact-accelc_buffered-ip", + "netstd-py3_compact-accelc_buffered-ip-ssl", + "netstd-py3_compact-accelc_framed-ip", + "netstd-py3_compact-accelc_framed-ip-ssl", + "netstd-py3_compact_buffered-ip", + "netstd-py3_compact_buffered-ip-ssl", + "netstd-py3_compact_framed-ip", + "netstd-py3_compact_framed-ip-ssl", + "netstd-py3_json_buffered-ip", + "netstd-py3_json_buffered-ip-ssl", + "netstd-py3_json_framed-ip", + "netstd-py3_json_framed-ip-ssl", + "netstd-py_binary-accel_buffered-ip", + "netstd-py_binary-accel_buffered-ip-ssl", + "netstd-py_binary-accel_framed-ip", + "netstd-py_binary-accel_framed-ip-ssl", + "netstd-py_binary_buffered-ip", + "netstd-py_binary_buffered-ip-ssl", + "netstd-py_binary_framed-ip", + "netstd-py_binary_framed-ip-ssl", + "netstd-py_compact-accelc_buffered-ip", + "netstd-py_compact-accelc_buffered-ip-ssl", + "netstd-py_compact-accelc_framed-ip", + "netstd-py_compact-accelc_framed-ip-ssl", + "netstd-py_compact_buffered-ip", + "netstd-py_compact_buffered-ip-ssl", + "netstd-py_compact_framed-ip", + "netstd-py_compact_framed-ip-ssl", + "netstd-py_json_buffered-ip", + "netstd-py_json_buffered-ip-ssl", + "netstd-py_json_framed-ip", + "netstd-py_json_framed-ip-ssl", + "netstd-rb_binary-accel_buffered-ip", + "netstd-rb_binary-accel_buffered-ip-ssl", + "netstd-rb_binary-accel_framed-ip", + "netstd-rb_binary-accel_framed-ip-ssl", + "netstd-rb_binary_buffered-ip", + "netstd-rb_binary_buffered-ip-ssl", + "netstd-rb_binary_framed-ip", + "netstd-rb_binary_framed-ip-ssl", + "netstd-rb_compact_buffered-ip", + "netstd-rb_compact_buffered-ip-ssl", + "netstd-rb_compact_framed-ip", + "netstd-rb_compact_framed-ip-ssl", + "netstd-rb_json_buffered-ip", + "netstd-rb_json_buffered-ip-ssl", + "netstd-rb_json_framed-ip", + "netstd-rb_json_framed-ip-ssl", + "netstd-rs_binary_buffered-ip", + "netstd-rs_binary_framed-ip", + "netstd-rs_compact_buffered-ip", + "netstd-rs_compact_framed-ip", "nodejs-cpp_binary_http-domain", "nodejs-cpp_binary_http-ip", "nodejs-cpp_binary_http-ip-ssl", + "nodejs-cpp_binary_websocket-domain", + "nodejs-cpp_binary_websocket-ip", + "nodejs-cpp_binary_websocket-ip-ssl", "nodejs-cpp_compact_http-domain", "nodejs-cpp_compact_http-ip", "nodejs-cpp_compact_http-ip-ssl", + "nodejs-cpp_compact_websocket-domain", + "nodejs-cpp_compact_websocket-ip", + "nodejs-cpp_compact_websocket-ip-ssl", + "nodejs-cpp_header_http-domain", + "nodejs-cpp_header_http-ip", + "nodejs-cpp_header_http-ip-ssl", + "nodejs-cpp_header_websocket-domain", + "nodejs-cpp_header_websocket-ip", + "nodejs-cpp_header_websocket-ip-ssl", "nodejs-cpp_json_buffered-ip-ssl", "nodejs-cpp_json_http-domain", "nodejs-cpp_json_http-ip", "nodejs-cpp_json_http-ip-ssl", + "nodejs-cpp_json_websocket-domain", + "nodejs-cpp_json_websocket-ip", + "nodejs-cpp_json_websocket-ip-ssl", + "nodejs-dart_binary_http-ip", + "nodejs-dart_compact_http-ip", + "nodejs-dart_json_http-ip", "nodejs-d_binary_http-ip", "nodejs-d_binary_http-ip-ssl", "nodejs-d_compact_http-ip", "nodejs-d_compact_http-ip-ssl", "nodejs-d_json_http-ip", "nodejs-d_json_http-ip-ssl", - "nodejs-dart_binary_http-ip", - "nodejs-dart_compact_http-ip", - "nodejs-dart_json_http-ip", "nodejs-go_binary_http-ip", "nodejs-go_binary_http-ip-ssl", "nodejs-go_compact_http-ip", "nodejs-go_compact_http-ip-ssl", + "nodejs-go_header_http-ip", + "nodejs-go_header_http-ip-ssl", "nodejs-go_json_http-ip", "nodejs-go_json_http-ip-ssl", "nodejs-hs_binary_http-ip", "nodejs-hs_compact_http-ip", + "nodejs-hs_header_http-ip", "nodejs-hs_json_http-ip", "nodejs-java_binary_http-ip", "nodejs-java_binary_http-ip-ssl", @@ -359,44 +820,151 @@ "nodejs-lua_binary_http-ip", "nodejs-lua_compact_http-ip", "nodejs-lua_json_http-ip", + "nodejs-netstd_binary_buffered-ip", + "nodejs-netstd_binary_buffered-ip-ssl", + "nodejs-netstd_binary_framed-ip", + "nodejs-netstd_binary_framed-ip-ssl", + "nodejs-netstd_compact_buffered-ip", + "nodejs-netstd_compact_buffered-ip-ssl", + "nodejs-netstd_compact_framed-ip", + "nodejs-netstd_compact_framed-ip-ssl", + "nodejs-netstd_json_buffered-ip", + "nodejs-netstd_json_buffered-ip-ssl", + "nodejs-netstd_json_framed-ip", + "nodejs-netstd_json_framed-ip-ssl", + "nodejs-nodejs_binary_websocket-domain", + "nodejs-nodejs_compact_websocket-domain", + "nodejs-nodejs_header_websocket-domain", + "nodejs-nodejs_json_websocket-domain", + "nodejs-php_binary-accel_buffered-ip", + "nodejs-php_binary-accel_framed-ip", + "nodejs-php_json_buffered-ip", + "nodejs-php_json_framed-ip", + "nodejs-py3_binary-accel_http-domain", "nodejs-py3_binary-accel_http-ip", "nodejs-py3_binary-accel_http-ip-ssl", + "nodejs-py3_binary_http-domain", "nodejs-py3_binary_http-ip", "nodejs-py3_binary_http-ip-ssl", + "nodejs-py3_compact-accelc_http-domain", "nodejs-py3_compact-accelc_http-ip", "nodejs-py3_compact-accelc_http-ip-ssl", + "nodejs-py3_compact_http-domain", "nodejs-py3_compact_http-ip", "nodejs-py3_compact_http-ip-ssl", + "nodejs-py3_header_http-domain", + "nodejs-py3_header_http-ip", + "nodejs-py3_header_http-ip-ssl", + "nodejs-py3_json_http-domain", "nodejs-py3_json_http-ip", "nodejs-py3_json_http-ip-ssl", + "nodejs-py_binary-accel_http-domain", "nodejs-py_binary-accel_http-ip", "nodejs-py_binary-accel_http-ip-ssl", + "nodejs-py_binary_http-domain", "nodejs-py_binary_http-ip", "nodejs-py_binary_http-ip-ssl", + "nodejs-py_compact-accelc_http-domain", "nodejs-py_compact-accelc_http-ip", "nodejs-py_compact-accelc_http-ip-ssl", + "nodejs-py_compact_http-domain", "nodejs-py_compact_http-ip", "nodejs-py_compact_http-ip-ssl", + "nodejs-py_header_http-domain", + "nodejs-py_header_http-ip", + "nodejs-py_header_http-ip-ssl", + "nodejs-py_json_http-domain", "nodejs-py_json_http-ip", "nodejs-py_json_http-ip-ssl", + "nodets-netstd_binary_buffered-ip", + "nodets-php_binary-accel_buffered-ip", + "perl-netstd_binary_buffered-ip", + "perl-netstd_binary_buffered-ip-ssl", + "perl-netstd_binary_framed-ip", + "perl-netstd_binary_framed-ip-ssl", + "perl-netstd_multi-binary_buffered-ip", + "perl-netstd_multi-binary_buffered-ip-ssl", + "perl-netstd_multi-binary_framed-ip", + "perl-netstd_multi-binary_framed-ip-ssl", "perl-rs_multi_buffered-ip", "perl-rs_multi_framed-ip", + "py-cpp_accel-binary_http-domain", "py-cpp_accel-binary_http-ip", "py-cpp_accel-binary_http-ip-ssl", + "py-cpp_accel-binary_zlib-domain", "py-cpp_accel-binary_zlib-ip", "py-cpp_accel-binary_zlib-ip-ssl", + "py-cpp_accelc-compact_http-domain", "py-cpp_accelc-compact_http-ip", "py-cpp_accelc-compact_http-ip-ssl", + "py-cpp_accelc-compact_zlib-domain", "py-cpp_accelc-compact_zlib-ip", "py-cpp_accelc-compact_zlib-ip-ssl", + "py-cpp_binary_http-domain", "py-cpp_binary_http-ip", "py-cpp_binary_http-ip-ssl", + "py-cpp_compact_http-domain", "py-cpp_compact_http-ip", "py-cpp_compact_http-ip-ssl", + "py-cpp_header_http-domain", "py-cpp_header_http-ip", "py-cpp_header_http-ip-ssl", + "py-cpp_json_http-domain", "py-cpp_json_http-ip", "py-cpp_json_http-ip-ssl", + "py-cpp_multi-binary_http-domain", + "py-cpp_multi-binary_http-ip", + "py-cpp_multi-binary_http-ip-ssl", + "py-cpp_multia-binary_http-domain", + "py-cpp_multia-binary_http-ip", + "py-cpp_multia-binary_http-ip-ssl", + "py-cpp_multia-binary_zlib-domain", + "py-cpp_multia-binary_zlib-ip", + "py-cpp_multia-binary_zlib-ip-ssl", + "py-cpp_multia-multi_http-domain", + "py-cpp_multia-multi_http-ip", + "py-cpp_multia-multi_http-ip-ssl", + "py-cpp_multia-multi_zlib-domain", + "py-cpp_multia-multi_zlib-ip", + "py-cpp_multia-multi_zlib-ip-ssl", + "py-cpp_multiac-compact_http-domain", + "py-cpp_multiac-compact_http-ip", + "py-cpp_multiac-compact_http-ip-ssl", + "py-cpp_multiac-compact_zlib-domain", + "py-cpp_multiac-compact_zlib-ip", + "py-cpp_multiac-compact_zlib-ip-ssl", + "py-cpp_multiac-multic_http-domain", + "py-cpp_multiac-multic_http-ip", + "py-cpp_multiac-multic_http-ip-ssl", + "py-cpp_multiac-multic_zlib-domain", + "py-cpp_multiac-multic_zlib-ip", + "py-cpp_multiac-multic_zlib-ip-ssl", + "py-cpp_multic-compact_http-domain", + "py-cpp_multic-compact_http-ip", + "py-cpp_multic-compact_http-ip-ssl", + "py-cpp_multic_http-domain", + "py-cpp_multic_http-ip", + "py-cpp_multic_http-ip-ssl", + "py-cpp_multih-header_http-domain", + "py-cpp_multih-header_http-ip", + "py-cpp_multih-header_http-ip-ssl", + "py-cpp_multih_http-domain", + "py-cpp_multih_http-ip", + "py-cpp_multih_http-ip-ssl", + "py-cpp_multij-json_http-domain", + "py-cpp_multij-json_http-ip", + "py-cpp_multij-json_http-ip-ssl", + "py-cpp_multij_http-domain", + "py-cpp_multij_http-ip", + "py-cpp_multij_http-ip-ssl", + "py-cpp_multi_http-domain", + "py-cpp_multi_http-ip", + "py-cpp_multi_http-ip-ssl", + "py-dart_accel-binary_http-ip", + "py-dart_accelc-compact_http-ip", + "py-dart_binary_http-ip", + "py-dart_compact_http-ip", + "py-dart_json_http-ip", "py-d_accel-binary_http-ip", "py-d_accel-binary_http-ip-ssl", "py-d_accelc-compact_http-ip", @@ -407,48 +975,149 @@ "py-d_compact_http-ip-ssl", "py-d_json_http-ip", "py-d_json_http-ip-ssl", - "py-dart_accel-binary_http-ip", - "py-dart_accelc-compact_http-ip", - "py-dart_binary_http-ip", - "py-dart_compact_http-ip", - "py-dart_json_http-ip", "py-hs_accel-binary_http-ip", "py-hs_accelc-compact_http-ip", "py-hs_binary_http-ip", "py-hs_compact_http-ip", "py-hs_header_http-ip", "py-hs_json_http-ip", - "py-java_accel-binary_http-ip", "py-java_accel-binary_http-ip-ssl", - "py-java_accelc-compact_http-ip", "py-java_accelc-compact_http-ip-ssl", - "py-java_binary_http-ip", "py-java_binary_http-ip-ssl", - "py-java_compact_http-ip", "py-java_compact_http-ip-ssl", - "py-java_json_http-ip", "py-java_json_http-ip-ssl", + "py-java_multi-binary_http-ip-ssl", + "py-java_multia-binary_http-ip-ssl", + "py-java_multia-multi_http-ip-ssl", + "py-java_multiac-compact_http-ip-ssl", + "py-java_multiac-multic_http-ip-ssl", + "py-java_multic-compact_http-ip-ssl", + "py-java_multic_http-ip-ssl", + "py-java_multij-json_http-ip-ssl", + "py-java_multij_http-ip-ssl", + "py-java_multi_http-ip-ssl", "py-lua_accel-binary_http-ip", "py-lua_accelc-compact_http-ip", "py-lua_binary_http-ip", "py-lua_compact_http-ip", "py-lua_json_http-ip", + "py-netstd_accel-binary_buffered-ip", + "py-netstd_accel-binary_buffered-ip-ssl", + "py-netstd_accel-binary_framed-ip", + "py-netstd_accel-binary_framed-ip-ssl", + "py-netstd_accelc-compact_buffered-ip", + "py-netstd_accelc-compact_buffered-ip-ssl", + "py-netstd_accelc-compact_framed-ip", + "py-netstd_accelc-compact_framed-ip-ssl", + "py-netstd_binary_buffered-ip", + "py-netstd_binary_buffered-ip-ssl", + "py-netstd_binary_framed-ip", + "py-netstd_binary_framed-ip-ssl", + "py-netstd_compact_buffered-ip", + "py-netstd_compact_buffered-ip-ssl", + "py-netstd_compact_framed-ip", + "py-netstd_compact_framed-ip-ssl", + "py-netstd_json_buffered-ip", + "py-netstd_json_buffered-ip-ssl", + "py-netstd_json_framed-ip", + "py-netstd_json_framed-ip-ssl", + "py-nodejs_accel-binary_http-domain", + "py-nodejs_accelc-compact_http-domain", + "py-nodejs_binary_http-domain", + "py-nodejs_compact_http-domain", + "py-nodejs_header_http-domain", + "py-nodejs_json_http-domain", + "py-php_accel_buffered-ip", + "py-php_accel_framed-ip", + "py-php_binary-accel_buffered-ip", + "py-php_binary-accel_framed-ip", + "py-php_json_buffered-ip", + "py-php_json_framed-ip", + "py-rs_multia-multi_buffered-ip", + "py-rs_multia-multi_framed-ip", + "py-rs_multiac-multic_buffered-ip", + "py-rs_multiac-multic_framed-ip", + "py-rs_multic_buffered-ip", + "py-rs_multic_framed-ip", + "py-rs_multi_buffered-ip", + "py-rs_multi_framed-ip", + "py3-cpp_accel-binary_http-domain", "py3-cpp_accel-binary_http-ip", "py3-cpp_accel-binary_http-ip-ssl", + "py3-cpp_accel-binary_zlib-domain", "py3-cpp_accel-binary_zlib-ip", "py3-cpp_accel-binary_zlib-ip-ssl", + "py3-cpp_accelc-compact_http-domain", "py3-cpp_accelc-compact_http-ip", "py3-cpp_accelc-compact_http-ip-ssl", + "py3-cpp_accelc-compact_zlib-domain", "py3-cpp_accelc-compact_zlib-ip", "py3-cpp_accelc-compact_zlib-ip-ssl", + "py3-cpp_binary_http-domain", "py3-cpp_binary_http-ip", "py3-cpp_binary_http-ip-ssl", + "py3-cpp_compact_http-domain", "py3-cpp_compact_http-ip", "py3-cpp_compact_http-ip-ssl", + "py3-cpp_header_http-domain", "py3-cpp_header_http-ip", "py3-cpp_header_http-ip-ssl", + "py3-cpp_json_http-domain", "py3-cpp_json_http-ip", "py3-cpp_json_http-ip-ssl", + "py3-cpp_multi-binary_http-domain", + "py3-cpp_multi-binary_http-ip", + "py3-cpp_multi-binary_http-ip-ssl", + "py3-cpp_multia-binary_http-domain", + "py3-cpp_multia-binary_http-ip", + "py3-cpp_multia-binary_http-ip-ssl", + "py3-cpp_multia-binary_zlib-domain", + "py3-cpp_multia-binary_zlib-ip", + "py3-cpp_multia-binary_zlib-ip-ssl", + "py3-cpp_multia-multi_http-domain", + "py3-cpp_multia-multi_http-ip", + "py3-cpp_multia-multi_http-ip-ssl", + "py3-cpp_multia-multi_zlib-domain", + "py3-cpp_multia-multi_zlib-ip", + "py3-cpp_multia-multi_zlib-ip-ssl", + "py3-cpp_multiac-compact_http-domain", + "py3-cpp_multiac-compact_http-ip", + "py3-cpp_multiac-compact_http-ip-ssl", + "py3-cpp_multiac-compact_zlib-domain", + "py3-cpp_multiac-compact_zlib-ip", + "py3-cpp_multiac-compact_zlib-ip-ssl", + "py3-cpp_multiac-multic_http-domain", + "py3-cpp_multiac-multic_http-ip", + "py3-cpp_multiac-multic_http-ip-ssl", + "py3-cpp_multiac-multic_zlib-domain", + "py3-cpp_multiac-multic_zlib-ip", + "py3-cpp_multiac-multic_zlib-ip-ssl", + "py3-cpp_multic-compact_http-domain", + "py3-cpp_multic-compact_http-ip", + "py3-cpp_multic-compact_http-ip-ssl", + "py3-cpp_multic_http-domain", + "py3-cpp_multic_http-ip", + "py3-cpp_multic_http-ip-ssl", + "py3-cpp_multih-header_http-domain", + "py3-cpp_multih-header_http-ip", + "py3-cpp_multih-header_http-ip-ssl", + "py3-cpp_multih_http-domain", + "py3-cpp_multih_http-ip", + "py3-cpp_multih_http-ip-ssl", + "py3-cpp_multij-json_http-domain", + "py3-cpp_multij-json_http-ip", + "py3-cpp_multij-json_http-ip-ssl", + "py3-cpp_multij_http-domain", + "py3-cpp_multij_http-ip", + "py3-cpp_multij_http-ip-ssl", + "py3-cpp_multi_http-domain", + "py3-cpp_multi_http-ip", + "py3-cpp_multi_http-ip-ssl", + "py3-dart_accel-binary_http-ip", + "py3-dart_accelc-compact_http-ip", + "py3-dart_binary_http-ip", + "py3-dart_compact_http-ip", + "py3-dart_json_http-ip", "py3-d_accel-binary_http-ip", "py3-d_accel-binary_http-ip-ssl", "py3-d_accelc-compact_http-ip", @@ -459,36 +1128,101 @@ "py3-d_compact_http-ip-ssl", "py3-d_json_http-ip", "py3-d_json_http-ip-ssl", - "py3-dart_accel-binary_http-ip", - "py3-dart_accelc-compact_http-ip", - "py3-dart_binary_http-ip", - "py3-dart_compact_http-ip", - "py3-dart_json_http-ip", "py3-hs_accel-binary_http-ip", "py3-hs_accelc-compact_http-ip", "py3-hs_binary_http-ip", "py3-hs_compact_http-ip", "py3-hs_header_http-ip", "py3-hs_json_http-ip", - "py3-java_accel-binary_http-ip", "py3-java_accel-binary_http-ip-ssl", - "py3-java_accelc-compact_http-ip", "py3-java_accelc-compact_http-ip-ssl", - "py3-java_binary_http-ip", "py3-java_binary_http-ip-ssl", - "py3-java_compact_http-ip", "py3-java_compact_http-ip-ssl", - "py3-java_json_http-ip", "py3-java_json_http-ip-ssl", + "py3-java_multi-binary_http-ip-ssl", + "py3-java_multia-binary_http-ip-ssl", + "py3-java_multia-multi_http-ip-ssl", + "py3-java_multiac-compact_http-ip-ssl", + "py3-java_multiac-multic_http-ip-ssl", + "py3-java_multic-compact_http-ip-ssl", + "py3-java_multic_http-ip-ssl", + "py3-java_multij-json_http-ip-ssl", + "py3-java_multij_http-ip-ssl", + "py3-java_multi_http-ip-ssl", "py3-lua_accel-binary_http-ip", "py3-lua_accelc-compact_http-ip", "py3-lua_binary_http-ip", "py3-lua_compact_http-ip", "py3-lua_json_http-ip", + "py3-netstd_accel-binary_buffered-ip", + "py3-netstd_accel-binary_buffered-ip-ssl", + "py3-netstd_accel-binary_framed-ip", + "py3-netstd_accel-binary_framed-ip-ssl", + "py3-netstd_accelc-compact_buffered-ip", + "py3-netstd_accelc-compact_buffered-ip-ssl", + "py3-netstd_accelc-compact_framed-ip", + "py3-netstd_accelc-compact_framed-ip-ssl", + "py3-netstd_binary_buffered-ip", + "py3-netstd_binary_buffered-ip-ssl", + "py3-netstd_binary_framed-ip", + "py3-netstd_binary_framed-ip-ssl", + "py3-netstd_compact_buffered-ip", + "py3-netstd_compact_buffered-ip-ssl", + "py3-netstd_compact_framed-ip", + "py3-netstd_compact_framed-ip-ssl", + "py3-netstd_json_buffered-ip", + "py3-netstd_json_buffered-ip-ssl", + "py3-netstd_json_framed-ip", + "py3-netstd_json_framed-ip-ssl", + "py3-nodejs_accel-binary_http-domain", + "py3-nodejs_accelc-compact_http-domain", + "py3-nodejs_binary_http-domain", + "py3-nodejs_compact_http-domain", + "py3-nodejs_header_http-domain", + "py3-nodejs_json_http-domain", + "py3-php_accel_buffered-ip", + "py3-php_accel_framed-ip", + "py3-php_binary-accel_buffered-ip", + "py3-php_binary-accel_framed-ip", + "py3-php_json_buffered-ip", + "py3-php_json_framed-ip", + "py3-rs_multia-multi_buffered-ip", + "py3-rs_multia-multi_framed-ip", + "py3-rs_multiac-multic_buffered-ip", + "py3-rs_multiac-multic_framed-ip", + "py3-rs_multic_buffered-ip", + "py3-rs_multic_framed-ip", + "py3-rs_multi_buffered-ip", + "py3-rs_multi_framed-ip", "rb-cpp_json_buffered-domain", "rb-cpp_json_buffered-ip", "rb-cpp_json_buffered-ip-ssl", "rb-cpp_json_framed-domain", "rb-cpp_json_framed-ip", - "rb-cpp_json_framed-ip-ssl" -] \ No newline at end of file + "rb-cpp_json_framed-ip-ssl", + "rb-netstd_accel-binary_buffered-ip", + "rb-netstd_accel-binary_buffered-ip-ssl", + "rb-netstd_accel-binary_framed-ip", + "rb-netstd_accel-binary_framed-ip-ssl", + "rb-netstd_binary_buffered-ip", + "rb-netstd_binary_buffered-ip-ssl", + "rb-netstd_binary_framed-ip", + "rb-netstd_binary_framed-ip-ssl", + "rb-netstd_compact_buffered-ip", + "rb-netstd_compact_buffered-ip-ssl", + "rb-netstd_compact_framed-ip", + "rb-netstd_compact_framed-ip-ssl", + "rb-netstd_json_buffered-ip", + "rb-netstd_json_buffered-ip-ssl", + "rb-netstd_json_framed-ip", + "rb-netstd_json_framed-ip-ssl", + "rs-netstd_binary_buffered-ip", + "rs-netstd_binary_framed-ip", + "rs-netstd_compact_buffered-ip", + "rs-netstd_compact_buffered-ip", + "rs-netstd_compact_framed-ip", + "rs-netstd_multi-binary_buffered-ip", + "rs-netstd_multi-binary_framed-ip", + "rs-netstd_multic-compact_buffered-ip", + "rs-netstd_multic-compact_framed-ip" +] diff --git a/test/lua/Makefile.am b/test/lua/Makefile.am index ed8c5aee8d1..b2683e1b968 100644 --- a/test/lua/Makefile.am +++ b/test/lua/Makefile.am @@ -28,4 +28,7 @@ stubs: ../ThriftTest.thrift $(THRIFT) precross: stubs clean-local: - $(RM) -r gen-lua + $(RM) -r gen-lua/ + +dist-hook: + $(RM) -r $(distdir)/gen-lua/ diff --git a/test/lua/test_basic_client.lua b/test/lua/test_basic_client.lua index 77d8d078aa4..11567d906b3 100644 --- a/test/lua/test_basic_client.lua +++ b/test/lua/test_basic_client.lua @@ -172,6 +172,9 @@ function testBasicClient(rawArgs) assertEqual(o.i32_thing, r.i32_thing, 'Failed testStruct 3') assertEqual(o.i64_thing, r.i64_thing, 'Failed testStruct 4') + -- oneway + client:testOneway(3) + -- TODO add list map set exception etc etc end diff --git a/test/lua/test_basic_server.lua b/test/lua/test_basic_server.lua index acd2d79b823..20ac407c8da 100644 --- a/test/lua/test_basic_server.lua +++ b/test/lua/test_basic_server.lua @@ -66,6 +66,10 @@ function TestHandler:testStruct(thing) return thing end +function TestHandler:testOneway(secondsToSleep) + print("testOneway secondsToSleep:", secondsToSleep) +end + -------------------------------------------------------------------------------- -- Test local server @@ -132,6 +136,7 @@ function testBasicServer(rawArgs) protocolFactory = prot_factory } assert(server, 'Failed to create server') + server:setExceptionHandler(function (err) error(err) end) -- Serve server:serve() diff --git a/test/netcore/Client/.gitignore b/test/netcore/Client/.gitignore deleted file mode 100644 index 67d55106a4b..00000000000 --- a/test/netcore/Client/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -# ignore for autogenerated files -/ThriftTest diff --git a/test/netcore/Server/.gitignore b/test/netcore/Server/.gitignore deleted file mode 100644 index 67d55106a4b..00000000000 --- a/test/netcore/Server/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -# ignore for autogenerated files -/ThriftTest diff --git a/test/netcore/Client/Client.csproj b/test/netstd/Client/Client.csproj similarity index 55% rename from test/netcore/Client/Client.csproj rename to test/netstd/Client/Client.csproj index f16af390a52..4ed57cbd947 100644 --- a/test/netcore/Client/Client.csproj +++ b/test/netstd/Client/Client.csproj @@ -1,6 +1,25 @@ + + - netcoreapp2.0 + netcoreapp3.1 Client Client Exe @@ -12,20 +31,20 @@ false - + - + - + - - - + + + diff --git a/test/netstd/Client/Performance/PerformanceTests.cs b/test/netstd/Client/Performance/PerformanceTests.cs new file mode 100644 index 00000000000..2c79aa6ef22 --- /dev/null +++ b/test/netstd/Client/Performance/PerformanceTests.cs @@ -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. + +using System; +using System.Collections.Generic; +using System.Text; +using ThriftTest; +using Thrift.Collections; +using Thrift; +using Thrift.Protocol; +using System.Threading; +using Thrift.Transport.Client; +using System.Threading.Tasks; +using System.Diagnostics; +using Thrift.Transport; + +namespace Client.Tests +{ + public class PerformanceTests + { + private CancellationTokenSource Cancel; + private CrazyNesting Testdata; + private TMemoryBufferTransport MemBuffer; + private TTransport Transport; + private LayeredChoice Layered; + private readonly TConfiguration Configuration = new TConfiguration(); + + internal static int Execute() + { + var instance = new PerformanceTests(); + instance.ProtocolPeformanceTestAsync().Wait(); + + // debug only + if (Debugger.IsAttached) + { + Console.Write("Hit ENTER ..."); + Console.ReadKey(); + } + + return 0; + } + + public PerformanceTests() + { + Configuration.MaxFrameSize = Configuration.MaxMessageSize; // default frame size is too small for this test + } + + private async Task ProtocolPeformanceTestAsync() + { + Console.WriteLine("Setting up for ProtocolPeformanceTestAsync ..."); + Cancel = new CancellationTokenSource(); + Testdata = TestDataFactory.CreateCrazyNesting(); + + foreach (var layered in Enum.GetValues(typeof(LayeredChoice))) + { + Layered = (LayeredChoice)layered; + await RunTestAsync(async (bool b) => { return await GenericProtocolFactory(b); }); + await RunTestAsync(async (bool b) => { return await GenericProtocolFactory(b); }); + await RunTestAsync(async (bool b) => { return await GenericProtocolFactory(b); }); + } + } + + private Task GenericProtocolFactory(bool forWrite) + where T : TProtocol + { + var oldTrans = Transport; + try + { + // read happens after write here, so let's take over the written bytes + if (forWrite) + MemBuffer = new TMemoryBufferTransport(Configuration); + else + MemBuffer = new TMemoryBufferTransport(MemBuffer.GetBuffer(), Configuration); + + // layered transports anyone? + switch (Layered) + { + case LayeredChoice.None: + Transport = MemBuffer; + break; + case LayeredChoice.Framed: + Transport = new TFramedTransport(MemBuffer); + break; + case LayeredChoice.Buffered: + Transport = new TBufferedTransport(MemBuffer); + break; + default: + Debug.Assert(false); + break; + } + + if (!Transport.IsOpen) + Transport.OpenAsync().Wait(); + + var instance = (T)Activator.CreateInstance(typeof(T), Transport); + return Task.FromResult(instance); + } + finally + { + if (oldTrans is IDisposable) + (oldTrans as IDisposable).Dispose(); + } + } + + private string GetProtocolTransportName(TProtocol proto) + { + var name = Transport.GetType().Name; + if (name.Equals(MemBuffer.GetType().Name)) + name = string.Empty; + else + name = " + " + name; + + name = proto.GetType().Name + name; + return name; + } + + + private async Task RunTestAsync(Func> factory) + { + var stop = new Stopwatch(); + + var proto = await factory(true); + stop.Start(); + await Testdata.WriteAsync(proto, Cancel.Token); + await Transport.FlushAsync(Cancel.Token); + stop.Stop(); + Console.WriteLine("RunTestAsync({0}): write = {1} msec", + GetProtocolTransportName(proto), + stop.ElapsedMilliseconds); + + var restored = new CrazyNesting(); + proto = await factory(false); + stop.Start(); + await restored.ReadAsync(proto, Cancel.Token); + stop.Stop(); + Console.WriteLine("RunTestAsync({0}): read = {1} msec", + GetProtocolTransportName(proto), + stop.ElapsedMilliseconds); + } + + } +} diff --git a/test/netstd/Client/Performance/TestDataFactory.cs b/test/netstd/Client/Performance/TestDataFactory.cs new file mode 100644 index 00000000000..98962857cb5 --- /dev/null +++ b/test/netstd/Client/Performance/TestDataFactory.cs @@ -0,0 +1,154 @@ +// Licensed to the Apache Software Foundation(ASF) under one +// or more contributor license agreements.See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership.The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +using System; +using System.Collections.Generic; +using System.Text; +using ThriftTest; +using Thrift.Collections; + +namespace Client.Tests +{ + + static class TestDataFactory + { + public static CrazyNesting CreateCrazyNesting(int count = 10) + { + if (count <= 0) + return null; + + return new CrazyNesting() + { + Binary_field = CreateBytesArray(count), + List_field = CreateListField(count), + Set_field = CreateSetField(count), + String_field = string.Format("data level {0}", count) + }; + } + + private static THashSet CreateSetField(int count) + { + var retval = new THashSet(); + for (var i = 0; i < count; ++i) + retval.Add(CreateInsanity(count)); + return retval; + } + + private static Insanity CreateInsanity(int count) + { + return new Insanity() + { + UserMap = CreateUserMap(count), + Xtructs = CreateXtructs(count) + }; + } + + private static List CreateXtructs(int count) + { + var retval = new List(); + for (var i = 0; i < count; ++i) + retval.Add(CreateXtruct(count)); + return retval; + } + + private static Xtruct CreateXtruct(int count) + { + return new Xtruct() + { + Byte_thing = (sbyte)(count % 128), + I32_thing = count, + I64_thing = count, + String_thing = string.Format("data level {0}", count) + }; + } + + private static Dictionary CreateUserMap(int count) + { + var retval = new Dictionary(); + retval.Add(Numberz.ONE, count); + retval.Add(Numberz.TWO, count); + retval.Add(Numberz.THREE, count); + retval.Add(Numberz.FIVE, count); + retval.Add(Numberz.SIX, count); + retval.Add(Numberz.EIGHT, count); + return retval; + } + + private static List, Dictionary>>>>> CreateListField(int count) + { + var retval = new List, Dictionary>>>>>(); + for (var i = 0; i < count; ++i) + retval.Add(CreateListFieldData(count)); + return retval; + } + + private static Dictionary, Dictionary>>>> CreateListFieldData(int count) + { + var retval = new Dictionary, Dictionary>>>>(); + for (var i = 0; i < count; ++i) + retval.Add( CreateIntHashSet(count), CreateListFieldDataDict(count)); + return retval; + } + + private static THashSet CreateIntHashSet(int count) + { + var retval = new THashSet(); + for (var i = 0; i < count; ++i) + retval.Add(i); + return retval; + } + + private static Dictionary>>> CreateListFieldDataDict(int count) + { + var retval = new Dictionary>>>(); + for (var i = 0; i < count; ++i) + retval.Add(i, CreateListFieldDataDictValue(count)); + return retval; + } + + private static THashSet>> CreateListFieldDataDictValue(int count) + { + var retval = new THashSet>>(); + for (var i = 0; i < count; ++i) + retval.Add( CreateListFieldDataDictValueList(count)); + return retval; + } + + private static List> CreateListFieldDataDictValueList(int count) + { + var retval = new List>(); + for (var i = 0; i < count; ++i) + retval.Add(CreateListFieldDataDictValueListDict(count)); + return retval; + } + + private static Dictionary CreateListFieldDataDictValueListDict(int count) + { + var retval = new Dictionary(); + retval.Add(CreateInsanity(count), string.Format("data level {0}", count)); + return retval; + } + + private static byte[] CreateBytesArray(int count) + { + var retval = new byte[count]; + for (var i = 0; i < count; ++i) + retval[i] = (byte)(i % 0xFF); + return retval; + } + } +} diff --git a/test/netcore/Client/Program.cs b/test/netstd/Client/Program.cs similarity index 67% rename from test/netcore/Client/Program.cs rename to test/netstd/Client/Program.cs index 72139d9def5..0fe2ccee26e 100644 --- a/test/netcore/Client/Program.cs +++ b/test/netstd/Client/Program.cs @@ -17,6 +17,7 @@ using System; using System.Collections.Generic; +using System.Linq; using ThriftTest; namespace Client @@ -34,33 +35,30 @@ public static int Main(string[] args) Console.WriteLine("Failed to grow scroll-back buffer"); } - // split mode and options - var subArgs = new List(args); - var firstArg = string.Empty; - if (subArgs.Count > 0) - { - firstArg = subArgs[0]; - subArgs.RemoveAt(0); - } - - // run whatever mode is choosen - switch(firstArg) + // run whatever mode is choosen, default to test impl + var argslist = new List(args); + switch (argslist.FirstOrDefault()) { - case "client": - return TestClient.Execute(subArgs); + case "client": // crosstest wants to pass this, so just emit a hint and ignore + Console.WriteLine("Hint: The 'client' argument is no longer required."); + argslist.RemoveAt(0); + return TestClient.Execute(argslist); + case "--performance": + case "--performance-test": + return Tests.PerformanceTests.Execute(); case "--help": PrintHelp(); return 0; default: - PrintHelp(); - return -1; + return TestClient.Execute(argslist); } } private static void PrintHelp() { Console.WriteLine("Usage:"); - Console.WriteLine(" Client client [options]'"); + Console.WriteLine(" Client [options]"); + Console.WriteLine(" Client --performance-test"); Console.WriteLine(" Client --help"); Console.WriteLine(""); diff --git a/test/netcore/Client/Properties/AssemblyInfo.cs b/test/netstd/Client/Properties/AssemblyInfo.cs similarity index 100% rename from test/netcore/Client/Properties/AssemblyInfo.cs rename to test/netstd/Client/Properties/AssemblyInfo.cs diff --git a/test/netcore/Client/TestClient.cs b/test/netstd/Client/TestClient.cs similarity index 67% rename from test/netcore/Client/TestClient.cs rename to test/netstd/Client/TestClient.cs index 8be198c692a..ec90250559c 100644 --- a/test/netcore/Client/TestClient.cs +++ b/test/netstd/Client/TestClient.cs @@ -28,35 +28,60 @@ using System.Text; using System.Threading; using System.Threading.Tasks; +using Thrift; using Thrift.Collections; -using Thrift.Protocols; -using Thrift.Transports; -using Thrift.Transports.Client; +using Thrift.Protocol; +using Thrift.Transport; +using Thrift.Transport.Client; namespace ThriftTest { + internal enum ProtocolChoice + { + Binary, + Compact, + Json + } + + // it does not make much sense to use buffered when we already use framed + internal enum LayeredChoice + { + None, + Buffered, + Framed + } + + internal enum TransportChoice + { + Socket, + TlsSocket, + Http, + NamedPipe + } + public class TestClient { private class TestParams { public int numIterations = 1; - public IPAddress host = IPAddress.Any; + public string host = "localhost"; public int port = 9090; public int numThreads = 1; public string url; public string pipe; - public bool buffered; - public bool framed; - public string protocol; - public bool encrypted = false; + public LayeredChoice layered = LayeredChoice.None; + public ProtocolChoice protocol = ProtocolChoice.Binary; + public TransportChoice transport = TransportChoice.Socket; + private readonly TConfiguration Configuration = null; // or new TConfiguration() if needed - internal void Parse( List args) + internal void Parse(List args) { for (var i = 0; i < args.Count; ++i) { if (args[i] == "-u") { url = args[++i]; + transport = TransportChoice.Http; } else if (args[i] == "-n") { @@ -65,26 +90,28 @@ internal void Parse( List args) else if (args[i].StartsWith("--pipe=")) { pipe = args[i].Substring(args[i].IndexOf("=") + 1); - Console.WriteLine("Using named pipes transport"); + transport = TransportChoice.NamedPipe; } else if (args[i].StartsWith("--host=")) { // check there for ipaddress - host = new IPAddress(Encoding.Unicode.GetBytes(args[i].Substring(args[i].IndexOf("=") + 1))); + host = args[i].Substring(args[i].IndexOf("=") + 1); + if (transport != TransportChoice.TlsSocket) + transport = TransportChoice.Socket; } else if (args[i].StartsWith("--port=")) { port = int.Parse(args[i].Substring(args[i].IndexOf("=") + 1)); + if (transport != TransportChoice.TlsSocket) + transport = TransportChoice.Socket; } else if (args[i] == "-b" || args[i] == "--buffered" || args[i] == "--transport=buffered") { - buffered = true; - Console.WriteLine("Using buffered sockets"); + layered = LayeredChoice.Buffered; } else if (args[i] == "-f" || args[i] == "--framed" || args[i] == "--transport=framed") { - framed = true; - Console.WriteLine("Using framed transport"); + layered = LayeredChoice.Framed; } else if (args[i] == "-t") { @@ -92,29 +119,80 @@ internal void Parse( List args) } else if (args[i] == "--binary" || args[i] == "--protocol=binary") { - protocol = "binary"; - Console.WriteLine("Using binary protocol"); + protocol = ProtocolChoice.Binary; } else if (args[i] == "--compact" || args[i] == "--protocol=compact") { - protocol = "compact"; - Console.WriteLine("Using compact protocol"); + protocol = ProtocolChoice.Compact; } else if (args[i] == "--json" || args[i] == "--protocol=json") { - protocol = "json"; - Console.WriteLine("Using JSON protocol"); + protocol = ProtocolChoice.Json; } else if (args[i] == "--ssl") { - encrypted = true; - Console.WriteLine("Using encrypted transport"); + transport = TransportChoice.TlsSocket; + } + else if (args[i] == "--help") + { + PrintOptionsHelp(); + return; } else { - //throw new ArgumentException(args[i]); + Console.WriteLine("Invalid argument: {0}", args[i]); + PrintOptionsHelp(); + return; } } + + switch (transport) + { + case TransportChoice.Socket: + Console.WriteLine("Using socket transport"); + break; + case TransportChoice.TlsSocket: + Console.WriteLine("Using encrypted transport"); + break; + case TransportChoice.Http: + Console.WriteLine("Using HTTP transport"); + break; + case TransportChoice.NamedPipe: + Console.WriteLine("Using named pipes transport"); + break; + default: // unhandled case + Debug.Assert(false); + break; + } + + switch (layered) + { + case LayeredChoice.Framed: + Console.WriteLine("Using framed transport"); + break; + case LayeredChoice.Buffered: + Console.WriteLine("Using buffered transport"); + break; + default: // unhandled case? + Debug.Assert(layered == LayeredChoice.None); + break; + } + + switch (protocol) + { + case ProtocolChoice.Binary: + Console.WriteLine("Using binary protocol"); + break; + case ProtocolChoice.Compact: + Console.WriteLine("Using compact protocol"); + break; + case ProtocolChoice.Json: + Console.WriteLine("Using JSON protocol"); + break; + default: // unhandled case? + Debug.Assert(false); + break; + } } private static X509Certificate2 GetClientCert() @@ -143,74 +221,78 @@ private static X509Certificate2 GetClientCert() { throw new FileNotFoundException($"Cannot find file: {clientCertName}"); } - + var cert = new X509Certificate2(existingPath, "thrift"); return cert; } - - public TClientTransport CreateTransport() + + public TTransport CreateTransport() { - if (url == null) + // endpoint transport + TTransport trans = null; + + switch (transport) { - // endpoint transport - TClientTransport trans = null; + case TransportChoice.Http: + Debug.Assert(url != null); + trans = new THttpTransport(new Uri(url), Configuration); + break; - if (pipe != null) - { - trans = new TNamedPipeClientTransport(pipe); - } - else - { - if (encrypted) - { - var cert = GetClientCert(); - - if (cert == null || !cert.HasPrivateKey) - { - throw new InvalidOperationException("Certificate doesn't contain private key"); - } - - trans = new TTlsSocketClientTransport(host, port, 0, cert, - (sender, certificate, chain, errors) => true, - null, SslProtocols.Tls | SslProtocols.Tls11 | SslProtocols.Tls12); - } - else + case TransportChoice.NamedPipe: + Debug.Assert(pipe != null); + trans = new TNamedPipeTransport(pipe,Configuration); + break; + + case TransportChoice.TlsSocket: + var cert = GetClientCert(); + if (cert == null || !cert.HasPrivateKey) { - trans = new TSocketClientTransport(host, port); + throw new InvalidOperationException("Certificate doesn't contain private key"); } - } - - // layered transport - if (buffered) - { - trans = new TBufferedClientTransport(trans); - } - if (framed) - { - trans = new TFramedClientTransport(trans); - } + trans = new TTlsSocketTransport(host, port, Configuration, 0, + cert, + (sender, certificate, chain, errors) => true, + null, SslProtocols.Tls | SslProtocols.Tls11 | SslProtocols.Tls12); + break; - return trans; + case TransportChoice.Socket: + default: + trans = new TSocketTransport(host, port, Configuration); + break; } - return new THttpClientTransport(new Uri(url), null); - } - public TProtocol CreateProtocol(TClientTransport transport) - { - if (protocol == "compact") + // layered transport + switch (layered) { - return new TCompactProtocol(transport); + case LayeredChoice.Buffered: + trans = new TBufferedTransport(trans); + break; + case LayeredChoice.Framed: + trans = new TFramedTransport(trans); + break; + default: + Debug.Assert(layered == LayeredChoice.None); + break; } - if (protocol == "json") + return trans; + } + + public TProtocol CreateProtocol(TTransport transport) + { + switch (protocol) { - return new TJsonProtocol(transport); + case ProtocolChoice.Compact: + return new TCompactProtocol(transport); + case ProtocolChoice.Json: + return new TJsonProtocol(transport); + case ProtocolChoice.Binary: + default: + return new TBinaryProtocol(transport); } - - return new TBinaryProtocol(transport); } } @@ -223,7 +305,7 @@ public TProtocol CreateProtocol(TClientTransport transport) private class ClientTest { - private readonly TClientTransport transport; + private readonly TTransport transport; private readonly ThriftTest.Client client; private readonly int numIterations; private bool done; @@ -239,8 +321,6 @@ public ClientTest(TestParams param) public void Execute() { - var token = CancellationToken.None; - if (done) { Console.WriteLine("Execute called more than once"); @@ -252,16 +332,14 @@ public void Execute() try { if (!transport.IsOpen) - { - transport.OpenAsync(token).GetAwaiter().GetResult(); - } + transport.OpenAsync(MakeTimeoutToken()).GetAwaiter().GetResult(); } catch (TTransportException ex) { Console.WriteLine("*** FAILED ***"); Console.WriteLine("Connect failed: " + ex.Message); ReturnCode |= ErrorUnknown; - Console.WriteLine(ex.Message + " ST: " + ex.StackTrace); + Console.WriteLine(ex.Message + "\n" + ex.StackTrace); continue; } catch (Exception ex) @@ -269,7 +347,7 @@ public void Execute() Console.WriteLine("*** FAILED ***"); Console.WriteLine("Connect failed: " + ex.Message); ReturnCode |= ErrorUnknown; - Console.WriteLine(ex.Message + " ST: " + ex.StackTrace); + Console.WriteLine(ex.Message + "\n" + ex.StackTrace); continue; } @@ -280,7 +358,7 @@ public void Execute() catch (Exception ex) { Console.WriteLine("*** FAILED ***"); - Console.WriteLine(ex.Message + " ST: " + ex.StackTrace); + Console.WriteLine(ex.Message + "\n" + ex.StackTrace); ReturnCode |= ErrorUnknown; } } @@ -291,7 +369,7 @@ public void Execute() catch (Exception ex) { Console.WriteLine("Error while closing transport"); - Console.WriteLine(ex.Message + " ST: " + ex.StackTrace); + Console.WriteLine(ex.Message + "\n" + ex.StackTrace); } done = true; } @@ -325,8 +403,8 @@ public static int Execute(List args) catch (Exception ex) { Console.WriteLine("*** FAILED ***"); - Console.WriteLine("Error while parsing arguments"); - Console.WriteLine(ex.Message + " ST: " + ex.StackTrace); + Console.WriteLine("Error while parsing arguments"); + Console.WriteLine(ex.Message + "\n" + ex.StackTrace); return ErrorUnknown; } @@ -350,7 +428,7 @@ public static int Execute(List args) { Console.WriteLine("*** FAILED ***"); Console.WriteLine("Unexpected error"); - Console.WriteLine(outerEx.Message + " ST: " + outerEx.StackTrace); + Console.WriteLine(outerEx.Message + "\n" + outerEx.StackTrace); return ErrorUnknown; } } @@ -360,15 +438,46 @@ public static string BytesToHex(byte[] data) return BitConverter.ToString(data).Replace("-", string.Empty); } - public static byte[] PrepareTestData(bool randomDist) + + public enum BinaryTestSize { - var retval = new byte[0x100]; - var initLen = Math.Min(0x100, retval.Length); + Empty, // Edge case: the zero-length empty binary + Normal, // Fairly small array of usual size (256 bytes) + Large, // Large writes/reads may cause range check errors + PipeWriteLimit, // Windows Limit: Pipe write operations across a network are limited to 65,535 bytes per write. + FifteenMB // that's quite a bit of data + }; + + public static byte[] PrepareTestData(bool randomDist, BinaryTestSize testcase) + { + int amount; + switch (testcase) + { + case BinaryTestSize.Empty: + amount = 0; + break; + case BinaryTestSize.Normal: + amount = 0x100; + break; + case BinaryTestSize.Large: + amount = 0x8000 + 128; + break; + case BinaryTestSize.PipeWriteLimit: + amount = 0xFFFF + 128; + break; + case BinaryTestSize.FifteenMB: + amount = 15 * 1024 * 1024; + break; + default: + throw new ArgumentException(nameof(testcase)); + } + + var retval = new byte[amount]; // linear distribution, unless random is requested if (!randomDist) { - for (var i = 0; i < initLen; ++i) + for (var i = 0; i < retval.Length; ++i) { retval[i] = (byte)i; } @@ -376,37 +485,30 @@ public static byte[] PrepareTestData(bool randomDist) } // random distribution - for (var i = 0; i < initLen; ++i) - { - retval[i] = (byte)0; - } var rnd = new Random(); - for (var i = 1; i < initLen; ++i) + for (var i = 1; i < retval.Length; ++i) { - while (true) - { - var nextPos = rnd.Next() % initLen; - if (retval[nextPos] == 0) - { - retval[nextPos] = (byte)i; - break; - } - } + retval[i] = (byte)rnd.Next(0x100); } return retval; } + private static CancellationToken MakeTimeoutToken(int msec = 5000) + { + var token = new CancellationTokenSource(msec); + return token.Token; + } + public static async Task ExecuteClientTestAsync(ThriftTest.Client client) { - var token = CancellationToken.None; var returnCode = 0; Console.Write("testVoid()"); - await client.testVoidAsync(token); + await client.testVoidAsync(MakeTimeoutToken()); Console.WriteLine(" = void"); Console.Write("testString(\"Test\")"); - var s = await client.testStringAsync("Test", token); + var s = await client.testStringAsync("Test", MakeTimeoutToken()); Console.WriteLine(" = \"" + s + "\""); if ("Test" != s) { @@ -415,7 +517,7 @@ public static async Task ExecuteClientTestAsync(ThriftTest.Client client) } Console.Write("testBool(true)"); - var t = await client.testBoolAsync((bool)true, token); + var t = await client.testBoolAsync((bool)true, MakeTimeoutToken()); Console.WriteLine(" = " + t); if (!t) { @@ -423,7 +525,7 @@ public static async Task ExecuteClientTestAsync(ThriftTest.Client client) returnCode |= ErrorBaseTypes; } Console.Write("testBool(false)"); - var f = await client.testBoolAsync((bool)false, token); + var f = await client.testBoolAsync((bool)false, MakeTimeoutToken()); Console.WriteLine(" = " + f); if (f) { @@ -432,7 +534,7 @@ public static async Task ExecuteClientTestAsync(ThriftTest.Client client) } Console.Write("testByte(1)"); - var i8 = await client.testByteAsync((sbyte)1, token); + var i8 = await client.testByteAsync((sbyte)1, MakeTimeoutToken()); Console.WriteLine(" = " + i8); if (1 != i8) { @@ -441,7 +543,7 @@ public static async Task ExecuteClientTestAsync(ThriftTest.Client client) } Console.Write("testI32(-1)"); - var i32 = await client.testI32Async(-1, token); + var i32 = await client.testI32Async(-1, MakeTimeoutToken()); Console.WriteLine(" = " + i32); if (-1 != i32) { @@ -450,7 +552,7 @@ public static async Task ExecuteClientTestAsync(ThriftTest.Client client) } Console.Write("testI64(-34359738368)"); - var i64 = await client.testI64Async(-34359738368, token); + var i64 = await client.testI64Async(-34359738368, MakeTimeoutToken()); Console.WriteLine(" = " + i64); if (-34359738368 != i64) { @@ -460,7 +562,7 @@ public static async Task ExecuteClientTestAsync(ThriftTest.Client client) // TODO: Validate received message Console.Write("testDouble(5.325098235)"); - var dub = await client.testDoubleAsync(5.325098235, token); + var dub = await client.testDoubleAsync(5.325098235, MakeTimeoutToken()); Console.WriteLine(" = " + dub); if (5.325098235 != dub) { @@ -468,7 +570,7 @@ public static async Task ExecuteClientTestAsync(ThriftTest.Client client) returnCode |= ErrorBaseTypes; } Console.Write("testDouble(-0.000341012439638598279)"); - dub = await client.testDoubleAsync(-0.000341012439638598279, token); + dub = await client.testDoubleAsync(-0.000341012439638598279, MakeTimeoutToken()); Console.WriteLine(" = " + dub); if (-0.000341012439638598279 != dub) { @@ -476,32 +578,39 @@ public static async Task ExecuteClientTestAsync(ThriftTest.Client client) returnCode |= ErrorBaseTypes; } - var binOut = PrepareTestData(true); - Console.Write("testBinary(" + BytesToHex(binOut) + ")"); - try + // testBinary() + foreach(BinaryTestSize binTestCase in Enum.GetValues(typeof(BinaryTestSize))) { - var binIn = await client.testBinaryAsync(binOut, token); - Console.WriteLine(" = " + BytesToHex(binIn)); - if (binIn.Length != binOut.Length) + var binOut = PrepareTestData(true, binTestCase); + + Console.Write("testBinary({0} bytes)", binOut.Length); + try { - Console.WriteLine("*** FAILED ***"); - returnCode |= ErrorBaseTypes; - } - for (var ofs = 0; ofs < Math.Min(binIn.Length, binOut.Length); ++ofs) - if (binIn[ofs] != binOut[ofs]) + var binIn = await client.testBinaryAsync(binOut, MakeTimeoutToken()); + Console.WriteLine(" = {0} bytes", binIn.Length); + if (binIn.Length != binOut.Length) { Console.WriteLine("*** FAILED ***"); returnCode |= ErrorBaseTypes; } - } - catch (Thrift.TApplicationException ex) - { - Console.WriteLine("*** FAILED ***"); - returnCode |= ErrorBaseTypes; - Console.WriteLine(ex.Message + " ST: " + ex.StackTrace); + for (var ofs = 0; ofs < Math.Min(binIn.Length, binOut.Length); ++ofs) + { + if (binIn[ofs] != binOut[ofs]) + { + Console.WriteLine("*** FAILED ***"); + returnCode |= ErrorBaseTypes; + } + } + } + catch (Thrift.TApplicationException ex) + { + Console.WriteLine("*** FAILED ***"); + returnCode |= ErrorBaseTypes; + Console.WriteLine(ex.Message + "\n" + ex.StackTrace); + } } - // binary equals? only with hashcode option enabled ... + // CrazyNesting Console.WriteLine("Test CrazyNesting"); var one = new CrazyNesting(); var two = new CrazyNesting(); @@ -515,27 +624,30 @@ public static async Task ExecuteClientTestAsync(ThriftTest.Client client) { Console.WriteLine("*** FAILED ***"); returnCode |= ErrorContainers; - throw new Exception("CrazyNesting.Equals failed"); } } // TODO: Validate received message Console.Write("testStruct({\"Zero\", 1, -3, -5})"); - var o = new Xtruct(); - o.String_thing = "Zero"; - o.Byte_thing = (sbyte)1; - o.I32_thing = -3; - o.I64_thing = -5; - var i = await client.testStructAsync(o, token); + var o = new Xtruct + { + String_thing = "Zero", + Byte_thing = (sbyte)1, + I32_thing = -3, + I64_thing = -5 + }; + var i = await client.testStructAsync(o, MakeTimeoutToken()); Console.WriteLine(" = {\"" + i.String_thing + "\", " + i.Byte_thing + ", " + i.I32_thing + ", " + i.I64_thing + "}"); // TODO: Validate received message Console.Write("testNest({1, {\"Zero\", 1, -3, -5}, 5})"); - var o2 = new Xtruct2(); - o2.Byte_thing = (sbyte)1; - o2.Struct_thing = o; - o2.I32_thing = 5; - var i2 = await client.testNestAsync(o2, token); + var o2 = new Xtruct2 + { + Byte_thing = (sbyte)1, + Struct_thing = o, + I32_thing = 5 + }; + var i2 = await client.testNestAsync(o2, MakeTimeoutToken()); i = i2.Struct_thing; Console.WriteLine(" = {" + i2.Byte_thing + ", {\"" + i.String_thing + "\", " + i.Byte_thing + ", " + i.I32_thing + ", " + i.I64_thing + "}, " + i2.I32_thing + "}"); @@ -560,7 +672,7 @@ public static async Task ExecuteClientTestAsync(ThriftTest.Client client) } Console.Write("})"); - var mapin = await client.testMapAsync(mapout, token); + var mapin = await client.testMapAsync(mapout, MakeTimeoutToken()); Console.Write(" = {"); first = true; @@ -600,7 +712,7 @@ public static async Task ExecuteClientTestAsync(ThriftTest.Client client) } Console.Write("})"); - var listin = await client.testListAsync(listout, token); + var listin = await client.testListAsync(listout, MakeTimeoutToken()); Console.Write(" = {"); first = true; @@ -641,7 +753,7 @@ public static async Task ExecuteClientTestAsync(ThriftTest.Client client) } Console.Write("})"); - var setin = await client.testSetAsync(setout, token); + var setin = await client.testSetAsync(setout, MakeTimeoutToken()); Console.Write(" = {"); first = true; @@ -661,7 +773,7 @@ public static async Task ExecuteClientTestAsync(ThriftTest.Client client) Console.Write("testEnum(ONE)"); - var ret = await client.testEnumAsync(Numberz.ONE, token); + var ret = await client.testEnumAsync(Numberz.ONE, MakeTimeoutToken()); Console.WriteLine(" = " + ret); if (Numberz.ONE != ret) { @@ -670,7 +782,7 @@ public static async Task ExecuteClientTestAsync(ThriftTest.Client client) } Console.Write("testEnum(TWO)"); - ret = await client.testEnumAsync(Numberz.TWO, token); + ret = await client.testEnumAsync(Numberz.TWO, MakeTimeoutToken()); Console.WriteLine(" = " + ret); if (Numberz.TWO != ret) { @@ -679,7 +791,7 @@ public static async Task ExecuteClientTestAsync(ThriftTest.Client client) } Console.Write("testEnum(THREE)"); - ret = await client.testEnumAsync(Numberz.THREE, token); + ret = await client.testEnumAsync(Numberz.THREE, MakeTimeoutToken()); Console.WriteLine(" = " + ret); if (Numberz.THREE != ret) { @@ -688,7 +800,7 @@ public static async Task ExecuteClientTestAsync(ThriftTest.Client client) } Console.Write("testEnum(FIVE)"); - ret = await client.testEnumAsync(Numberz.FIVE, token); + ret = await client.testEnumAsync(Numberz.FIVE, MakeTimeoutToken()); Console.WriteLine(" = " + ret); if (Numberz.FIVE != ret) { @@ -697,7 +809,7 @@ public static async Task ExecuteClientTestAsync(ThriftTest.Client client) } Console.Write("testEnum(EIGHT)"); - ret = await client.testEnumAsync(Numberz.EIGHT, token); + ret = await client.testEnumAsync(Numberz.EIGHT, MakeTimeoutToken()); Console.WriteLine(" = " + ret); if (Numberz.EIGHT != ret) { @@ -706,7 +818,7 @@ public static async Task ExecuteClientTestAsync(ThriftTest.Client client) } Console.Write("testTypedef(309858235082523)"); - var uid = await client.testTypedefAsync(309858235082523L, token); + var uid = await client.testTypedefAsync(309858235082523L, MakeTimeoutToken()); Console.WriteLine(" = " + uid); if (309858235082523L != uid) { @@ -716,7 +828,7 @@ public static async Task ExecuteClientTestAsync(ThriftTest.Client client) // TODO: Validate received message Console.Write("testMapMap(1)"); - var mm = await client.testMapMapAsync(1, token); + var mm = await client.testMapMapAsync(1, MakeTimeoutToken()); Console.Write(" = {"); foreach (var key in mm.Keys) { @@ -731,18 +843,26 @@ public static async Task ExecuteClientTestAsync(ThriftTest.Client client) Console.WriteLine("}"); // TODO: Validate received message - var insane = new Insanity(); - insane.UserMap = new Dictionary(); - insane.UserMap[Numberz.FIVE] = 5000L; - var truck = new Xtruct(); - truck.String_thing = "Truck"; - truck.Byte_thing = (sbyte)8; - truck.I32_thing = 8; - truck.I64_thing = 8; - insane.Xtructs = new List(); - insane.Xtructs.Add(truck); + var insane = new Insanity + { + UserMap = new Dictionary + { + [Numberz.FIVE] = 5000L + } + }; + var truck = new Xtruct + { + String_thing = "Truck", + Byte_thing = (sbyte)8, + I32_thing = 8, + I64_thing = 8 + }; + insane.Xtructs = new List + { + truck + }; Console.Write("testInsanity()"); - var whoa = await client.testInsanityAsync(insane, token); + var whoa = await client.testInsanityAsync(insane, MakeTimeoutToken()); Console.Write(" = {"); foreach (var key in whoa.Keys) { @@ -795,8 +915,10 @@ public static async Task ExecuteClientTestAsync(ThriftTest.Client client) sbyte arg0 = 1; var arg1 = 2; var arg2 = long.MaxValue; - var multiDict = new Dictionary(); - multiDict[1] = "one"; + var multiDict = new Dictionary + { + [1] = "one" + }; var tmpMultiDict = new List(); foreach (var pair in multiDict) @@ -805,14 +927,14 @@ public static async Task ExecuteClientTestAsync(ThriftTest.Client client) var arg4 = Numberz.FIVE; long arg5 = 5000000; Console.Write("Test Multi(" + arg0 + "," + arg1 + "," + arg2 + ",{" + string.Join(",", tmpMultiDict) + "}," + arg4 + "," + arg5 + ")"); - var multiResponse = await client.testMultiAsync(arg0, arg1, arg2, multiDict, arg4, arg5, token); + var multiResponse = await client.testMultiAsync(arg0, arg1, arg2, multiDict, arg4, arg5, MakeTimeoutToken()); Console.Write(" = Xtruct(byte_thing:" + multiResponse.Byte_thing + ",String_thing:" + multiResponse.String_thing + ",i32_thing:" + multiResponse.I32_thing + ",i64_thing:" + multiResponse.I64_thing + ")\n"); try { Console.WriteLine("testException(\"Xception\")"); - await client.testExceptionAsync("Xception", token); + await client.testExceptionAsync("Xception", MakeTimeoutToken()); Console.WriteLine("*** FAILED ***"); returnCode |= ErrorExceptions; } @@ -828,12 +950,12 @@ public static async Task ExecuteClientTestAsync(ThriftTest.Client client) { Console.WriteLine("*** FAILED ***"); returnCode |= ErrorExceptions; - Console.WriteLine(ex.Message + " ST: " + ex.StackTrace); + Console.WriteLine(ex.Message + "\n" + ex.StackTrace); } try { Console.WriteLine("testException(\"TException\")"); - await client.testExceptionAsync("TException", token); + await client.testExceptionAsync("TException", MakeTimeoutToken()); Console.WriteLine("*** FAILED ***"); returnCode |= ErrorExceptions; } @@ -845,25 +967,25 @@ public static async Task ExecuteClientTestAsync(ThriftTest.Client client) { Console.WriteLine("*** FAILED ***"); returnCode |= ErrorExceptions; - Console.WriteLine(ex.Message + " ST: " + ex.StackTrace); + Console.WriteLine(ex.Message + "\n" + ex.StackTrace); } try { Console.WriteLine("testException(\"ok\")"); - await client.testExceptionAsync("ok", token); + await client.testExceptionAsync("ok", MakeTimeoutToken()); // OK } catch (Exception ex) { Console.WriteLine("*** FAILED ***"); returnCode |= ErrorExceptions; - Console.WriteLine(ex.Message + " ST: " + ex.StackTrace); + Console.WriteLine(ex.Message + "\n" + ex.StackTrace); } try { Console.WriteLine("testMultiException(\"Xception\", ...)"); - await client.testMultiExceptionAsync("Xception", "ignore", token); + await client.testMultiExceptionAsync("Xception", "ignore", MakeTimeoutToken()); Console.WriteLine("*** FAILED ***"); returnCode |= ErrorExceptions; } @@ -879,12 +1001,12 @@ public static async Task ExecuteClientTestAsync(ThriftTest.Client client) { Console.WriteLine("*** FAILED ***"); returnCode |= ErrorExceptions; - Console.WriteLine(ex.Message + " ST: " + ex.StackTrace); + Console.WriteLine(ex.Message + "\n" + ex.StackTrace); } try { Console.WriteLine("testMultiException(\"Xception2\", ...)"); - await client.testMultiExceptionAsync("Xception2", "ignore", token); + await client.testMultiExceptionAsync("Xception2", "ignore", MakeTimeoutToken()); Console.WriteLine("*** FAILED ***"); returnCode |= ErrorExceptions; } @@ -900,12 +1022,12 @@ public static async Task ExecuteClientTestAsync(ThriftTest.Client client) { Console.WriteLine("*** FAILED ***"); returnCode |= ErrorExceptions; - Console.WriteLine(ex.Message + " ST: " + ex.StackTrace); + Console.WriteLine(ex.Message + "\n" + ex.StackTrace); } try { Console.WriteLine("testMultiException(\"success\", \"OK\")"); - if ("OK" != (await client.testMultiExceptionAsync("success", "OK", token)).String_thing) + if ("OK" != (await client.testMultiExceptionAsync("success", "OK", MakeTimeoutToken())).String_thing) { Console.WriteLine("*** FAILED ***"); returnCode |= ErrorExceptions; @@ -915,13 +1037,13 @@ public static async Task ExecuteClientTestAsync(ThriftTest.Client client) { Console.WriteLine("*** FAILED ***"); returnCode |= ErrorExceptions; - Console.WriteLine(ex.Message + " ST: " + ex.StackTrace); + Console.WriteLine(ex.Message + "\n" + ex.StackTrace); } + Console.WriteLine("Test Oneway(1)"); var sw = new Stopwatch(); sw.Start(); - Console.WriteLine("Test Oneway(1)"); - await client.testOnewayAsync(1, token); + await client.testOnewayAsync(1, MakeTimeoutToken()); sw.Stop(); if (sw.ElapsedMilliseconds > 1000) { @@ -933,6 +1055,7 @@ public static async Task ExecuteClientTestAsync(ThriftTest.Client client) var times = 50; sw.Reset(); sw.Start(); + var token = MakeTimeoutToken(20000); for (var k = 0; k < times; ++k) await client.testVoidAsync(token); sw.Stop(); diff --git a/test/netcore/Makefile.am b/test/netstd/Makefile.am similarity index 94% rename from test/netcore/Makefile.am rename to test/netstd/Makefile.am index 376ffb71e15..9712fc2a431 100644 --- a/test/netcore/Makefile.am +++ b/test/netstd/Makefile.am @@ -20,10 +20,10 @@ SUBDIRS = . all-local: - $(DOTNETCORE) build + $(DOTNETCORE) build -c Release precross: - $(DOTNETCORE) build + $(DOTNETCORE) build -c Release clean-local: $(RM) -r Client/bin diff --git a/test/netcore/README.md b/test/netstd/README.md similarity index 69% rename from test/netcore/README.md rename to test/netstd/README.md index ed728d1bac4..4ece0598438 100644 --- a/test/netcore/README.md +++ b/test/netstd/README.md @@ -1,12 +1,12 @@ # Apache Thrift net-core-lib tests -Tests for Thrift client library ported to Microsoft .Net Core +Tests for Thrift client library ported to Microsoft .NET Core # Content - ThriftTest - tests for Thrift library # Reused components -- NET Core Standard 1.6 (SDK 2.0.0) +- NET Core SDK 3.1 (LTS) # How to build on Windows - Get Thrift IDL compiler executable, add to some folder and add path to this folder into PATH variable @@ -15,6 +15,6 @@ or - Build with scripts # How to build on Unix -- Ensure you have .NET Core 2.0.0 SDK installed or use the Ubuntu Xenial docker image +- Ensure you have .NET Core 3.0 SDK installed or use the Ubuntu Xenial docker image - Follow common build practice for Thrift: bootstrap, configure, and make precross diff --git a/test/netcore/Server/Program.cs b/test/netstd/Server/Program.cs similarity index 71% rename from test/netcore/Server/Program.cs rename to test/netstd/Server/Program.cs index e647e5b2a58..8414b4810be 100644 --- a/test/netcore/Server/Program.cs +++ b/test/netstd/Server/Program.cs @@ -16,6 +16,7 @@ // under the License. using System; +using System.Linq; using System.Collections.Generic; using ThriftTest; @@ -34,37 +35,30 @@ public static int Main(string[] args) Console.WriteLine("Failed to grow scroll-back buffer"); } - // split mode and options - var subArgs = new List(args); - var firstArg = string.Empty; - if (subArgs.Count > 0) - { - firstArg = subArgs[0]; - subArgs.RemoveAt(0); - } - - // run whatever mode is choosen - switch(firstArg) + // run whatever mode is choosen, default to test impl + var argslist = new List(args); + switch (argslist.FirstOrDefault()) { - case "server": - return TestServer.Execute(subArgs); + case "server": // crosstest wants to pass this, so just emit a hint and ignore + Console.WriteLine("Hint: The 'server' argument is no longer required."); + argslist.RemoveAt(0); + return TestServer.Execute(argslist); case "--help": PrintHelp(); return 0; default: - PrintHelp(); - return -1; + return TestServer.Execute(argslist); } } private static void PrintHelp() { Console.WriteLine("Usage:"); - Console.WriteLine(" Server server [options]'"); + Console.WriteLine(" Server [options]"); Console.WriteLine(" Server --help"); Console.WriteLine(""); - TestServer.PrintOptionsHelp(); + ServerParam.PrintOptionsHelp(); } } } diff --git a/test/netcore/Server/Properties/AssemblyInfo.cs b/test/netstd/Server/Properties/AssemblyInfo.cs similarity index 100% rename from test/netcore/Server/Properties/AssemblyInfo.cs rename to test/netstd/Server/Properties/AssemblyInfo.cs diff --git a/test/netcore/Server/Server.csproj b/test/netstd/Server/Server.csproj similarity index 52% rename from test/netcore/Server/Server.csproj rename to test/netstd/Server/Server.csproj index 2f9b4b1f586..fa5ce46155a 100644 --- a/test/netcore/Server/Server.csproj +++ b/test/netstd/Server/Server.csproj @@ -1,6 +1,25 @@  + + - netcoreapp2.0 + netcoreapp3.1 Server Server Exe @@ -12,20 +31,22 @@ false - + + + - + - + - - - + + + - \ No newline at end of file + diff --git a/test/netcore/Server/TestServer.cs b/test/netstd/Server/TestServer.cs similarity index 71% rename from test/netcore/Server/TestServer.cs rename to test/netstd/Server/TestServer.cs index bfd33357fae..5c99099de6f 100644 --- a/test/netcore/Server/TestServer.cs +++ b/test/netstd/Server/TestServer.cs @@ -17,6 +17,7 @@ using System; using System.Collections.Generic; +using System.Diagnostics; using System.IO; using System.Linq; using System.Security.Authentication; @@ -27,20 +28,42 @@ using Microsoft.Extensions.Logging; using Thrift; using Thrift.Collections; -using Thrift.Protocols; +using Thrift.Processor; +using Thrift.Protocol; using Thrift.Server; -using Thrift.Transports; -using Thrift.Transports.Server; +using Thrift.Transport; +using Thrift.Transport.Server; + +#pragma warning disable IDE0063 // using can be simplified, we don't namespace ThriftTest { + internal enum ProtocolChoice + { + Binary, + Compact, + Json + } + + internal enum TransportChoice + { + Socket, + TlsSocket, + NamedPipe + } + + internal enum BufferChoice + { + None, + Buffered, + Framed + } + internal class ServerParam { - internal bool useBufferedSockets = false; - internal bool useFramed = false; - internal bool useEncryption = false; - internal bool compact = false; - internal bool json = false; + internal BufferChoice buffering = BufferChoice.None; + internal ProtocolChoice protocol = ProtocolChoice.Binary; + internal TransportChoice transport = TransportChoice.Socket; internal int port = 9090; internal string pipe = null; @@ -51,30 +74,33 @@ internal void Parse(List args) if (args[i].StartsWith("--pipe=")) { pipe = args[i].Substring(args[i].IndexOf("=") + 1); + transport = TransportChoice.NamedPipe; } else if (args[i].StartsWith("--port=")) { port = int.Parse(args[i].Substring(args[i].IndexOf("=") + 1)); + if(transport != TransportChoice.TlsSocket) + transport = TransportChoice.Socket; } else if (args[i] == "-b" || args[i] == "--buffered" || args[i] == "--transport=buffered") { - useBufferedSockets = true; + buffering = BufferChoice.Buffered; } else if (args[i] == "-f" || args[i] == "--framed" || args[i] == "--transport=framed") { - useFramed = true; + buffering = BufferChoice.Framed; } else if (args[i] == "--binary" || args[i] == "--protocol=binary") { - // nothing needed + protocol = ProtocolChoice.Binary; } else if (args[i] == "--compact" || args[i] == "--protocol=compact") { - compact = true; + protocol = ProtocolChoice.Compact; } else if (args[i] == "--json" || args[i] == "--protocol=json") { - json = true; + protocol = ProtocolChoice.Json; } else if (args[i] == "--threaded" || args[i] == "--server-type=threaded") { @@ -90,20 +116,42 @@ internal void Parse(List args) } else if (args[i] == "--ssl") { - useEncryption = true; + transport = TransportChoice.TlsSocket; + } + else if (args[i] == "--help") + { + PrintOptionsHelp(); + return; } else { - //throw new ArgumentException(args[i]); + Console.WriteLine("Invalid argument: {0}", args[i]); + PrintOptionsHelp(); + return; } } } + + internal static void PrintOptionsHelp() + { + Console.WriteLine("Server options:"); + Console.WriteLine(" --pipe="); + Console.WriteLine(" --port="); + Console.WriteLine(" --transport= one of buffered,framed (defaults to none)"); + Console.WriteLine(" --protocol= one of compact,json (defaults to binary)"); + Console.WriteLine(" --server-type= one of threaded,threadpool (defaults to simple)"); + Console.WriteLine(" --processor="); + Console.WriteLine(" --ssl"); + Console.WriteLine(); + } } public class TestServer { public static int _clientID = -1; + private static readonly TConfiguration Configuration = null; // or new TConfiguration() if needed + public delegate void TestLogDelegate(string msg, params object[] values); public class MyServerEventHandler : TServerEventHandler @@ -128,7 +176,7 @@ public Task DeleteContextAsync(object serverContext, TProtocol input, TProtocol return Task.CompletedTask; } - public Task ProcessContextAsync(object serverContext, TClientTransport transport, CancellationToken cancellationToken) + public Task ProcessContextAsync(object serverContext, TTransport transport, CancellationToken cancellationToken) { callCount++; return Task.CompletedTask; @@ -137,19 +185,19 @@ public Task ProcessContextAsync(object serverContext, TClientTransport transport public class TestHandlerAsync : ThriftTest.IAsync { - public TBaseServer server { get; set; } - private int handlerID; - private StringBuilder sb = new StringBuilder(); - private TestLogDelegate logger; + public TServer Server { get; set; } + private readonly int handlerID; + private readonly StringBuilder sb = new StringBuilder(); + private readonly TestLogDelegate logger; public TestHandlerAsync() { handlerID = Interlocked.Increment(ref _clientID); - logger += testConsoleLogger; + logger += TestConsoleLogger; logger.Invoke("New TestHandler instance created"); } - public void testConsoleLogger(string msg, params object[] values) + public void TestConsoleLogger(string msg, params object[] values) { sb.Clear(); sb.AppendFormat("handler{0:D3}:", handlerID); @@ -202,8 +250,7 @@ public Task testDoubleAsync(double thing, CancellationToken cancellation public Task testBinaryAsync(byte[] thing, CancellationToken cancellationToken) { - var hex = BitConverter.ToString(thing).Replace("-", string.Empty); - logger.Invoke("testBinary({0:X})", hex); + logger.Invoke("testBinary({0} bytes)", thing.Length); return Task.FromResult(thing); } @@ -447,18 +494,6 @@ public Task testOnewayAsync(int secondsToSleep, CancellationToken cancellationTo } } - internal static void PrintOptionsHelp() - { - Console.WriteLine("Server options:"); - Console.WriteLine(" --pipe="); - Console.WriteLine(" --port="); - Console.WriteLine(" --transport= one of buffered,framed (defaults to none)"); - Console.WriteLine(" --protocol= one of compact,json (defaults to binary)"); - Console.WriteLine(" --server-type= one of threaded,threadpool (defaults to simple)"); - Console.WriteLine(" --processor="); - Console.WriteLine(" --ssl"); - Console.WriteLine(); - } private static X509Certificate2 GetServerCert() { @@ -494,100 +529,114 @@ private static X509Certificate2 GetServerCert() public static int Execute(List args) { - var loggerFactory = new LoggerFactory();//.AddConsole().AddDebug(); - var logger = new LoggerFactory().CreateLogger("Test"); - - try + using (var loggerFactory = new LoggerFactory()) //.AddConsole().AddDebug(); { - var param = new ServerParam(); + var logger = loggerFactory.CreateLogger("Test"); try { - param.Parse(args); - } - catch (Exception ex) - { - Console.WriteLine("*** FAILED ***"); - Console.WriteLine("Error while parsing arguments"); - Console.WriteLine(ex.Message + " ST: " + ex.StackTrace); - return 1; - } - + var param = new ServerParam(); - // Transport - TServerTransport trans; - if (param.pipe != null) - { - trans = new TNamedPipeServerTransport(param.pipe); - } -// else if (param.useFramed) -// { -// trans = new TServerFramedTransport(param.port); -// } - else - { - if (param.useEncryption) + try { - var cert = GetServerCert(); - - if (cert == null || !cert.HasPrivateKey) - { - throw new InvalidOperationException("Certificate doesn't contain private key"); - } - - trans = new TTlsServerSocketTransport(param.port, param.useBufferedSockets, param.useFramed, cert, - (sender, certificate, chain, errors) => true, - null, SslProtocols.Tls | SslProtocols.Tls11 | SslProtocols.Tls12); + param.Parse(args); } - else + catch (Exception ex) { - trans = new TServerSocketTransport(param.port, 0, param.useBufferedSockets, param.useFramed); + Console.WriteLine("*** FAILED ***"); + Console.WriteLine("Error while parsing arguments"); + Console.WriteLine(ex.Message + " ST: " + ex.StackTrace); + return 1; } - } - - ITProtocolFactory proto; - if (param.compact) - proto = new TCompactProtocol.Factory(); - else if (param.json) - proto = new TJsonProtocol.Factory(); - else - proto = new TBinaryProtocol.Factory(); - ITProcessorFactory processorFactory; - // Processor - var testHandler = new TestHandlerAsync(); - var testProcessor = new ThriftTest.AsyncProcessor(testHandler); - processorFactory = new SingletonTProcessorFactory(testProcessor); + // Endpoint transport (mandatory) + TServerTransport trans; + switch (param.transport) + { + case TransportChoice.NamedPipe: + Debug.Assert(param.pipe != null); + trans = new TNamedPipeServerTransport(param.pipe, Configuration); + break; + + + case TransportChoice.TlsSocket: + var cert = GetServerCert(); + if (cert == null || !cert.HasPrivateKey) + { + cert?.Dispose(); + throw new InvalidOperationException("Certificate doesn't contain private key"); + } + + trans = new TTlsServerSocketTransport(param.port, Configuration, + cert, + (sender, certificate, chain, errors) => true, + null, SslProtocols.Tls | SslProtocols.Tls11 | SslProtocols.Tls12); + break; + + case TransportChoice.Socket: + default: + trans = new TServerSocketTransport(param.port, Configuration); + break; + } - TTransportFactory transFactory = new TTransportFactory(); + // Layered transport (mandatory) + TTransportFactory transFactory = null; + switch (param.buffering) + { + case BufferChoice.Framed: + transFactory = new TFramedTransport.Factory(); + break; + case BufferChoice.Buffered: + transFactory = new TBufferedTransport.Factory(); + break; + default: + Debug.Assert(param.buffering == BufferChoice.None, "unhandled case"); + transFactory = null; // no layered transprt + break; + } - TBaseServer serverEngine = new AsyncBaseServer(processorFactory, trans, transFactory, transFactory, proto, proto, logger); + TProtocolFactory proto = param.protocol switch + { + ProtocolChoice.Compact => new TCompactProtocol.Factory(), + ProtocolChoice.Json => new TJsonProtocol.Factory(), + ProtocolChoice.Binary => new TBinaryProtocol.Factory(), + _ => new TBinaryProtocol.Factory(), + }; - //Server event handler - var serverEvents = new MyServerEventHandler(); - serverEngine.SetEventHandler(serverEvents); + // Processor + var testHandler = new TestHandlerAsync(); + var testProcessor = new ThriftTest.AsyncProcessor(testHandler); + var processorFactory = new TSingletonProcessorFactory(testProcessor); + + TServer serverEngine = new TSimpleAsyncServer(processorFactory, trans, transFactory, transFactory, proto, proto, logger); + + //Server event handler + var serverEvents = new MyServerEventHandler(); + serverEngine.SetEventHandler(serverEvents); + + // Run it + var where = (!string.IsNullOrEmpty(param.pipe)) ? "on pipe " + param.pipe : "on port " + param.port; + Console.WriteLine("Starting the AsyncBaseServer " + where + + " with processor TPrototypeProcessorFactory prototype factory " + + (param.buffering == BufferChoice.Buffered ? " with buffered transport" : "") + + (param.buffering == BufferChoice.Framed ? " with framed transport" : "") + + (param.transport == TransportChoice.TlsSocket ? " with encryption" : "") + + (param.protocol == ProtocolChoice.Compact ? " with compact protocol" : "") + + (param.protocol == ProtocolChoice.Json ? " with json protocol" : "") + + "..."); + serverEngine.ServeAsync(CancellationToken.None).GetAwaiter().GetResult(); + Console.ReadLine(); + } + catch (Exception x) + { + Console.Error.Write(x); + return 1; + } - // Run it - var where = (! string.IsNullOrEmpty(param.pipe)) ? "on pipe " + param.pipe : "on port " + param.port; - Console.WriteLine("Starting the AsyncBaseServer " + where + - " with processor TPrototypeProcessorFactory prototype factory " + - (param.useBufferedSockets ? " with buffered socket" : "") + - (param.useFramed ? " with framed transport" : "") + - (param.useEncryption ? " with encryption" : "") + - (param.compact ? " with compact protocol" : "") + - (param.json ? " with json protocol" : "") + - "..."); - serverEngine.ServeAsync(CancellationToken.None).GetAwaiter().GetResult(); - Console.ReadLine(); - } - catch (Exception x) - { - Console.Error.Write(x); - return 1; + Console.WriteLine("done."); + return 0; } - Console.WriteLine("done."); - return 0; } } diff --git a/test/netcore/ThriftTest.sln b/test/netstd/ThriftTest.sln similarity index 94% rename from test/netcore/ThriftTest.sln rename to test/netstd/ThriftTest.sln index 2ab241add75..352576ef0e1 100644 --- a/test/netcore/ThriftTest.sln +++ b/test/netstd/ThriftTest.sln @@ -2,11 +2,11 @@ # Visual Studio 15 VisualStudioVersion = 15.0.26730.12 MinimumVisualStudioVersion = 10.0.40219.1 -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Thrift", "..\..\lib\netcore\Thrift\Thrift.csproj", "{C20EA2A9-7660-47DE-9A49-D1EF12FB2895}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Thrift", "..\..\lib\netstd\Thrift\Thrift.csproj", "{C20EA2A9-7660-47DE-9A49-D1EF12FB2895}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Client", "Client\Client.csproj", "{21039F25-6ED7-4E80-A545-EBC93472EBD1}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Client", "Client\Client.csproj", "{21039F25-6ED7-4E80-A545-EBC93472EBD1}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Server", "Server\Server.csproj", "{0C6E8685-F191-4479-9842-882A38961127}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Server", "Server\Server.csproj", "{0C6E8685-F191-4479-9842-882A38961127}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution diff --git a/test/netcore/build.cmd b/test/netstd/build.cmd similarity index 100% rename from test/netcore/build.cmd rename to test/netstd/build.cmd diff --git a/test/netcore/build.sh b/test/netstd/build.sh old mode 100755 new mode 100644 similarity index 100% rename from test/netcore/build.sh rename to test/netstd/build.sh diff --git a/test/perl/Makefile.am b/test/perl/Makefile.am index 165b9a7d043..1dbaf28b0d7 100644 --- a/test/perl/Makefile.am +++ b/test/perl/Makefile.am @@ -25,5 +25,7 @@ precross: stubs check: stubs clean-local: - $(RM) -r gen-perl + $(RM) -r gen-perl/ +dist-hook: + $(RM) -r $(distdir)/gen-perl/ diff --git a/test/perl/TestClient.pl b/test/perl/TestClient.pl index 96e3bec7705..6d49f1da1d8 100755 --- a/test/perl/TestClient.pl +++ b/test/perl/TestClient.pl @@ -218,9 +218,12 @@ sub usage { exit(ERR_BASETYPES) if ($dub ne -852.234234234); # -# BINARY TEST --- TODO +# BINARY TEST # - +print("testBinary(pack('C*', 0..255))"); +my $bin = $testClient->testBinary(pack('C*', 0..255)); +printf(" = %s\n", join ' ', map { sprintf '%02x', $_ } unpack('C*', $bin)); +exit(ERR_BASETYPES) if ($bin ne pack('C*', 0..255)); # # STRUCT TEST diff --git a/test/php/Makefile.am b/test/php/Makefile.am index 72f7fc58142..9e13affa145 100755 --- a/test/php/Makefile.am +++ b/test/php/Makefile.am @@ -21,11 +21,12 @@ stubs: ../ThriftTest.thrift $(THRIFT) --gen php ../ThriftTest.thrift $(THRIFT) --gen php:inlined ../ThriftTest.thrift $(MKDIR_P) gen-php-classmap - $(THRIFT) -out gen-php-classmap --gen php ../ThriftTest.thrift + $(THRIFT) -out gen-php-classmap --gen php:classmap ../ThriftTest.thrift php_ext_dir: mkdir -p php_ext_dir ln -s ../../../lib/php/src/ext/thrift_protocol/modules/thrift_protocol.so php_ext_dir/ + ln -s "$$(php-config --extension-dir)/json.so" php_ext_dir/ ln -s "$$(php-config --extension-dir)/sockets.so" php_ext_dir/ precross: stubs php_ext_dir @@ -33,7 +34,12 @@ precross: stubs php_ext_dir check: stubs php_ext_dir clean-local: - $(RM) -r gen-php gen-phpi gen-php-classmap php_ext_dir + $(RM) -r gen-*/ + $(RM) -r php_ext_dir + +dist-hook: + $(RM) -r $(distdir)/gen-*/ + $(RM) -r $(distdir)/php_ext_dir/ client: stubs php_ext_dir php TestClient.php diff --git a/test/php/test_php.ini b/test/php/test_php.ini index 3f9bb21e22f..aeb67cbd411 100644 --- a/test/php/test_php.ini +++ b/test/php/test_php.ini @@ -1,2 +1,3 @@ extension=thrift_protocol.so +extension=json.so extension=sockets.so diff --git a/test/py.tornado/Makefile.am b/test/py.tornado/Makefile.am index a8e680a97f1..e962f0cfcb0 100644 --- a/test/py.tornado/Makefile.am +++ b/test/py.tornado/Makefile.am @@ -27,4 +27,12 @@ check: thrift_gen ./test_suite.py clean-local: - $(RM) -r gen-py.tornado + $(RM) -r build + find . -type f \( -iname "*.pyc" \) | xargs rm -f + find . -type d \( -iname "__pycache__" -or -iname "_trial_temp" \) | xargs rm -rf + $(RM) -r gen-py*/ + +dist-hook: + find $(distdir) -type f \( -iname "*.pyc" \) | xargs rm -f + find $(distdir) -type d \( -iname "__pycache__" -or -iname "_trial_temp" \) | xargs rm -rf + $(RM) -r $(distdir)/gen-py*/ diff --git a/test/py.tornado/test_suite.py b/test/py.tornado/test_suite.py index 447fde61b7e..0ee0a9b85a0 100755 --- a/test/py.tornado/test_suite.py +++ b/test/py.tornado/test_suite.py @@ -82,10 +82,7 @@ def testStruct(self, thing): def testException(self, s): if s == 'Xception': - x = Xception() - x.errorCode = 1001 - x.message = s - raise x + raise Xception(1001, s) elif s == 'throw_undeclared': raise ValueError('testing undeclared exception') diff --git a/test/py.twisted/Makefile.am b/test/py.twisted/Makefile.am index d11908cc0ae..dee8e2f69a7 100644 --- a/test/py.twisted/Makefile.am +++ b/test/py.twisted/Makefile.am @@ -27,4 +27,12 @@ check: stubs $(TRIAL) ./test_suite.py clean-local: - $(RM) -r gen-py.twisted + $(RM) -r build + find . -type f \( -iname "*.pyc" \) | xargs rm -f + find . -type d \( -iname "__pycache__" -or -iname "_trial_temp" \) | xargs rm -rf + $(RM) -r gen-py*/ + +dist-hook: + find $(distdir) -type f \( -iname "*.pyc" \) | xargs rm -f + find $(distdir) -type d \( -iname "__pycache__" -or -iname "_trial_temp" \) | xargs rm -rf + $(RM) -r $(distdir)/gen-py*/ diff --git a/test/py.twisted/test_suite.py b/test/py.twisted/test_suite.py index 02eb7f14ff8..6e044939bba 100755 --- a/test/py.twisted/test_suite.py +++ b/test/py.twisted/test_suite.py @@ -76,10 +76,7 @@ def testStruct(self, thing): def testException(self, s): if s == 'Xception': - x = Xception() - x.errorCode = 1001 - x.message = s - raise x + raise Xception(1001, s) elif s == "throw_undeclared": raise ValueError("foo") diff --git a/test/py/Makefile.am b/test/py/Makefile.am index 829620055b3..9433e5907da 100644 --- a/test/py/Makefile.am +++ b/test/py/Makefile.am @@ -94,4 +94,12 @@ gen-py-dynamicslots/%/__init__.py: ../%.thrift $(THRIFT) $(THRIFT) --gen py:dynamic,slots -out gen-py-dynamicslots $< clean-local: - $(RM) -r gen-py gen-py-slots gen-py-default gen-py-oldstyle gen-py-no_utf8strings gen-py-dynamic gen-py-dynamicslots + $(RM) -r build + find . -type f \( -iname "*.pyc" \) | xargs rm -f + find . -type d \( -iname "__pycache__" -or -iname "_trial_temp" \) | xargs rm -rf + $(RM) -r gen-py*/ + +dist-hook: + find $(distdir) -type f \( -iname "*.pyc" \) | xargs rm -f + find $(distdir) -type d \( -iname "__pycache__" -or -iname "_trial_temp" \) | xargs rm -rf + $(RM) -r $(distdir)/gen-py*/ diff --git a/test/py/TestClient.py b/test/py/TestClient.py index ddcce8db0a5..8a30c3a57cd 100755 --- a/test/py/TestClient.py +++ b/test/py/TestClient.py @@ -23,9 +23,11 @@ import sys import time import unittest -from optparse import OptionParser +from optparse import OptionParser from util import local_libpath +sys.path.insert(0, local_libpath()) +from thrift.protocol import TProtocol, TProtocolDecorator SCRIPT_DIR = os.path.abspath(os.path.dirname(__file__)) @@ -49,7 +51,7 @@ def setUp(self): from thrift.transport import TSSLSocket socket = TSSLSocket.TSSLSocket(options.host, options.port, validate=False) else: - socket = TSocket.TSocket(options.host, options.port) + socket = TSocket.TSocket(options.host, options.port, options.domain_socket) # frame or buffer depending upon args self.transport = TTransport.TBufferedTransport(socket) if options.trans == 'framed': @@ -268,6 +270,47 @@ def testOnewayThenNormal(self): self.assertEqual(self.client.testString('Python'), 'Python') +# LAST_SEQID is a global because we have one transport and multiple protocols +# running on it (when multiplexed) +LAST_SEQID = None + + +class TPedanticSequenceIdProtocolWrapper(TProtocolDecorator.TProtocolDecorator): + """ + Wraps any protocol with sequence ID checking: looks for outbound + uniqueness as well as request/response alignment. + """ + def __init__(self, protocol): + # TProtocolDecorator.__new__ does all the heavy lifting + pass + + def writeMessageBegin(self, name, type, seqid): + global LAST_SEQID + if LAST_SEQID and LAST_SEQID == seqid: + raise TProtocol.TProtocolException( + TProtocol.TProtocolException.INVALID_DATA, + "Python client reused sequence ID {0}".format(seqid)) + LAST_SEQID = seqid + super(TPedanticSequenceIdProtocolWrapper, self).writeMessageBegin( + name, type, seqid) + + def readMessageBegin(self): + global LAST_SEQID + (name, type, seqid) =\ + super(TPedanticSequenceIdProtocolWrapper, self).readMessageBegin() + if LAST_SEQID != seqid: + raise TProtocol.TProtocolException( + TProtocol.TProtocolException.INVALID_DATA, + "We sent seqid {0} and server returned seqid {1}".format( + self.last, seqid)) + return (name, type, seqid) + + +def make_pedantic(proto): + """ Wrap a protocol in the pedantic sequence ID wrapper. """ + return TPedanticSequenceIdProtocolWrapper(proto) + + class MultiplexedOptionalTest(AbstractTest): def get_protocol2(self, transport): return None @@ -275,83 +318,93 @@ def get_protocol2(self, transport): class BinaryTest(MultiplexedOptionalTest): def get_protocol(self, transport): - return TBinaryProtocol.TBinaryProtocolFactory().getProtocol(transport) + return make_pedantic(TBinaryProtocol.TBinaryProtocolFactory().getProtocol(transport)) class MultiplexedBinaryTest(MultiplexedOptionalTest): def get_protocol(self, transport): - wrapped_proto = TBinaryProtocol.TBinaryProtocolFactory().getProtocol(transport) + wrapped_proto = make_pedantic(TBinaryProtocol.TBinaryProtocolFactory().getProtocol(transport)) return TMultiplexedProtocol.TMultiplexedProtocol(wrapped_proto, "ThriftTest") def get_protocol2(self, transport): - wrapped_proto = TBinaryProtocol.TBinaryProtocolFactory().getProtocol(transport) + wrapped_proto = make_pedantic(TBinaryProtocol.TBinaryProtocolFactory().getProtocol(transport)) return TMultiplexedProtocol.TMultiplexedProtocol(wrapped_proto, "SecondService") class AcceleratedBinaryTest(MultiplexedOptionalTest): def get_protocol(self, transport): - return TBinaryProtocol.TBinaryProtocolAcceleratedFactory(fallback=False).getProtocol(transport) + return make_pedantic(TBinaryProtocol.TBinaryProtocolAcceleratedFactory(fallback=False).getProtocol(transport)) class MultiplexedAcceleratedBinaryTest(MultiplexedOptionalTest): def get_protocol(self, transport): - wrapped_proto = TBinaryProtocol.TBinaryProtocolAcceleratedFactory(fallback=False).getProtocol(transport) + wrapped_proto = make_pedantic(TBinaryProtocol.TBinaryProtocolAcceleratedFactory(fallback=False).getProtocol(transport)) return TMultiplexedProtocol.TMultiplexedProtocol(wrapped_proto, "ThriftTest") def get_protocol2(self, transport): - wrapped_proto = TBinaryProtocol.TBinaryProtocolAcceleratedFactory(fallback=False).getProtocol(transport) + wrapped_proto = make_pedantic(TBinaryProtocol.TBinaryProtocolAcceleratedFactory(fallback=False).getProtocol(transport)) return TMultiplexedProtocol.TMultiplexedProtocol(wrapped_proto, "SecondService") class CompactTest(MultiplexedOptionalTest): def get_protocol(self, transport): - return TCompactProtocol.TCompactProtocolFactory().getProtocol(transport) + return make_pedantic(TCompactProtocol.TCompactProtocolFactory().getProtocol(transport)) class MultiplexedCompactTest(MultiplexedOptionalTest): def get_protocol(self, transport): - wrapped_proto = TCompactProtocol.TCompactProtocolFactory().getProtocol(transport) + wrapped_proto = make_pedantic(TCompactProtocol.TCompactProtocolFactory().getProtocol(transport)) return TMultiplexedProtocol.TMultiplexedProtocol(wrapped_proto, "ThriftTest") def get_protocol2(self, transport): - wrapped_proto = TCompactProtocol.TCompactProtocolFactory().getProtocol(transport) + wrapped_proto = make_pedantic(TCompactProtocol.TCompactProtocolFactory().getProtocol(transport)) return TMultiplexedProtocol.TMultiplexedProtocol(wrapped_proto, "SecondService") class AcceleratedCompactTest(MultiplexedOptionalTest): def get_protocol(self, transport): - return TCompactProtocol.TCompactProtocolAcceleratedFactory(fallback=False).getProtocol(transport) + return make_pedantic(TCompactProtocol.TCompactProtocolAcceleratedFactory(fallback=False).getProtocol(transport)) class MultiplexedAcceleratedCompactTest(MultiplexedOptionalTest): def get_protocol(self, transport): - wrapped_proto = TCompactProtocol.TCompactProtocolAcceleratedFactory(fallback=False).getProtocol(transport) + wrapped_proto = make_pedantic(TCompactProtocol.TCompactProtocolAcceleratedFactory(fallback=False).getProtocol(transport)) return TMultiplexedProtocol.TMultiplexedProtocol(wrapped_proto, "ThriftTest") def get_protocol2(self, transport): - wrapped_proto = TCompactProtocol.TCompactProtocolAcceleratedFactory(fallback=False).getProtocol(transport) + wrapped_proto = make_pedantic(TCompactProtocol.TCompactProtocolAcceleratedFactory(fallback=False).getProtocol(transport)) return TMultiplexedProtocol.TMultiplexedProtocol(wrapped_proto, "SecondService") class JSONTest(MultiplexedOptionalTest): def get_protocol(self, transport): - return TJSONProtocol.TJSONProtocolFactory().getProtocol(transport) + return make_pedantic(TJSONProtocol.TJSONProtocolFactory().getProtocol(transport)) class MultiplexedJSONTest(MultiplexedOptionalTest): def get_protocol(self, transport): - wrapped_proto = TJSONProtocol.TJSONProtocolFactory().getProtocol(transport) + wrapped_proto = make_pedantic(TJSONProtocol.TJSONProtocolFactory().getProtocol(transport)) return TMultiplexedProtocol.TMultiplexedProtocol(wrapped_proto, "ThriftTest") def get_protocol2(self, transport): - wrapped_proto = TJSONProtocol.TJSONProtocolFactory().getProtocol(transport) + wrapped_proto = make_pedantic(TJSONProtocol.TJSONProtocolFactory().getProtocol(transport)) return TMultiplexedProtocol.TMultiplexedProtocol(wrapped_proto, "SecondService") class HeaderTest(MultiplexedOptionalTest): def get_protocol(self, transport): factory = THeaderProtocol.THeaderProtocolFactory() - return factory.getProtocol(transport) + return make_pedantic(factory.getProtocol(transport)) + + +class MultiplexedHeaderTest(MultiplexedOptionalTest): + def get_protocol(self, transport): + wrapped_proto = make_pedantic(THeaderProtocol.THeaderProtocolFactory().getProtocol(transport)) + return TMultiplexedProtocol.TMultiplexedProtocol(wrapped_proto, "ThriftTest") + + def get_protocol2(self, transport): + wrapped_proto = make_pedantic(THeaderProtocol.THeaderProtocolFactory().getProtocol(transport)) + return TMultiplexedProtocol.TMultiplexedProtocol(wrapped_proto, "SecondService") def suite(): @@ -377,6 +430,8 @@ def suite(): suite.addTest(loader.loadTestsFromTestCase(MultiplexedAcceleratedCompactTest)) elif options.proto == 'multic': suite.addTest(loader.loadTestsFromTestCase(MultiplexedCompactTest)) + elif options.proto == 'multih': + suite.addTest(loader.loadTestsFromTestCase(MultiplexedHeaderTest)) elif options.proto == 'multij': suite.addTest(loader.loadTestsFromTestCase(MultiplexedJSONTest)) else: @@ -416,15 +471,16 @@ def parseArgs(self, argv): dest="verbose", const=0, help="minimal output") parser.add_option('--protocol', dest="proto", type="string", - help="protocol to use, one of: accel, accelc, binary, compact, header, json, multi, multia, multiac, multic, multij") + help="protocol to use, one of: accel, accelc, binary, compact, header, json, multi, multia, multiac, multic, multih, multij") parser.add_option('--transport', dest="trans", type="string", help="transport to use, one of: buffered, framed, http") + parser.add_option('--domain-socket', dest="domain_socket", type="string", + help="Unix domain socket path") parser.set_defaults(framed=False, http_path=None, verbose=1, host='localhost', port=9090, proto='binary') options, args = parser.parse_args() if options.genpydir: sys.path.insert(0, os.path.join(SCRIPT_DIR, options.genpydir)) - sys.path.insert(0, local_libpath()) if options.http_path: options.trans = 'http' diff --git a/test/py/TestFrozen.py b/test/py/TestFrozen.py index 6d2595cf29b..ce7425f88fb 100755 --- a/test/py/TestFrozen.py +++ b/test/py/TestFrozen.py @@ -19,7 +19,9 @@ # under the License. # +from DebugProtoTest import Srv from DebugProtoTest.ttypes import CompactProtoTestStruct, Empty, Wrapper +from DebugProtoTest.ttypes import ExceptionWithAMap, MutableException from thrift.Thrift import TFrozenDict from thrift.transport import TTransport from thrift.protocol import TBinaryProtocol, TCompactProtocol @@ -94,6 +96,21 @@ def test_struct(self): x2 = self._roundtrip(x, Wrapper) self.assertEqual(x2.foo, Empty()) + def test_frozen_exception(self): + exc = ExceptionWithAMap(blah='foo') + with self.assertRaises(TypeError): + exc.blah = 'bar' + mutexc = MutableException(msg='foo') + mutexc.msg = 'bar' + self.assertEqual(mutexc.msg, 'bar') + + def test_frozen_exception_serialization(self): + result = Srv.declaredExceptionMethod_result( + xwamap=ExceptionWithAMap(blah="error")) + deserialized = self._roundtrip( + result, Srv.declaredExceptionMethod_result()) + self.assertEqual(result, deserialized) + class TestFrozen(TestFrozenBase): def protocol(self, trans): diff --git a/test/py/TestServer.py b/test/py/TestServer.py index aba0d42988b..81ae1ad62ee 100755 --- a/test/py/TestServer.py +++ b/test/py/TestServer.py @@ -27,6 +27,8 @@ from optparse import OptionParser from util import local_libpath +sys.path.insert(0, local_libpath()) +from thrift.protocol import TProtocol, TProtocolDecorator SCRIPT_DIR = os.path.abspath(os.path.dirname(__file__)) @@ -173,26 +175,84 @@ def testInsanity(self, argument): def testMulti(self, arg0, arg1, arg2, arg3, arg4, arg5): if options.verbose > 1: - logging.info('testMulti(%s)' % [arg0, arg1, arg2, arg3, arg4, arg5]) + logging.info('testMulti(%s, %s, %s, %s, %s, %s)' % (arg0, arg1, arg2, arg3, arg4, arg5)) return Xtruct(string_thing='Hello2', byte_thing=arg0, i32_thing=arg1, i64_thing=arg2) +class SecondHandler(object): + def secondtestString(self, argument): + return "testString(\"" + argument + "\")" + + +# LAST_SEQID is a global because we have one transport and multiple protocols +# running on it (when multiplexed) +LAST_SEQID = None + + +class TPedanticSequenceIdProtocolWrapper(TProtocolDecorator.TProtocolDecorator): + """ + Wraps any protocol with sequence ID checking: looks for outbound + uniqueness as well as request/response alignment. + """ + def __init__(self, protocol): + # TProtocolDecorator.__new__ does all the heavy lifting + pass + + def readMessageBegin(self): + global LAST_SEQID + (name, type, seqid) =\ + super(TPedanticSequenceIdProtocolWrapper, self).readMessageBegin() + if LAST_SEQID is not None and LAST_SEQID == seqid: + raise TProtocol.TProtocolException( + TProtocol.TProtocolException.INVALID_DATA, + "We received the same seqid {0} twice in a row".format(seqid)) + LAST_SEQID = seqid + return (name, type, seqid) + + +def make_pedantic(proto): + """ Wrap a protocol in the pedantic sequence ID wrapper. """ + # NOTE: this is disabled for now as many clients send seqid + # of zero and that is okay, need a way to identify + # clients that MUST send seqid unique to function right + # or just force all implementations to send unique seqids (preferred) + return proto # TPedanticSequenceIdProtocolWrapper(proto) + + +class TPedanticSequenceIdProtocolFactory(TProtocol.TProtocolFactory): + def __init__(self, encapsulated): + super(TPedanticSequenceIdProtocolFactory, self).__init__() + self.encapsulated = encapsulated + + def getProtocol(self, trans): + return make_pedantic(self.encapsulated.getProtocol(trans)) + + def main(options): + # common header allowed client types + allowed_client_types = [ + THeaderTransport.THeaderClientType.HEADERS, + THeaderTransport.THeaderClientType.FRAMED_BINARY, + THeaderTransport.THeaderClientType.UNFRAMED_BINARY, + THeaderTransport.THeaderClientType.FRAMED_COMPACT, + THeaderTransport.THeaderClientType.UNFRAMED_COMPACT, + ] + # set up the protocol factory form the --protocol option prot_factories = { 'accel': TBinaryProtocol.TBinaryProtocolAcceleratedFactory(), + 'multia': TBinaryProtocol.TBinaryProtocolAcceleratedFactory(), 'accelc': TCompactProtocol.TCompactProtocolAcceleratedFactory(), - 'binary': TBinaryProtocol.TBinaryProtocolFactory(), + 'multiac': TCompactProtocol.TCompactProtocolAcceleratedFactory(), + 'binary': TPedanticSequenceIdProtocolFactory(TBinaryProtocol.TBinaryProtocolFactory()), + 'multi': TPedanticSequenceIdProtocolFactory(TBinaryProtocol.TBinaryProtocolFactory()), 'compact': TCompactProtocol.TCompactProtocolFactory(), - 'header': THeaderProtocol.THeaderProtocolFactory(allowed_client_types=[ - THeaderTransport.THeaderClientType.HEADERS, - THeaderTransport.THeaderClientType.FRAMED_BINARY, - THeaderTransport.THeaderClientType.UNFRAMED_BINARY, - THeaderTransport.THeaderClientType.FRAMED_COMPACT, - THeaderTransport.THeaderClientType.UNFRAMED_COMPACT, - ]), + 'multic': TCompactProtocol.TCompactProtocolFactory(), + 'header': THeaderProtocol.THeaderProtocolFactory(allowed_client_types), + 'multih': THeaderProtocol.THeaderProtocolFactory(allowed_client_types), 'json': TJSONProtocol.TJSONProtocolFactory(), + 'multij': TJSONProtocol.TJSONProtocolFactory(), } pfactory = prot_factories.get(options.proto, None) if pfactory is None: @@ -215,6 +275,16 @@ def main(options): handler = TestHandler() processor = ThriftTest.Processor(handler) + if options.proto.startswith('multi'): + secondHandler = SecondHandler() + secondProcessor = SecondService.Processor(secondHandler) + + multiplexedProcessor = TMultiplexedProcessor() + multiplexedProcessor.registerDefault(processor) + multiplexedProcessor.registerProcessor('ThriftTest', processor) + multiplexedProcessor.registerProcessor('SecondService', secondProcessor) + processor = multiplexedProcessor + global server # Handle THttpServer as a special case @@ -237,7 +307,7 @@ def main(options): from thrift.transport import TSSLSocket transport = TSSLSocket.TSSLServerSocket(host, options.port, certfile=abs_key_path) else: - transport = TSocket.TServerSocket(host, options.port) + transport = TSocket.TServerSocket(host, options.port, options.domain_socket) tfactory = TTransport.TBufferedTransportFactory() if options.trans == 'buffered': tfactory = TTransport.TBufferedTransportFactory() @@ -312,9 +382,11 @@ def exit_gracefully(signum, frame): dest="verbose", const=0, help="minimal output") parser.add_option('--protocol', dest="proto", type="string", - help="protocol to use, one of: accel, accelc, binary, compact, json") + help="protocol to use, one of: accel, accelc, binary, compact, json, multi, multia, multiac, multic, multih, multij") parser.add_option('--transport', dest="trans", type="string", help="transport to use, one of: buffered, framed, http") + parser.add_option('--domain-socket', dest="domain_socket", type="string", + help="Unix domain socket path") parser.add_option('--container-limit', dest='container_limit', type='int', default=None) parser.add_option('--string-limit', dest='string_limit', type='int', default=None) parser.set_defaults(port=9090, verbose=1, proto='binary', transport='buffered') @@ -324,11 +396,11 @@ def exit_gracefully(signum, frame): logging.basicConfig(level=options.verbose) sys.path.insert(0, os.path.join(SCRIPT_DIR, options.genpydir)) - sys.path.insert(0, local_libpath()) - from ThriftTest import ThriftTest + from ThriftTest import ThriftTest, SecondService from ThriftTest.ttypes import Xtruct, Xception, Xception2, Insanity from thrift.Thrift import TException + from thrift.TMultiplexedProcessor import TMultiplexedProcessor from thrift.transport import THeaderTransport from thrift.transport import TTransport from thrift.transport import TSocket diff --git a/test/rb/Makefile.am b/test/rb/Makefile.am index cfdc1496efd..39109340d8c 100644 --- a/test/rb/Makefile.am +++ b/test/rb/Makefile.am @@ -30,3 +30,8 @@ if HAVE_BUNDLER $(BUNDLER) exec $(RUBY) -I. test_suite.rb endif +clean-local: + $(RM) -r gen-rb/ + +dist-hook: + $(RM) -r $(distdir)/gen-rb/ diff --git a/test/rs/Cargo.toml b/test/rs/Cargo.toml index 9b35e3c4102..8a85d7c7e92 100644 --- a/test/rs/Cargo.toml +++ b/test/rs/Cargo.toml @@ -1,16 +1,15 @@ [package] name = "thrift-test" version = "0.1.0" +edition = "2018" license = "Apache-2.0" authors = ["Apache Thrift Developers "] publish = false [dependencies] -clap = "<2.28.0" -env_logger = "0.4.0" -log = "0.3.7" -ordered-float = "0.3.0" -try_from = "0.2.0" +clap = "2.33" +env_logger = "0.8" +log = "0.4" [dependencies.thrift] path = "../../lib/rs" diff --git a/test/rs/Makefile.am b/test/rs/Makefile.am index 54905b4ef5c..4b061eae064 100644 --- a/test/rs/Makefile.am +++ b/test/rs/Makefile.am @@ -34,7 +34,7 @@ clean-local: EXTRA_DIST = \ Cargo.toml \ - src/lib.rs \ + src/lib.rs \ src/bin/test_server.rs \ src/bin/test_client.rs diff --git a/test/rs/src/bin/test_client.rs b/test/rs/src/bin/test_client.rs index 29b5b88e888..6cbc238ae41 100644 --- a/test/rs/src/bin/test_client.rs +++ b/test/rs/src/bin/test_client.rs @@ -15,20 +15,15 @@ // specific language governing permissions and limitations // under the License. -#[macro_use] -extern crate log; -extern crate env_logger; +use env_logger; +use log::*; +use clap::{clap_app, value_t}; -#[macro_use] -extern crate clap; -extern crate ordered_float; -extern crate thrift; -extern crate thrift_test; // huh. I have to do this to use my lib - -use ordered_float::OrderedFloat; use std::collections::{BTreeMap, BTreeSet}; use std::fmt::Debug; +use thrift; +use thrift::OrderedFloat; use thrift::protocol::{TBinaryInputProtocol, TBinaryOutputProtocol, TCompactInputProtocol, TCompactOutputProtocol, TInputProtocol, TMultiplexedOutputProtocol, TOutputProtocol}; @@ -38,7 +33,7 @@ use thrift::transport::{ReadHalf, TBufferedReadTransport, TBufferedWriteTranspor use thrift_test::*; fn main() { - env_logger::init().expect("logger setup failed"); + env_logger::init(); debug!("initialized logger - running cross-test client"); @@ -54,7 +49,7 @@ fn main() { fn run() -> thrift::Result<()> { // unsupported options: // --domain-socket - // --named-pipe + // --pipe // --anon-pipes // --ssl // --threads @@ -65,7 +60,7 @@ fn run() -> thrift::Result<()> { (@arg host: --host +takes_value "Host on which the Thrift test server is located") (@arg port: --port +takes_value "Port on which the Thrift test server is listening") (@arg transport: --transport +takes_value "Thrift transport implementation to use (\"buffered\", \"framed\")") - (@arg protocol: --protocol +takes_value "Thrift protocol implementation to use (\"binary\", \"compact\")") + (@arg protocol: --protocol +takes_value "Thrift protocol implementation to use (\"binary\", \"compact\", \"multi\", \"multic\")") (@arg testloops: -n --testloops +takes_value "Number of times to run tests") ) .get_matches(); @@ -110,10 +105,10 @@ fn build_protocols( transport: &str, protocol: &str, service_name: &str, -) -> thrift::Result<(Box, Box)> { +) -> thrift::Result<(Box, Box)> { let (i_chan, o_chan) = tcp_channel(host, port)?; - let (i_tran, o_tran): (Box, Box) = match transport { + let (i_tran, o_tran): (Box, Box) = match transport { "buffered" => { (Box::new(TBufferedReadTransport::new(i_chan)), Box::new(TBufferedWriteTransport::new(o_chan))) @@ -125,7 +120,7 @@ fn build_protocols( unmatched => return Err(format!("unsupported transport {}", unmatched).into()), }; - let (i_prot, o_prot): (Box, Box) = match protocol { + let (i_prot, o_prot): (Box, Box) = match protocol { "binary" => { (Box::new(TBinaryInputProtocol::new(i_tran, true)), Box::new(TBinaryOutputProtocol::new(o_tran, true))) @@ -164,10 +159,10 @@ fn tcp_channel( c.split() } -type BuildThriftTestClient = ThriftTestSyncClient, Box>; -type BuiltSecondServiceClient = SecondServiceSyncClient, Box>; +type BuildThriftTestClient = ThriftTestSyncClient, Box>; +type BuiltSecondServiceClient = SecondServiceSyncClient, Box>; -#[cfg_attr(feature = "cargo-clippy", allow(cyclomatic_complexity))] +#[allow(clippy::cognitive_complexity)] fn make_thrift_calls( thrift_test_client: &mut BuildThriftTestClient, second_service_client: &mut Option, @@ -191,7 +186,7 @@ fn make_thrift_calls( verify_expected_result(thrift_test_client.test_byte(42), 42)?; info!("testi32"); - verify_expected_result(thrift_test_client.test_i32(1159348374), 1159348374)?; + verify_expected_result(thrift_test_client.test_i32(1_159_348_374), 1_159_348_374)?; info!("testi64"); // try!(verify_expected_result(thrift_test_client.test_i64(-8651829879438294565), @@ -231,14 +226,14 @@ fn make_thrift_calls( let x_snd = Xtruct { string_thing: Some("foo".to_owned()), byte_thing: Some(12), - i32_thing: Some(219129), - i64_thing: Some(12938492818), + i32_thing: Some(219_129), + i64_thing: Some(12_938_492_818), }; let x_cmp = Xtruct { string_thing: Some("foo".to_owned()), byte_thing: Some(12), - i32_thing: Some(219129), - i64_thing: Some(12938492818), + i32_thing: Some(219_129), + i64_thing: Some(12_938_492_818), }; verify_expected_result(thrift_test_client.test_struct(x_snd), x_cmp)?; } @@ -264,11 +259,11 @@ fn make_thrift_calls( Xtruct { string_thing: Some("foo".to_owned()), byte_thing: Some(1), - i32_thing: Some(324382098), - i64_thing: Some(12938492818), + i32_thing: Some(324_382_098), + i64_thing: Some(12_938_492_818), }, ), - i32_thing: Some(293481098), + i32_thing: Some(293_481_098), }; let x_cmp = Xtruct2 { byte_thing: Some(32), @@ -276,11 +271,11 @@ fn make_thrift_calls( Xtruct { string_thing: Some("foo".to_owned()), byte_thing: Some(1), - i32_thing: Some(324382098), - i64_thing: Some(12938492818), + i32_thing: Some(324_382_098), + i64_thing: Some(12_938_492_818), }, ), - i32_thing: Some(293481098), + i32_thing: Some(293_481_098), }; verify_expected_result(thrift_test_client.test_nest(x_snd), x_cmp)?; } @@ -314,12 +309,12 @@ fn make_thrift_calls( info!("testSet"); { let mut s_snd: BTreeSet = BTreeSet::new(); - s_snd.insert(293481); + s_snd.insert(293_481); s_snd.insert(23); s_snd.insert(3234); let mut s_cmp: BTreeSet = BTreeSet::new(); - s_cmp.insert(293481); + s_cmp.insert(293_481); s_cmp.insert(23); s_cmp.insert(3234); @@ -386,12 +381,12 @@ fn make_thrift_calls( let s_cmp = Xtruct { string_thing: Some("Hello2".to_owned()), byte_thing: Some(1), - i32_thing: Some(-123948), - i64_thing: Some(-19234123981), + i32_thing: Some(-123_948), + i64_thing: Some(-19_234_123_981), }; verify_expected_result( - thrift_test_client.test_multi(1, -123948, -19234123981, m_snd, Numberz::Eight, 81), + thrift_test_client.test_multi(1, -123_948, -19_234_123_981, m_snd, Numberz::Eight, 81), s_cmp, )?; } @@ -429,8 +424,8 @@ fn make_thrift_calls( Xtruct { string_thing: Some("baz".to_owned()), byte_thing: Some(0), - i32_thing: Some(3948539), - i64_thing: Some(-12938492), + i32_thing: Some(3_948_539), + i64_thing: Some(-12_938_492), }, ); @@ -453,7 +448,7 @@ fn make_thrift_calls( s_cmp.insert(1 as UserId, s_cmp_nested_1); s_cmp.insert(2 as UserId, s_cmp_nested_2); - verify_expected_result(thrift_test_client.test_insanity(insanity.clone()), s_cmp)?; + verify_expected_result(thrift_test_client.test_insanity(insanity), s_cmp)?; } info!("testException - remote throws Xception"); @@ -583,7 +578,6 @@ fn make_thrift_calls( thrift_test_client.test_void() } -#[cfg_attr(feature = "cargo-clippy", allow(needless_pass_by_value))] fn verify_expected_result( actual: Result, expected: T, diff --git a/test/rs/src/bin/test_server.rs b/test/rs/src/bin/test_server.rs index 81c1ec7046d..74be12d488d 100644 --- a/test/rs/src/bin/test_server.rs +++ b/test/rs/src/bin/test_server.rs @@ -15,21 +15,16 @@ // specific language governing permissions and limitations // under the License. -#[macro_use] -extern crate log; -extern crate env_logger; +use env_logger; +use log::*; +use clap::{clap_app, value_t}; -#[macro_use] -extern crate clap; -extern crate ordered_float; -extern crate thrift; -extern crate thrift_test; - -use ordered_float::OrderedFloat; use std::collections::{BTreeMap, BTreeSet}; use std::thread; use std::time::Duration; +use thrift; +use thrift::OrderedFloat; use thrift::protocol::{TBinaryInputProtocolFactory, TBinaryOutputProtocolFactory, TCompactInputProtocolFactory, TCompactOutputProtocolFactory, TInputProtocolFactory, TOutputProtocolFactory}; @@ -40,7 +35,7 @@ use thrift::transport::{TBufferedReadTransportFactory, TBufferedWriteTransportFa use thrift_test::*; fn main() { - env_logger::init().expect("logger setup failed"); + env_logger::init(); debug!("initialized logger - running cross-test server"); @@ -57,7 +52,7 @@ fn run() -> thrift::Result<()> { // unsupported options: // --domain-socket - // --named-pipe + // --pipe // --ssl let matches = clap_app!(rust_test_client => (version: "1.0") @@ -80,8 +75,8 @@ fn run() -> thrift::Result<()> { info!("binding to {}", listen_address); - let (i_transport_factory, o_transport_factory): (Box, - Box) = + let (i_transport_factory, o_transport_factory): (Box, + Box) = match &*transport { "buffered" => { (Box::new(TBufferedReadTransportFactory::new()), @@ -96,8 +91,8 @@ fn run() -> thrift::Result<()> { } }; - let (i_protocol_factory, o_protocol_factory): (Box, - Box) = + let (i_protocol_factory, o_protocol_factory): (Box, + Box) = match &*protocol { "binary" | "multi" | "multi:binary" => { (Box::new(TBinaryInputProtocolFactory::new()), @@ -274,7 +269,7 @@ impl ThriftTestSyncHandler for ThriftTestSyncHandlerImpl { info!("testInsanity({:?})", argument); let mut map_0: BTreeMap = BTreeMap::new(); map_0.insert(Numberz::Two, argument.clone()); - map_0.insert(Numberz::Three, argument.clone()); + map_0.insert(Numberz::Three, argument); let mut map_1: BTreeMap = BTreeMap::new(); let insanity = Insanity { diff --git a/test/rs/src/lib.rs b/test/rs/src/lib.rs index 479bf90515c..3c7cfc09e80 100644 --- a/test/rs/src/lib.rs +++ b/test/rs/src/lib.rs @@ -15,9 +15,9 @@ // specific language governing permissions and limitations // under the License. -extern crate ordered_float; -extern crate thrift; -extern crate try_from; + + + mod thrift_test; -pub use thrift_test::*; +pub use crate::thrift_test::*; diff --git a/test/tests.json b/test/tests.json index 43d6dedae20..a8dbef7d4c7 100644 --- a/test/tests.json +++ b/test/tests.json @@ -32,7 +32,8 @@ "framed" ], "sockets": [ - "ip" + "ip", + "domain" ], "protocols": [ "binary", @@ -75,7 +76,8 @@ "transports": [ "http", "buffered", - "framed" + "framed", + "zlib" ], "sockets": [ "ip", @@ -115,7 +117,8 @@ "protocols": [ "binary", "compact", - "json" + "json", + "header" ], "workdir": "go/bin" }, @@ -150,7 +153,8 @@ "transports": [ "buffered", "framed", - "framed:fastframed" + "framed:fastframed", + "zlib" ], "sockets": [ "ip", @@ -189,7 +193,8 @@ "transports": [ "buffered", "framed", - "http" + "http", + "websocket" ], "sockets": [ "ip", @@ -199,7 +204,8 @@ "protocols": [ "compact", "binary", - "json" + "json", + "header" ], "workdir": "../lib/nodejs/test" }, @@ -251,13 +257,6 @@ "--verbose", "--host=localhost", "--genpydir=gen-py" - ], - "protocols": [ - "multi", - "multi:multia", - "multic", - "multic:multiac", - "multij" ] }, "transports": [ @@ -268,15 +267,24 @@ ], "sockets": [ "ip", - "ip-ssl" + "ip-ssl", + "domain" ], "protocols": [ - "compact", "binary", - "json", "binary:accel", + "compact", "compact:accelc", - "header" + "header", + "json", + "multi", + "multi:multia", + "multia", + "multiac", + "multic", + "multic:multiac", + "multih", + "multij" ], "workdir": "py" }, @@ -299,13 +307,6 @@ "TestClient.py", "--host=localhost", "--genpydir=gen-py" - ], - "protocols": [ - "multi", - "multi:multia", - "multic", - "multic:multiac", - "multij" ] }, "transports": [ @@ -316,15 +317,24 @@ ], "sockets": [ "ip", - "ip-ssl" + "ip-ssl", + "domain" ], "protocols": [ - "compact", "binary", - "json", "binary:accel", + "compact", "compact:accelc", - "header" + "header", + "json", + "multi", + "multi:multia", + "multia", + "multiac", + "multic", + "multic:multiac", + "multih", + "multij" ], "workdir": "py" }, @@ -357,7 +367,8 @@ "buffered", "http", "framed", - "zlib" + "zlib", + "websocket" ], "sockets": [ "ip", @@ -385,7 +396,7 @@ ] }, "client": { - "timeout": 5, + "timeout": 10, "command": [ "ruby", "../integration/TestClient.rb", @@ -410,48 +421,7 @@ "workdir": "rb/gen-rb" }, { - "name": "csharp", - "env": { - "MONO_PATH": "../../lib/csharp/" - }, - "transports": [ - "buffered", - "framed" - ], - "sockets": [ - "ip", - "ip-ssl" - ], - "protocols": [ - "binary", - "compact", - "json" - ], - "server": { - "command": [ - "mono", - "TestClientServer.exe", - "server" - ] - }, - "client": { - "timeout": 9, - "command": [ - "mono", - "TestClientServer.exe", - "client" - ], - "protocols": [ - "multi", - "multic", - "multi:binary", - "multic:compact" - ] - }, - "workdir": "csharp" - }, - { - "name": "netcore", + "name": "netstd", "transports": [ "buffered", "framed" @@ -484,7 +454,7 @@ "client" ] }, - "workdir": "netcore" + "workdir": "netstd" }, { "name": "perl", @@ -543,8 +513,9 @@ ], "protocols": [ "binary", + "binary:accel", "compact", - "binary:accel" + "json" ], "command": [ "php", @@ -562,6 +533,7 @@ { "name": "dart", "client": { + "timeout": 30, "transports": [ "buffered", "framed", @@ -577,7 +549,9 @@ ], "command": [ "dart", - "test_client/bin/main.dart" + "--enable-asserts", + "test_client/bin/main.dart", + "--verbose" ] }, "workdir": "dart" @@ -603,9 +577,9 @@ "true", "-noshell", "-pa", - "../../lib/erl/ebin/", + "../../lib/erl/_build/default/lib/thrift/ebin/", "-pa", - "./ebin", + "./_build/default/lib/thrift_test/ebin", "-s", "test_client", "-s", @@ -621,9 +595,9 @@ "true", "-noshell", "-pa", - "../../lib/erl/ebin/", + "../../lib/erl/_build/default/lib/thrift/ebin/", "-pa", - "./ebin", + "./_build/default/lib/thrift_test/ebin", "-s", "test_thrift_server", "-extra" diff --git a/test/threads/ThreadsClient.cpp b/test/threads/ThreadsClient.cpp index 9306a3f2528..e8bd79e697e 100644 --- a/test/threads/ThreadsClient.cpp +++ b/test/threads/ThreadsClient.cpp @@ -27,7 +27,7 @@ #include #include #include -#include +#include #if _WIN32 #include #endif diff --git a/test/threads/ThreadsServer.cpp b/test/threads/ThreadsServer.cpp index a267c3b90c4..3811b60c5eb 100644 --- a/test/threads/ThreadsServer.cpp +++ b/test/threads/ThreadsServer.cpp @@ -28,7 +28,7 @@ #include #include #include -#include +#include #if _WIN32 #include #endif @@ -118,8 +118,8 @@ int main(int argc, char **argv) { /* shared_ptr threadManager = ThreadManager::newSimpleThreadManager(10); - shared_ptr threadFactory = - shared_ptr(new PlatformThreadFactory()); + shared_ptr threadFactory = + shared_ptr(new ThreadFactory()); threadManager->threadFactory(threadFactory); threadManager->start(); diff --git a/tutorial/Makefile.am b/tutorial/Makefile.am index 0499460aaf4..484b485ee16 100755 --- a/tutorial/Makefile.am +++ b/tutorial/Makefile.am @@ -58,8 +58,8 @@ if WITH_HAXE SUBDIRS += haxe endif -if WITH_DOTNETCORE -SUBDIRS += netcore +if WITH_DOTNET +SUBDIRS += netstd endif if WITH_GO @@ -82,6 +82,14 @@ if WITH_CL SUBDIRS += cl endif +if WITH_PERL +SUBDIRS += perl +endif + +if WITH_PHP +SUBDIRS += php +endif + # # generate html for ThriftTest.thrift # @@ -97,14 +105,11 @@ endif # EXTRA_DIST to be included in the release EXTRA_DIST = \ as3 \ - csharp \ d \ delphi \ erl \ hs \ ocaml \ - perl \ - php \ shared.thrift \ tutorial.thrift \ README.md diff --git a/tutorial/README.md b/tutorial/README.md index 7772bf3e68a..9fafa9edd34 100644 --- a/tutorial/README.md +++ b/tutorial/README.md @@ -30,10 +30,10 @@ Tutorial 2) Read tutorial.thrift to learn about the syntax of a Thrift file 3) Compile the code for the language of your choice: - +``` $ thrift $ thrift -r --gen cpp tutorial.thrift - +``` 4) Take a look at the generated code. 5) Look in the language directories for sample client/server code. diff --git a/tutorial/c_glib/Makefile.am b/tutorial/c_glib/Makefile.am index 4dbe655ef4c..f37649495f4 100755 --- a/tutorial/c_glib/Makefile.am +++ b/tutorial/c_glib/Makefile.am @@ -16,7 +16,7 @@ # specific language governing permissions and limitations # under the License. # -AUTOMAKE_OPTIONS = subdir-objects serial-tests +AUTOMAKE_OPTIONS = subdir-objects serial-tests nostdinc BUILT_SOURCES = \ gen-c_glib/calculator.h \ @@ -24,7 +24,7 @@ BUILT_SOURCES = \ gen-c_glib/shared_types.h \ gen-c_glib/tutorial_types.h -AM_CFLAGS = -g -Wall -Wextra -pedantic $(GLIB_CFLAGS) $(GOBJECT_CFLAGS) $(OPENSSL_INCLUDES) @GCOV_CFLAGS@ +AM_CFLAGS = -g -Wall -Wextra -pedantic $(GLIB_CFLAGS) $(GOBJECT_CFLAGS) $(OPENSSL_INCLUDES) @GCOV_CFLAGS@ -I$(top_builddir)/lib/c_glib/src/thrift AM_CPPFLAGS = -I$(top_srcdir)/lib/c_glib/src -Igen-c_glib AM_LDFLAGS = $(GLIB_LIBS) $(GOBJECT_LIBS) $(OPENSSL_LDFLAGS) $(OPENSSL_LIBS) @GCOV_LDFLAGS@ diff --git a/tutorial/cl/Makefile.am b/tutorial/cl/Makefile.am index 2b2013a3c99..70c5e0730a5 100755 --- a/tutorial/cl/Makefile.am +++ b/tutorial/cl/Makefile.am @@ -16,8 +16,8 @@ # specific language governing permissions and limitations # under the License. -setup-local-lisp-env: ensure-externals.sh - bash ensure-externals.sh +setup-local-lisp-env: ../../lib/cl/ensure-externals.sh + bash ../../lib/cl/ensure-externals.sh gen-cl: $(top_srcdir)/tutorial/tutorial.thrift $(THRIFT) --gen cl -r $< @@ -62,4 +62,5 @@ EXTRA_DIST = \ shared-implementation.lisp \ thrift-tutorial.asd \ make-tutorial-server.lisp \ - make-tutorial-client.lisp + make-tutorial-client.lisp \ + load-locally.lisp diff --git a/tutorial/cl/ensure-externals.sh b/tutorial/cl/ensure-externals.sh deleted file mode 120000 index 5ae8c5657c6..00000000000 --- a/tutorial/cl/ensure-externals.sh +++ /dev/null @@ -1 +0,0 @@ -../../lib/cl/ensure-externals.sh \ No newline at end of file diff --git a/tutorial/cpp/CMakeLists.txt b/tutorial/cpp/CMakeLists.txt index 8634b4144b2..b250a2c1c4b 100644 --- a/tutorial/cpp/CMakeLists.txt +++ b/tutorial/cpp/CMakeLists.txt @@ -17,7 +17,8 @@ # under the License. # -include_directories(SYSTEM "${Boost_INCLUDE_DIRS}") +include(BoostMacros) +REQUIRE_BOOST_HEADERS() #Make sure gen-cpp files can be included include_directories("${CMAKE_CURRENT_BINARY_DIR}") @@ -29,7 +30,6 @@ include(ThriftMacros) set(tutorialgencpp_SOURCES gen-cpp/Calculator.cpp gen-cpp/SharedService.cpp - gen-cpp/shared_constants.cpp gen-cpp/shared_types.cpp gen-cpp/tutorial_constants.cpp gen-cpp/tutorial_types.cpp @@ -37,7 +37,7 @@ set(tutorialgencpp_SOURCES add_library(tutorialgencpp STATIC ${tutorialgencpp_SOURCES}) LINK_AGAINST_THRIFT_LIBRARY(tutorialgencpp thrift) -add_custom_command(OUTPUT gen-cpp/Calculator.cpp gen-cpp/SharedService.cpp gen-cpp/shared_constants.cpp gen-cpp/shared_types.cpp gen-cpp/tutorial_constants.cpp gen-cpp/tutorial_types.cpp +add_custom_command(OUTPUT gen-cpp/Calculator.cpp gen-cpp/SharedService.cpp gen-cpp/shared_types.cpp gen-cpp/tutorial_constants.cpp gen-cpp/tutorial_types.cpp COMMAND ${THRIFT_COMPILER} --gen cpp -r ${PROJECT_SOURCE_DIR}/tutorial/tutorial.thrift ) diff --git a/tutorial/cpp/CppClient.cpp b/tutorial/cpp/CppClient.cpp index f10c72557e2..520841143b4 100644 --- a/tutorial/cpp/CppClient.cpp +++ b/tutorial/cpp/CppClient.cpp @@ -22,7 +22,6 @@ #include #include #include -#include #include "../gen-cpp/Calculator.h" @@ -35,9 +34,9 @@ using namespace tutorial; using namespace shared; int main() { - stdcxx::shared_ptr socket(new TSocket("localhost", 9090)); - stdcxx::shared_ptr transport(new TBufferedTransport(socket)); - stdcxx::shared_ptr protocol(new TBinaryProtocol(transport)); + std::shared_ptr socket(new TSocket("localhost", 9090)); + std::shared_ptr transport(new TBufferedTransport(socket)); + std::shared_ptr protocol(new TBinaryProtocol(transport)); CalculatorClient client(protocol); try { diff --git a/tutorial/cpp/CppServer.cpp b/tutorial/cpp/CppServer.cpp index 80b100e59a9..635afefda0d 100644 --- a/tutorial/cpp/CppServer.cpp +++ b/tutorial/cpp/CppServer.cpp @@ -18,7 +18,7 @@ */ #include -#include +#include #include #include #include @@ -27,7 +27,6 @@ #include #include #include -#include #include #include @@ -47,16 +46,16 @@ using namespace shared; class CalculatorHandler : public CalculatorIf { public: - CalculatorHandler() {} + CalculatorHandler() = default; - void ping() { cout << "ping()" << endl; } + void ping() override { cout << "ping()" << endl; } - int32_t add(const int32_t n1, const int32_t n2) { + int32_t add(const int32_t n1, const int32_t n2) override { cout << "add(" << n1 << ", " << n2 << ")" << endl; return n1 + n2; } - int32_t calculate(const int32_t logid, const Work& work) { + int32_t calculate(const int32_t logid, const Work& work) override { cout << "calculate(" << logid << ", " << work << ")" << endl; int32_t val; @@ -95,12 +94,12 @@ class CalculatorHandler : public CalculatorIf { return val; } - void getStruct(SharedStruct& ret, const int32_t logid) { + void getStruct(SharedStruct& ret, const int32_t logid) override { cout << "getStruct(" << logid << ")" << endl; ret = log[logid]; } - void zip() { cout << "zip()" << endl; } + void zip() override { cout << "zip()" << endl; } protected: map log; @@ -114,10 +113,10 @@ class CalculatorHandler : public CalculatorIf { */ class CalculatorCloneFactory : virtual public CalculatorIfFactory { public: - virtual ~CalculatorCloneFactory() {} - virtual CalculatorIf* getHandler(const ::apache::thrift::TConnectionInfo& connInfo) + ~CalculatorCloneFactory() override = default; + CalculatorIf* getHandler(const ::apache::thrift::TConnectionInfo& connInfo) override { - stdcxx::shared_ptr sock = stdcxx::dynamic_pointer_cast(connInfo.transport); + std::shared_ptr sock = std::dynamic_pointer_cast(connInfo.transport); cout << "Incoming connection\n"; cout << "\tSocketInfo: " << sock->getSocketInfo() << "\n"; cout << "\tPeerHost: " << sock->getPeerHost() << "\n"; @@ -125,25 +124,25 @@ class CalculatorCloneFactory : virtual public CalculatorIfFactory { cout << "\tPeerPort: " << sock->getPeerPort() << "\n"; return new CalculatorHandler; } - virtual void releaseHandler( ::shared::SharedServiceIf* handler) { + void releaseHandler( ::shared::SharedServiceIf* handler) override { delete handler; } }; int main() { TThreadedServer server( - stdcxx::make_shared(stdcxx::make_shared()), - stdcxx::make_shared(9090), //port - stdcxx::make_shared(), - stdcxx::make_shared()); + std::make_shared(std::make_shared()), + std::make_shared(9090), //port + std::make_shared(), + std::make_shared()); /* // if you don't need per-connection state, do the following instead TThreadedServer server( - stdcxx::make_shared(stdcxx::make_shared()), - stdcxx::make_shared(9090), //port - stdcxx::make_shared(), - stdcxx::make_shared()); + std::make_shared(std::make_shared()), + std::make_shared(9090), //port + std::make_shared(), + std::make_shared()); */ /** @@ -151,25 +150,25 @@ int main() { // This server only allows one connection at a time, but spawns no threads TSimpleServer server( - stdcxx::make_shared(stdcxx::make_shared()), - stdcxx::make_shared(9090), - stdcxx::make_shared(), - stdcxx::make_shared()); + std::make_shared(std::make_shared()), + std::make_shared(9090), + std::make_shared(), + std::make_shared()); const int workerCount = 4; - stdcxx::shared_ptr threadManager = + std::shared_ptr threadManager = ThreadManager::newSimpleThreadManager(workerCount); threadManager->threadFactory( - stdcxx::make_shared()); + std::make_shared()); threadManager->start(); // This server allows "workerCount" connection at a time, and reuses threads TThreadPoolServer server( - stdcxx::make_shared(stdcxx::make_shared()), - stdcxx::make_shared(9090), - stdcxx::make_shared(), - stdcxx::make_shared(), + std::make_shared(std::make_shared()), + std::make_shared(9090), + std::make_shared(), + std::make_shared(), threadManager); */ diff --git a/tutorial/cpp/Makefile.am b/tutorial/cpp/Makefile.am index 49cf3be9758..77fd6d579cd 100755 --- a/tutorial/cpp/Makefile.am +++ b/tutorial/cpp/Makefile.am @@ -16,7 +16,7 @@ # specific language governing permissions and limitations # under the License. # -AUTOMAKE_OPTIONS = subdir-objects serial-tests +AUTOMAKE_OPTIONS = subdir-objects serial-tests nostdinc BUILT_SOURCES = gen-cpp/shared_types.cpp \ gen-cpp/tutorial_types.cpp @@ -27,8 +27,6 @@ nodist_libtutorialgencpp_la_SOURCES = \ gen-cpp/Calculator.h \ gen-cpp/SharedService.cpp \ gen-cpp/SharedService.h \ - gen-cpp/shared_constants.cpp \ - gen-cpp/shared_constants.h \ gen-cpp/shared_types.cpp \ gen-cpp/shared_types.h \ gen-cpp/tutorial_constants.cpp \ @@ -61,7 +59,7 @@ TutorialClient_LDADD = \ # # Common thrift code generation rules # -gen-cpp/Calculator.cpp gen-cpp/SharedService.cpp gen-cpp/shared_constants.cpp gen-cpp/shared_types.cpp gen-cpp/tutorial_constants.cpp gen-cpp/tutorial_types.cpp: $(top_srcdir)/tutorial/tutorial.thrift +gen-cpp/Calculator.cpp gen-cpp/SharedService.cpp gen-cpp/shared_types.cpp gen-cpp/tutorial_constants.cpp gen-cpp/tutorial_types.cpp: $(top_srcdir)/tutorial/tutorial.thrift $(THRIFT) --gen cpp -r $< AM_CPPFLAGS = $(BOOST_CPPFLAGS) $(LIBEVENT_CPPFLAGS) -I$(top_srcdir)/lib/cpp/src -Igen-cpp diff --git a/tutorial/csharp/CsharpClient/CsharpClient.cs b/tutorial/csharp/CsharpClient/CsharpClient.cs deleted file mode 100644 index 113a4722345..00000000000 --- a/tutorial/csharp/CsharpClient/CsharpClient.cs +++ /dev/null @@ -1,92 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -using System; -using Thrift; -using Thrift.Protocol; -using Thrift.Server; -using Thrift.Transport; - - -namespace CSharpTutorial -{ - public class CSharpClient - { - public static void Main() - { - try - { - TTransport transport = new TSocket("localhost", 9090); - TProtocol protocol = new TBinaryProtocol(transport); - Calculator.Client client = new Calculator.Client(protocol); - - transport.Open(); - try - { - client.ping(); - Console.WriteLine("ping()"); - - int sum = client.add(1, 1); - Console.WriteLine("1+1={0}", sum); - - Work work = new Work(); - - work.Op = Operation.DIVIDE; - work.Num1 = 1; - work.Num2 = 0; - try - { - int quotient = client.calculate(1, work); - Console.WriteLine("Whoa we can divide by 0"); - } - catch (InvalidOperation io) - { - Console.WriteLine("Invalid operation: " + io.Why); - } - - work.Op = Operation.SUBTRACT; - work.Num1 = 15; - work.Num2 = 10; - try - { - int diff = client.calculate(1, work); - Console.WriteLine("15-10={0}", diff); - } - catch (InvalidOperation io) - { - Console.WriteLine("Invalid operation: " + io.Why); - } - - SharedStruct log = client.getStruct(1); - Console.WriteLine("Check log: {0}", log.Value); - - } - finally - { - transport.Close(); - } - } - catch (TApplicationException x) - { - Console.WriteLine(x.StackTrace); - } - - } - } -} diff --git a/tutorial/csharp/CsharpClient/CsharpClient.csproj b/tutorial/csharp/CsharpClient/CsharpClient.csproj deleted file mode 100644 index 1ea7ff63902..00000000000 --- a/tutorial/csharp/CsharpClient/CsharpClient.csproj +++ /dev/null @@ -1,110 +0,0 @@ - - - - - Debug - AnyCPU - 9.0.30729 - 2.0 - {18F24087-4760-43DA-ACAB-7B9F0E096B11} - Exe - Properties - CsharpClient - CsharpClient - v3.5 - 512 - - - true - full - false - bin\Debug\ - DEBUG;TRACE - prompt - 4 - - - pdbonly - true - bin\Release\ - TRACE - prompt - 4 - - - - - 3.5 - - - 3.5 - - - 3.5 - - - - - - - Calculator.cs - - - InvalidOperation.cs - - - Operation.cs - - - SharedService.cs - - - SharedStruct.cs - - - tutorial.Constants.cs - - - Work.cs - - - - - - - {499eb63c-d74c-47e8-ae48-a2fc94538e9d} - Thrift - - - - - pushd "$(SolutionDir)" -thrift -gen csharp -r ../tutorial.thrift -popd - - - - \ No newline at end of file diff --git a/tutorial/csharp/CsharpClient/Properties/AssemblyInfo.cs b/tutorial/csharp/CsharpClient/Properties/AssemblyInfo.cs deleted file mode 100644 index 2e7f736d76b..00000000000 --- a/tutorial/csharp/CsharpClient/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -using System.Reflection; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; - -// General Information about an assembly is controlled through the following -// set of attributes. Change these attribute values to modify the information -// associated with an assembly. -[assembly: AssemblyTitle("CsharpClient")] -[assembly: AssemblyDescription("")] -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("The Apache Software Foundation")] -[assembly: AssemblyProduct("Thrift")] -[assembly: AssemblyCopyright("The Apache Software Foundation")] -[assembly: AssemblyTrademark("")] -[assembly: AssemblyCulture("")] - -// Setting ComVisible to false makes the types in this assembly not visible -// to COM components. If you need to access a type in this assembly from -// COM, set the ComVisible attribute to true on that type. -[assembly: ComVisible(false)] - -// The following GUID is for the ID of the typelib if this project is exposed to COM -[assembly: Guid("1a461214-fa28-452a-bd1d-d23ca8e947e3")] - -// Version information for an assembly consists of the following four values: -// -// Major Version -// Minor Version -// Build Number -// Revision -// -// You can specify all the values or you can default the Build and Revision Numbers -// by using the '*' as shown below: -// [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("0.12.0.0")] -[assembly: AssemblyFileVersion("0.12.0.0")] diff --git a/tutorial/csharp/CsharpServer/CsharpServer.cs b/tutorial/csharp/CsharpServer/CsharpServer.cs deleted file mode 100644 index 439790aaf59..00000000000 --- a/tutorial/csharp/CsharpServer/CsharpServer.cs +++ /dev/null @@ -1,129 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -using System; -using System.Collections.Generic; -using Thrift.Server; -using Thrift.Transport; - -namespace CSharpTutorial -{ - public class CalculatorHandler : Calculator.Iface - { - Dictionary log; - - public CalculatorHandler() - { - log = new Dictionary(); - } - - public void ping() - { - Console.WriteLine("ping()"); - } - - public int add(int n1, int n2) - { - Console.WriteLine("add({0},{1})", n1, n2); - return n1 + n2; - } - - public int calculate(int logid, Work work) - { - Console.WriteLine("calculate({0}, [{1},{2},{3}])", logid, work.Op, work.Num1, work.Num2); - int val = 0; - switch (work.Op) - { - case Operation.ADD: - val = work.Num1 + work.Num2; - break; - - case Operation.SUBTRACT: - val = work.Num1 - work.Num2; - break; - - case Operation.MULTIPLY: - val = work.Num1 * work.Num2; - break; - - case Operation.DIVIDE: - if (work.Num2 == 0) - { - InvalidOperation io = new InvalidOperation(); - io.WhatOp = (int)work.Op; - io.Why = "Cannot divide by 0"; - throw io; - } - val = work.Num1 / work.Num2; - break; - - default: - { - InvalidOperation io = new InvalidOperation(); - io.WhatOp = (int)work.Op; - io.Why = "Unknown operation"; - throw io; - } - } - - SharedStruct entry = new SharedStruct(); - entry.Key = logid; - entry.Value = val.ToString(); - log[logid] = entry; - - return val; - } - - public SharedStruct getStruct(int key) - { - Console.WriteLine("getStruct({0})", key); - return log[key]; - } - - public void zip() - { - Console.WriteLine("zip()"); - } - } - - public class CSharpServer - { - public static void Main() - { - try - { - CalculatorHandler handler = new CalculatorHandler(); - Calculator.Processor processor = new Calculator.Processor(handler); - TServerTransport serverTransport = new TServerSocket(9090); - TServer server = new TSimpleServer(processor, serverTransport); - - // Use this for a multithreaded server - // server = new TThreadPoolServer(processor, serverTransport); - - Console.WriteLine("Starting the server..."); - server.Serve(); - } - catch (Exception x) - { - Console.WriteLine(x.StackTrace); - } - Console.WriteLine("done."); - } - } -} diff --git a/tutorial/csharp/CsharpServer/CsharpServer.csproj b/tutorial/csharp/CsharpServer/CsharpServer.csproj deleted file mode 100644 index 07481806c33..00000000000 --- a/tutorial/csharp/CsharpServer/CsharpServer.csproj +++ /dev/null @@ -1,111 +0,0 @@ - - - - - Debug - AnyCPU - 9.0.30729 - 2.0 - {66707BAE-BBF9-4F03-B53E-BE3AD58322F8} - Exe - Properties - CsharpServer - CsharpServer - v3.5 - 512 - - - true - full - false - bin\Debug\ - DEBUG;TRACE - prompt - 4 - - - pdbonly - true - bin\Release\ - TRACE - prompt - 4 - - - - - 3.5 - - - 3.5 - - - 3.5 - - - - - - - Calculator.cs - - - InvalidOperation.cs - - - Operation.cs - - - SharedService.cs - - - SharedStruct.cs - - - tutorial.Constants.cs - - - Work.cs - - - - - - - {499eb63c-d74c-47e8-ae48-a2fc94538e9d} - Thrift - - - - - pushd "$(SolutionDir)" -thrift -gen csharp -r ../tutorial.thrift -popd - - - - - \ No newline at end of file diff --git a/tutorial/csharp/CsharpServer/Properties/AssemblyInfo.cs b/tutorial/csharp/CsharpServer/Properties/AssemblyInfo.cs deleted file mode 100644 index 14067dd5d00..00000000000 --- a/tutorial/csharp/CsharpServer/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -using System.Reflection; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; - -// General Information about an assembly is controlled through the following -// set of attributes. Change these attribute values to modify the information -// associated with an assembly. -[assembly: AssemblyTitle("CsharpServer")] -[assembly: AssemblyDescription("")] -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("The Apache Software Foundation")] -[assembly: AssemblyProduct("Thrift")] -[assembly: AssemblyCopyright("The Apache Software Foundation")] -[assembly: AssemblyTrademark("")] -[assembly: AssemblyCulture("")] - -// Setting ComVisible to false makes the types in this assembly not visible -// to COM components. If you need to access a type in this assembly from -// COM, set the ComVisible attribute to true on that type. -[assembly: ComVisible(false)] - -// The following GUID is for the ID of the typelib if this project is exposed to COM -[assembly: Guid("e3b428f4-b2e9-4fc1-8a34-84abc4339860")] - -// Version information for an assembly consists of the following four values: -// -// Major Version -// Minor Version -// Build Number -// Revision -// -// You can specify all the values or you can default the Build and Revision Numbers -// by using the '*' as shown below: -// [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("0.12.0.0")] -[assembly: AssemblyFileVersion("0.12.0.0")] diff --git a/tutorial/csharp/tutorial.sln b/tutorial/csharp/tutorial.sln deleted file mode 100644 index ec57a188d3b..00000000000 --- a/tutorial/csharp/tutorial.sln +++ /dev/null @@ -1,39 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 11.00 -# Visual Studio 2010 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Thrift", "..\..\lib\csharp\src\Thrift.csproj", "{499EB63C-D74C-47E8-AE48-A2FC94538E9D}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CsharpClient", "CsharpClient\CsharpClient.csproj", "{18F24087-4760-43DA-ACAB-7B9F0E096B11}" - ProjectSection(ProjectDependencies) = postProject - {499EB63C-D74C-47E8-AE48-A2FC94538E9D} = {499EB63C-D74C-47E8-AE48-A2FC94538E9D} - {66707BAE-BBF9-4F03-B53E-BE3AD58322F8} = {66707BAE-BBF9-4F03-B53E-BE3AD58322F8} - EndProjectSection -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CsharpServer", "CsharpServer\CsharpServer.csproj", "{66707BAE-BBF9-4F03-B53E-BE3AD58322F8}" - ProjectSection(ProjectDependencies) = postProject - {499EB63C-D74C-47E8-AE48-A2FC94538E9D} = {499EB63C-D74C-47E8-AE48-A2FC94538E9D} - EndProjectSection -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Release|Any CPU = Release|Any CPU - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {499EB63C-D74C-47E8-AE48-A2FC94538E9D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {499EB63C-D74C-47E8-AE48-A2FC94538E9D}.Debug|Any CPU.Build.0 = Debug|Any CPU - {499EB63C-D74C-47E8-AE48-A2FC94538E9D}.Release|Any CPU.ActiveCfg = Release|Any CPU - {499EB63C-D74C-47E8-AE48-A2FC94538E9D}.Release|Any CPU.Build.0 = Release|Any CPU - {18F24087-4760-43DA-ACAB-7B9F0E096B11}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {18F24087-4760-43DA-ACAB-7B9F0E096B11}.Debug|Any CPU.Build.0 = Debug|Any CPU - {18F24087-4760-43DA-ACAB-7B9F0E096B11}.Release|Any CPU.ActiveCfg = Release|Any CPU - {18F24087-4760-43DA-ACAB-7B9F0E096B11}.Release|Any CPU.Build.0 = Release|Any CPU - {66707BAE-BBF9-4F03-B53E-BE3AD58322F8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {66707BAE-BBF9-4F03-B53E-BE3AD58322F8}.Debug|Any CPU.Build.0 = Debug|Any CPU - {66707BAE-BBF9-4F03-B53E-BE3AD58322F8}.Release|Any CPU.ActiveCfg = Release|Any CPU - {66707BAE-BBF9-4F03-B53E-BE3AD58322F8}.Release|Any CPU.Build.0 = Release|Any CPU - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection -EndGlobal diff --git a/tutorial/d/Makefile.am b/tutorial/d/Makefile.am index d8c8b2910fd..358294ce56d 100644 --- a/tutorial/d/Makefile.am +++ b/tutorial/d/Makefile.am @@ -44,3 +44,10 @@ all-local: $(PROGS) clean: $(RM) -f $(PROGS) + $(RM) -r gen-d/ + find . -type f -name '*.o' | xargs rm -f + +dist-hook: + $(RM) -f $(distdir)/$(PROGS) + $(RM) -r $(distdir)/gen-d/ + find $(destdir) -type f -name '*.o' | xargs rm -f diff --git a/tutorial/dart/Makefile.am b/tutorial/dart/Makefile.am index 0495aca6043..0b93ac859dd 100644 --- a/tutorial/dart/Makefile.am +++ b/tutorial/dart/Makefile.am @@ -25,11 +25,19 @@ gen-dart/tutorial/lib/tutorial.dart gen-dart/shared/lib/shared.dart: $(top_srcdi all-local: gen-dart/tutorial/lib/tutorial.dart pub-get clean-local: - $(RM) -r gen-* + $(RM) -r gen-*/ + find . -type d -name ".dart_tool" | xargs $(RM) -r find . -type d -name "packages" | xargs $(RM) -r find . -type f -name ".packages" | xargs $(RM) find . -type f -name "pubspec.lock" | xargs $(RM) +dist-hook: + $(RM) -r $(distdir)/gen-*/ + find $(distdir) -type d -name ".dart_tool" | xargs $(RM) -r + find $(distdir) -type d -name "packages" | xargs $(RM) -r + find $(distdir) -type f -name ".packages" | xargs $(RM) + find $(distdir) -type f -name "pubspec.lock" | xargs $(RM) + pub-get: pub-get-gen pub-get-client pub-get-console-client pub-get-server pub-get-gen: pub-get-tutorial pub-get-shared diff --git a/tutorial/dart/client/pubspec.yaml b/tutorial/dart/client/pubspec.yaml index 78ee5272224..35880ed0870 100644 --- a/tutorial/dart/client/pubspec.yaml +++ b/tutorial/dart/client/pubspec.yaml @@ -16,16 +16,15 @@ # under the License. name: tutorial_client -version: 0.12.0 +version: 0.14.0 description: A Dart client implementation of the Apache Thrift tutorial author: Apache Thrift Developers homepage: http://thrift.apache.org environment: - sdk: ">=1.13.0 <2.0.0" + sdk: ">=1.13.0 <3.0.0" dependencies: - browser: ^0.10.0 shared: path: ../gen-dart/shared thrift: diff --git a/tutorial/dart/client/web/index.html b/tutorial/dart/client/web/index.html index 64b184e5561..9d36b4388a5 100644 --- a/tutorial/dart/client/web/index.html +++ b/tutorial/dart/client/web/index.html @@ -24,8 +24,7 @@ Thrift Tutorial - - + diff --git a/tutorial/dart/console_client/pubspec.yaml b/tutorial/dart/console_client/pubspec.yaml index be7328efe11..1068f909cc6 100644 --- a/tutorial/dart/console_client/pubspec.yaml +++ b/tutorial/dart/console_client/pubspec.yaml @@ -16,17 +16,17 @@ # under the License. name: tutorial_console_client -version: 0.12.0 +version: 0.14.0 description: > A Dart console client to implementation of the Apache Thrift tutorial author: Apache Thrift Developers homepage: http://thrift.apache.org environment: - sdk: ">=1.13.0 <2.0.0" + sdk: ">=1.13.0 <3.0.0" dependencies: - args: ^0.13.0 + args: ">=0.13.0 <2.0.0" collection: ^1.1.0 shared: path: ../gen-dart/shared diff --git a/tutorial/dart/server/pubspec.yaml b/tutorial/dart/server/pubspec.yaml index 6c616b33afc..bdd74b826f9 100644 --- a/tutorial/dart/server/pubspec.yaml +++ b/tutorial/dart/server/pubspec.yaml @@ -16,16 +16,16 @@ # under the License. name: tutorial_server -version: 0.12.0 +version: 0.14.0 description: A Dart server to support the Apache Thrift tutorial author: Apache Thrift Developers homepage: http://thrift.apache.org environment: - sdk: ">=1.13.0 <2.0.0" + sdk: ">=1.13.0 <3.0.0" dependencies: - args: ^0.13.0 + args: ">=0.13.0 <2.0.0" shared: path: ../gen-dart/shared thrift: diff --git a/tutorial/delphi/DelphiClient/DelphiClient.dpr b/tutorial/delphi/DelphiClient/DelphiClient.dpr index 74d0d45c6a5..64d7d685786 100644 --- a/tutorial/delphi/DelphiClient/DelphiClient.dpr +++ b/tutorial/delphi/DelphiClient/DelphiClient.dpr @@ -26,12 +26,16 @@ uses Generics.Collections, Thrift in '..\..\..\lib\delphi\src\Thrift.pas', Thrift.Collections in '..\..\..\lib\delphi\src\Thrift.Collections.pas', + Thrift.Configuration in '..\..\..\lib\delphi\src\Thrift.Configuration.pas', Thrift.Exception in '..\..\..\lib\delphi\src\Thrift.Exception.pas', Thrift.Utils in '..\..\..\lib\delphi\src\Thrift.Utils.pas', Thrift.Stream in '..\..\..\lib\delphi\src\Thrift.Stream.pas', Thrift.Protocol in '..\..\..\lib\delphi\src\Thrift.Protocol.pas', Thrift.Server in '..\..\..\lib\delphi\src\Thrift.Server.pas', Thrift.Transport in '..\..\..\lib\delphi\src\Thrift.Transport.pas', + Thrift.Transport.WinHTTP in '..\..\..\lib\delphi\src\Thrift.Transport.WinHTTP.pas', + Thrift.Transport.MsxmlHTTP in '..\..\..\lib\delphi\src\Thrift.Transport.MsxmlHTTP.pas', + Thrift.WinHTTP in '..\..\..\lib\delphi\src\Thrift.WinHTTP.pas', Shared in '..\..\gen-delphi\Shared.pas', Tutorial in '..\..\gen-delphi\Tutorial.pas'; diff --git a/tutorial/delphi/DelphiClient/DelphiClient.dproj b/tutorial/delphi/DelphiClient/DelphiClient.dproj index dd587811020..2612f149b64 100644 --- a/tutorial/delphi/DelphiClient/DelphiClient.dproj +++ b/tutorial/delphi/DelphiClient/DelphiClient.dproj @@ -52,12 +52,16 @@ + + + + @@ -97,13 +101,13 @@ Thrift Tutorial - 0.12.0.0 + 0.14.0.0 DelphiClient Copyright © 2012 The Apache Software Foundation DelphiClient.exe Thrift - 1.0.0.0 + 0.14.0.0 diff --git a/tutorial/delphi/DelphiServer/DelphiServer.dpr b/tutorial/delphi/DelphiServer/DelphiServer.dpr index 5f42e7e1089..41a3514d018 100644 --- a/tutorial/delphi/DelphiServer/DelphiServer.dpr +++ b/tutorial/delphi/DelphiServer/DelphiServer.dpr @@ -28,12 +28,14 @@ uses Generics.Collections, Thrift in '..\..\..\lib\delphi\src\Thrift.pas', Thrift.Collections in '..\..\..\lib\delphi\src\Thrift.Collections.pas', + Thrift.Configuration in '..\..\..\lib\delphi\src\Thrift.Configuration.pas', Thrift.Exception in '..\..\..\lib\delphi\src\Thrift.Exception.pas', Thrift.Utils in '..\..\..\lib\delphi\src\Thrift.Utils.pas', Thrift.Stream in '..\..\..\lib\delphi\src\Thrift.Stream.pas', Thrift.Protocol in '..\..\..\lib\delphi\src\Thrift.Protocol.pas', Thrift.Server in '..\..\..\lib\delphi\src\Thrift.Server.pas', Thrift.Transport in '..\..\..\lib\delphi\src\Thrift.Transport.pas', + Thrift.WinHTTP in '..\..\..\lib\delphi\src\Thrift.WinHTTP.pas', Shared in '..\..\gen-delphi\Shared.pas', Tutorial in '..\..\gen-delphi\Tutorial.pas'; diff --git a/tutorial/delphi/DelphiServer/DelphiServer.dproj b/tutorial/delphi/DelphiServer/DelphiServer.dproj index 3f9fc4dbafd..f62257e2245 100644 --- a/tutorial/delphi/DelphiServer/DelphiServer.dproj +++ b/tutorial/delphi/DelphiServer/DelphiServer.dproj @@ -51,12 +51,14 @@ + + @@ -96,13 +98,13 @@ Thrift Tutorial - 0.12.0.0 + 0.14.0.0 DelphiServer Copyright © 2012 The Apache Software Foundation DelphiServer.exe Thrift - 1.0.0.0 + 0.14.0.0 diff --git a/tutorial/erl/server.sh b/tutorial/erl/server.sh deleted file mode 120000 index 26b3c58e44d..00000000000 --- a/tutorial/erl/server.sh +++ /dev/null @@ -1 +0,0 @@ -client.sh \ No newline at end of file diff --git a/tutorial/erl/server.sh b/tutorial/erl/server.sh new file mode 100755 index 00000000000..775afb62d54 --- /dev/null +++ b/tutorial/erl/server.sh @@ -0,0 +1,37 @@ +#!/bin/sh + +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +ERL_THRIFT=../../lib/erl + +if ! [ -d ${ERL_THRIFT}/ebin ]; then + echo "Please build the Thrift library by running \`make' in ${ERL_THRIFT}" + exit 1 +fi + +if ! [ -d gen-erl ]; then + ../../compiler/cpp/thrift -r --gen erl ../tutorial.thrift +fi + + +erlc -I ${ERL_THRIFT}/include -I ${ERL_THRIFT}/ebin \ + -I gen-erl -o gen-erl gen-erl/*.erl && + erlc -I ${ERL_THRIFT}/include -I gen-erl *.erl && + erl +K true -pa ${ERL_THRIFT}/ebin -pa gen-erl diff --git a/tutorial/go/src/client.go b/tutorial/go/src/client.go index e3ebe00dffb..319ca3e6eed 100644 --- a/tutorial/go/src/client.go +++ b/tutorial/go/src/client.go @@ -49,7 +49,6 @@ func handleClient(client *tutorial.CalculatorClient) (err error) { default: fmt.Println("Error during operation:", err) } - return err } else { fmt.Println("Whoa we can divide by 0 with new value:", quotient) } diff --git a/tutorial/hs/HaskellClient.hs b/tutorial/hs/HaskellClient.hs index bd29df06df1..76a8824099c 100644 --- a/tutorial/hs/HaskellClient.hs +++ b/tutorial/hs/HaskellClient.hs @@ -48,7 +48,7 @@ main = do printf "1+1=%d\n" sum - let work = Work { work_op = DIVIDE, + let work = Work { work_op = Operation_DIVIDE, work_num1 = 1, work_num2 = 0, work_comment = Nothing @@ -58,7 +58,7 @@ main = do (\e -> printf "InvalidOperation %s\n" (show (e :: InvalidOperation))) - let work = Work { work_op = SUBTRACT, + let work = Work { work_op = Operation_SUBTRACT, work_num1 = 15, work_num2 = 10, work_comment = Nothing diff --git a/tutorial/hs/HaskellServer.hs b/tutorial/hs/HaskellServer.hs index cfe13441d4a..1594ee3420e 100644 --- a/tutorial/hs/HaskellServer.hs +++ b/tutorial/hs/HaskellServer.hs @@ -64,13 +64,13 @@ instance Calculator_Iface CalculatorHandler where printf "calculate(%d, %s)\n" logid (show work) let val = case op work of - ADD -> + Operation_ADD -> num1 work + num2 work - SUBTRACT -> + Operation_SUBTRACT -> num1 work - num2 work - MULTIPLY -> + Operation_MULTIPLY -> num1 work * num2 work - DIVIDE -> + Operation_DIVIDE -> if num2 work == 0 then throw $ InvalidOperation { diff --git a/tutorial/hs/Makefile.am b/tutorial/hs/Makefile.am index a3eccc2b1c8..9c6fd8308a6 100755 --- a/tutorial/hs/Makefile.am +++ b/tutorial/hs/Makefile.am @@ -27,7 +27,12 @@ install-exec-hook: # Make sure this doesn't fail if Haskell is not configured. clean-local: $(CABAL) clean - $(RM) -r gen-* + $(RM) -r dist/ + $(RM) -r gen-*/ + +dist-hook: + $(RM) -r $(distdir)/dist/ + $(RM) -r $(distdir)/gen-*/ check-local: $(CABAL) check diff --git a/tutorial/hs/ThriftTutorial.cabal b/tutorial/hs/ThriftTutorial.cabal index 64af3ecad04..acf2130bfd4 100755 --- a/tutorial/hs/ThriftTutorial.cabal +++ b/tutorial/hs/ThriftTutorial.cabal @@ -18,7 +18,7 @@ -- Name: ThriftTutorial -Version: 0.12.0 +Version: 0.14.0 Cabal-Version: >= 1.4 License: OtherLicense Category: Foreign diff --git a/tutorial/js/tutorial.html b/tutorial/js/tutorial.html index d7f3945f2ed..d020bed3734 100755 --- a/tutorial/js/tutorial.html +++ b/tutorial/js/tutorial.html @@ -98,7 +98,7 @@

Thrift Javascript Bindings

-

This Java Script example uses tutorial.thrift and a Thrift server using JSON protocol and HTTP transport. +

This Java Script example uses tutorial.thrift and a Thrift server using JSON protocol and HTTP transport.

- - - netcoreapp2.0 - Client - Client - Exe - false - false - false - false - - - - - - - - diff --git a/tutorial/netcore/Interfaces/.gitignore b/tutorial/netcore/Interfaces/.gitignore deleted file mode 100644 index 2e7446e33e7..00000000000 --- a/tutorial/netcore/Interfaces/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -# ignore for autogenerated files -/shared -/tutorial diff --git a/tutorial/netcore/Server/Server.csproj b/tutorial/netcore/Server/Server.csproj deleted file mode 100644 index 0fbd3032398..00000000000 --- a/tutorial/netcore/Server/Server.csproj +++ /dev/null @@ -1,26 +0,0 @@ - - - - netcoreapp2.0 - Server - Server - Exe - false - false - false - false - - - - - - - - - - - - - - - diff --git a/tutorial/netstd/Client/Client.csproj b/tutorial/netstd/Client/Client.csproj new file mode 100644 index 00000000000..10d5040f0a2 --- /dev/null +++ b/tutorial/netstd/Client/Client.csproj @@ -0,0 +1,41 @@ + + + + + netcoreapp3.1 + Client + Client + Exe + false + false + false + false + + + + + + + + + + + + diff --git a/tutorial/netcore/Client/Program.cs b/tutorial/netstd/Client/Program.cs similarity index 74% rename from tutorial/netcore/Client/Program.cs rename to tutorial/netstd/Client/Program.cs index ce5d8c7e4d7..abbba702e82 100644 --- a/tutorial/netcore/Client/Program.cs +++ b/tutorial/netstd/Client/Program.cs @@ -17,6 +17,7 @@ using System; using System.Collections.Generic; +using System.Diagnostics; using System.IO; using System.Linq; using System.Net; @@ -25,10 +26,11 @@ using System.Threading; using System.Threading.Tasks; using Microsoft.Extensions.Logging; +using Microsoft.Extensions.DependencyInjection; using Thrift; -using Thrift.Protocols; -using Thrift.Transports; -using Thrift.Transports.Client; +using Thrift.Protocol; +using Thrift.Transport; +using Thrift.Transport.Client; using tutorial; using shared; @@ -36,26 +38,31 @@ namespace Client { public class Program { - private static readonly ILogger Logger = new LoggerFactory().AddConsole().AddDebug().CreateLogger(nameof(Client)); + private static ServiceCollection ServiceCollection = new ServiceCollection(); + private static ILogger Logger; + private static readonly TConfiguration Configuration = null; // new TConfiguration() if needed private static void DisplayHelp() { Logger.LogInformation(@" Usage: - Client.exe -help + Client -help will diplay help information - Client.exe -tr: -pr: -mc: + Client -tr: -bf: -pr: -mc: will run client with specified arguments (tcp transport and binary protocol by default) and with 1 client Options: -tr (transport): tcp - (default) tcp transport will be used (host - ""localhost"", port - 9090) - tcpbuffered - buffered transport over tcp will be used (host - ""localhost"", port - 9090) namedpipe - namedpipe transport will be used (pipe address - "".test"") http - http transport will be used (address - ""http://localhost:9090"") tcptls - tcp tls transport will be used (host - ""localhost"", port - 9090) - framed - tcp framed transport will be used (host - ""localhost"", port - 9090) + + -bf (buffering): + none - (default) no buffering will be used + buffered - buffered transport will be used + framed - framed transport will be used -pr (protocol): binary - (default) binary protocol will be used @@ -67,7 +74,7 @@ will run client with specified arguments (tcp transport and binary protocol by d - number of multiple clients to connect to server (max 100, default 1) Sample: - Client.exe -tr:tcp -p:binary + Client -tr:tcp -pr:binary "); } @@ -75,27 +82,40 @@ public static void Main(string[] args) { args = args ?? new string[0]; - if (args.Any(x => x.StartsWith("-help", StringComparison.OrdinalIgnoreCase))) + ServiceCollection.AddLogging(logging => ConfigureLogging(logging)); + using (var serviceProvider = ServiceCollection.BuildServiceProvider()) { - DisplayHelp(); - return; - } + Logger = serviceProvider.GetService().CreateLogger(nameof(Client)); - Logger.LogInformation("Starting client..."); + if (args.Any(x => x.StartsWith("-help", StringComparison.OrdinalIgnoreCase))) + { + DisplayHelp(); + return; + } - using (var source = new CancellationTokenSource()) - { - RunAsync(args, source.Token).GetAwaiter().GetResult(); + Logger.LogInformation("Starting client..."); + + using (var source = new CancellationTokenSource()) + { + RunAsync(args, source.Token).GetAwaiter().GetResult(); + } } } + private static void ConfigureLogging(ILoggingBuilder logging) + { + logging.SetMinimumLevel(LogLevel.Trace); + logging.AddConsole(); + logging.AddDebug(); + } + private static async Task RunAsync(string[] args, CancellationToken cancellationToken) { var numClients = GetNumberOfClients(args); Logger.LogInformation($"Selected # of clients: {numClients}"); - var transports = new TClientTransport[numClients]; + var transports = new TTransport[numClients]; for (int i = 0; i < numClients; i++) { var t = GetTransport(args); @@ -125,31 +145,60 @@ private static async Task RunAsync(string[] args, CancellationToken cancellation await Task.CompletedTask; } - private static TClientTransport GetTransport(string[] args) + private static TTransport GetTransport(string[] args) { - var transport = args.FirstOrDefault(x => x.StartsWith("-tr"))?.Split(':')?[1]; + TTransport transport = new TSocketTransport(IPAddress.Loopback, 9090, Configuration); - Transport selectedTransport; - if (Enum.TryParse(transport, true, out selectedTransport)) + // construct endpoint transport + var transportArg = args.FirstOrDefault(x => x.StartsWith("-tr"))?.Split(':')?[1]; + if (Enum.TryParse(transportArg, true, out Transport selectedTransport)) { switch (selectedTransport) { case Transport.Tcp: - return new TSocketClientTransport(IPAddress.Loopback, 9090); + transport = new TSocketTransport(IPAddress.Loopback, 9090, Configuration); + break; + case Transport.NamedPipe: - return new TNamedPipeClientTransport(".test"); + transport = new TNamedPipeTransport(".test", Configuration); + break; + case Transport.Http: - return new THttpClientTransport(new Uri("http://localhost:9090"), null); - case Transport.TcpBuffered: - return new TBufferedClientTransport(new TSocketClientTransport(IPAddress.Loopback, 9090)); + transport = new THttpTransport(new Uri("http://localhost:9090"), Configuration); + break; + case Transport.TcpTls: - return new TTlsSocketClientTransport(IPAddress.Loopback, 9090, GetCertificate(), CertValidator, LocalCertificateSelectionCallback); - case Transport.Framed: - return new TFramedClientTransport(new TSocketClientTransport(IPAddress.Loopback, 9090)); + transport = new TTlsSocketTransport(IPAddress.Loopback, 9090, Configuration, + GetCertificate(), CertValidator, LocalCertificateSelectionCallback); + break; + + default: + Debug.Assert(false, "unhandled case"); + break; + } + } + + // optionally add layered transport(s) + var bufferingArg = args.FirstOrDefault(x => x.StartsWith("-bf"))?.Split(':')?[1]; + if (Enum.TryParse(bufferingArg, out var selectedBuffering)) + { + switch (selectedBuffering) + { + case Buffering.Buffered: + transport = new TBufferedTransport(transport); + break; + + case Buffering.Framed: + transport = new TFramedTransport(transport); + break; + + default: // layered transport(s) are optional + Debug.Assert(selectedBuffering == Buffering.None, "unhandled case"); + break; } } - return new TSocketClientTransport(IPAddress.Loopback, 9090); + return transport; } private static int GetNumberOfClients(string[] args) @@ -201,7 +250,7 @@ private static bool CertValidator(object sender, X509Certificate certificate, return true; } - private static Tuple GetProtocol(string[] args, TClientTransport transport) + private static Tuple GetProtocol(string[] args, TTransport transport) { var protocol = args.FirstOrDefault(x => x.StartsWith("-pr"))?.Split(':')?[1]; @@ -219,6 +268,9 @@ private static Tuple GetProtocol(string[] args, TClientTran case Protocol.Multiplexed: // it returns BinaryProtocol to avoid making wrapped protocol as public in TProtocolDecorator (in RunClientAsync it will be wrapped into Multiplexed protocol) return new Tuple(selectedProtocol, new TBinaryProtocol(transport)); + default: + Debug.Assert(false, "unhandled case"); + break; } } @@ -351,5 +403,12 @@ private enum Protocol Json, Multiplexed } + + private enum Buffering + { + None, + Buffered, + Framed + } } } diff --git a/tutorial/netcore/Client/Properties/AssemblyInfo.cs b/tutorial/netstd/Client/Properties/AssemblyInfo.cs similarity index 100% rename from tutorial/netcore/Client/Properties/AssemblyInfo.cs rename to tutorial/netstd/Client/Properties/AssemblyInfo.cs diff --git a/tutorial/netcore/Client/Properties/launchSettings.json b/tutorial/netstd/Client/Properties/launchSettings.json similarity index 100% rename from tutorial/netcore/Client/Properties/launchSettings.json rename to tutorial/netstd/Client/Properties/launchSettings.json diff --git a/tutorial/netcore/Client/ThriftTest.pfx b/tutorial/netstd/Client/ThriftTest.pfx similarity index 100% rename from tutorial/netcore/Client/ThriftTest.pfx rename to tutorial/netstd/Client/ThriftTest.pfx diff --git a/tutorial/netcore/Interfaces/Interfaces.csproj b/tutorial/netstd/Interfaces/Interfaces.csproj similarity index 51% rename from tutorial/netcore/Interfaces/Interfaces.csproj rename to tutorial/netstd/Interfaces/Interfaces.csproj index 4297a0654a7..c8b2bd81187 100644 --- a/tutorial/netcore/Interfaces/Interfaces.csproj +++ b/tutorial/netstd/Interfaces/Interfaces.csproj @@ -1,4 +1,22 @@ + netstandard2.0 @@ -11,20 +29,20 @@ - + - + - - - + + + diff --git a/tutorial/netcore/Interfaces/Properties/AssemblyInfo.cs b/tutorial/netstd/Interfaces/Properties/AssemblyInfo.cs similarity index 100% rename from tutorial/netcore/Interfaces/Properties/AssemblyInfo.cs rename to tutorial/netstd/Interfaces/Properties/AssemblyInfo.cs diff --git a/tutorial/netcore/Makefile.am b/tutorial/netstd/Makefile.am similarity index 97% rename from tutorial/netcore/Makefile.am rename to tutorial/netstd/Makefile.am index e3055565567..f295cc0729b 100644 --- a/tutorial/netcore/Makefile.am +++ b/tutorial/netstd/Makefile.am @@ -20,7 +20,7 @@ SUBDIRS = . all-local: - $(DOTNETCORE) build + $(DOTNETCORE) build -c Release clean-local: $(RM) Interfaces.dll diff --git a/tutorial/netcore/README.md b/tutorial/netstd/README.md similarity index 75% rename from tutorial/netcore/README.md rename to tutorial/netstd/README.md index 626ef921269..a49fb474bf1 100644 --- a/tutorial/netcore/README.md +++ b/tutorial/netstd/README.md @@ -1,62 +1,62 @@ # Building of samples for different platforms -# Reused components -- NET Core Standard 2.0 -- NET Core App 2.0 +# Requirements +- NET Core Standard 3.1 (LTS) runtime or SDK (see below for further info) # How to build -- Download and install the latest .NET Core SDK for your platform https://www.microsoft.com/net/core#windowsvs2015 (archive for SDK 1.0.0-preview2-003121 located by: https://github.com/dotnet/core/blob/master/release-notes/download-archive.md) -- Ensure that you have thrift.exe which supports netcore lib and it added to PATH +- Download and install the latest .NET Core SDK for your platform https://dotnet.microsoft.com/download/dotnet-core +- Ensure that you have thrift.exe which supports netstd lib and it added to PATH - Go to current folder - Run **build.sh** or **build.cmd** from the root of cloned repository - Check tests in **src/Tests** folder -- Continue with /tutorials/netcore +- Continue with /tutorials/netstd # How to run -Notes: dotnet run supports passing arguments to app after -- symbols (https://docs.microsoft.com/en-us/dotnet/articles/core/tools/dotnet-run) - example: **dotnet run -- -h** will show help for app +Depending on the platform, the name of the generated executables will vary. On Linux, it is just "Client" or "Server", on Windows it is "Client.exe" and "Server.exe". In the following, we use the abbreviated form "Client" and "Server". - build - go to folder (Client/Server) -- run with specifying of correct parameters **dotnet run -tr:tcp -pr:multiplexed**, **dotnet run -help** (later, after migration to csproj and latest SDK will be possibility to use more usable form **dotnet run -- arguments**) +- run the generated executables: server first, then client from a second console -#Notes -- Possible adding additional platforms after stabilization of .NET Core (runtimes, platforms (Red Hat Linux, OpenSuse, etc.) - -#Known issues +# Known issues - In trace logging mode you can see some not important internal exceptions # Running of samples -Please install Thrift C# .NET Core library or copy sources and build them to correcly build and run samples +On machines that do not have the SDK installed, you need to install the NET Core runtime first. The SDK is only needed to build programs, otherwise the runtime is sufficient. # NetCore Server Usage: - Server.exe -h + Server -help will diplay help information - Server.exe -tr: -pr: + Server -tr: -pr: will run server with specified arguments (tcp transport and binary protocol by default) Options: -tr (transport): tcp - (default) tcp transport will be used (host - ""localhost"", port - 9090) - tcpbuffered - tcp buffered transport will be used (host - ""localhost"", port - 9090) namedpipe - namedpipe transport will be used (pipe address - "".test"") http - http transport will be used (http address - ""localhost:9090"") tcptls - tcp transport with tls will be used (host - ""localhost"", port - 9090) - framed - tcp framed transport will be used (host - ""localhost"", port - 9090) + + -bf (buffering): + none - (default) no transport factory will be used + buffered - buffered transport factory will be used + framed - framed transport factory will be used (this must match the client) -pr (protocol): binary - (default) binary protocol will be used compact - compact protocol will be used json - json protocol will be used - + multiplexed - multiplexed protocol will be used + Sample: - Server.exe -tr:tcp + Server -tr:tcp **Remarks**: @@ -69,33 +69,37 @@ Sample: Usage: - Client.exe -h + Client -help will diplay help information - Client.exe -tr: -pr: -mc: + Client -tr: -pr: -mc: will run client with specified arguments (tcp transport and binary protocol by default) Options: -tr (transport): tcp - (default) tcp transport will be used (host - ""localhost"", port - 9090) - tcpbuffered - buffered transport over tcp will be used (host - ""localhost"", port - 9090) namedpipe - namedpipe transport will be used (pipe address - "".test"") http - http transport will be used (address - ""http://localhost:9090"") tcptls - tcp tls transport will be used (host - ""localhost"", port - 9090) - framed - tcp framed transport will be used (host - ""localhost"", port - 9090) + + -bf (buffering): + none - (default) no transport factory will be used + buffered - buffered transport factory will be used + framed - framed transport factory will be used (this must match the client) -pr (protocol): binary - (default) binary protocol will be used compact - compact protocol will be used json - json protocol will be used - + multiplexed - multiplexed protocol will be used + -mc (multiple clients): - number of multiple clients to connect to server (max 100, default 1) Sample: - Client.exe -tr:tcp -pr:binary -mc:10 + Client -tr:tcp -pr:binary -mc:10 Remarks: @@ -105,10 +109,10 @@ Remarks: # How to test communication between NetCore and Python -* Generate code with the latest **thrift.exe** util -* Ensure that **thrift.exe** util generated folder **gen-py** with generated code for Python +* Generate code with the latest **thrift** utility +* Ensure that **thrift** generated folder **gen-py** with generated code for Python exists * Create **client.py** and **server.py** from the code examples below and save them to the folder with previosly generated folder **gen-py** -* Run netcore samples (client and server) and python samples (client and server) +* Run netstd samples (client and server) and python samples (client and server) Remarks: @@ -235,16 +239,10 @@ class CalculatorHandler: val = work.Num1 * work.Num2 elif work.Op == Operation.Divide: if work.Num2 == 0: - x = InvalidOperation() - x.WhatOp = work.Op - x.Why = 'Cannot divide by 0' - raise x + raise InvalidOperation(work.Op, 'Cannot divide by 0') val = work.Num1 / work.Num2 else: - x = InvalidOperation() - x.WhatOp = work.Op - x.Why = 'Invalid operation' - raise x + raise InvalidOperation(work.Op, 'Invalid operation') log = SharedStruct() log.Key = logid diff --git a/tutorial/netcore/Server/Program.cs b/tutorial/netstd/Server/Program.cs similarity index 75% rename from tutorial/netcore/Server/Program.cs rename to tutorial/netstd/Server/Program.cs index 6a181bab750..3181e8e332a 100644 --- a/tutorial/netcore/Server/Program.cs +++ b/tutorial/netstd/Server/Program.cs @@ -29,60 +29,80 @@ using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using Thrift; -using Thrift.Protocols; +using Thrift.Protocol; using Thrift.Server; -using Thrift.Transports; -using Thrift.Transports.Server; +using Thrift.Transport; +using Thrift.Transport.Server; using tutorial; using shared; +using Thrift.Processor; +using System.Diagnostics; namespace Server { public class Program { - private static readonly ILogger Logger = new LoggerFactory().AddConsole(LogLevel.Trace).AddDebug(LogLevel.Trace).CreateLogger(nameof(Server)); + private static ServiceCollection ServiceCollection = new ServiceCollection(); + private static ILogger Logger; + private static readonly TConfiguration Configuration = null; // new TConfiguration() if needed public static void Main(string[] args) { args = args ?? new string[0]; - if (args.Any(x => x.StartsWith("-help", StringComparison.OrdinalIgnoreCase))) + ServiceCollection.AddLogging(logging => ConfigureLogging(logging)); + using (var serviceProvider = ServiceCollection.BuildServiceProvider()) { - DisplayHelp(); - return; - } + Logger = serviceProvider.GetService().CreateLogger(nameof(Server)); - using (var source = new CancellationTokenSource()) - { - RunAsync(args, source.Token).GetAwaiter().GetResult(); + if (args.Any(x => x.StartsWith("-help", StringComparison.OrdinalIgnoreCase))) + { + DisplayHelp(); + return; + } + + using (var source = new CancellationTokenSource()) + { + RunAsync(args, source.Token).GetAwaiter().GetResult(); - Logger.LogInformation("Press any key to stop..."); + Logger.LogInformation("Press any key to stop..."); - Console.ReadLine(); - source.Cancel(); + Console.ReadLine(); + source.Cancel(); + } + + Logger.LogInformation("Server stopped"); } + } - Logger.LogInformation("Server stopped"); + private static void ConfigureLogging(ILoggingBuilder logging) + { + logging.SetMinimumLevel(LogLevel.Trace); + logging.AddConsole(); + logging.AddDebug(); } private static void DisplayHelp() { Logger.LogInformation(@" Usage: - Server.exe -help + Server -help will diplay help information - Server.exe -tr: -pr: - will run server with specified arguments (tcp transport and binary protocol by default) + Server -tr: -bf: -pr: + will run server with specified arguments (tcp transport, no buffering, and binary protocol by default) Options: -tr (transport): tcp - (default) tcp transport will be used (host - ""localhost"", port - 9090) - tcpbuffered - tcp buffered transport will be used (host - ""localhost"", port - 9090) namedpipe - namedpipe transport will be used (pipe address - "".test"") http - http transport will be used (http address - ""localhost:9090"") tcptls - tcp transport with tls will be used (host - ""localhost"", port - 9090) - framed - tcp framed transport will be used (host - ""localhost"", port - 9090) + + -bf (buffering): + none - (default) no buffering will be used + buffered - buffered transport will be used + framed - framed transport will be used -pr (protocol): binary - (default) binary protocol will be used @@ -91,13 +111,14 @@ will run server with specified arguments (tcp transport and binary protocol by d multiplexed - multiplexed protocol will be used Sample: - Server.exe -tr:tcp + Server -tr:tcp "); } private static async Task RunAsync(string[] args, CancellationToken cancellationToken) { var selectedTransport = GetTransport(args); + var selectedBuffering = GetBuffering(args); var selectedProtocol = GetProtocol(args); if (selectedTransport == Transport.Http) @@ -106,7 +127,7 @@ private static async Task RunAsync(string[] args, CancellationToken cancellation } else { - await RunSelectedConfigurationAsync(selectedTransport, selectedProtocol, cancellationToken); + await RunSelectedConfigurationAsync(selectedTransport, selectedBuffering, selectedProtocol, cancellationToken); } } @@ -119,6 +140,15 @@ private static Protocol GetProtocol(string[] args) return selectedProtocol; } + private static Buffering GetBuffering(string[] args) + { + var buffering = args.FirstOrDefault(x => x.StartsWith("-bf"))?.Split(":")?[1]; + + Enum.TryParse(buffering, out var selectedBuffering); + + return selectedBuffering; + } + private static Transport GetTransport(string[] args) { var transport = args.FirstOrDefault(x => x.StartsWith("-tr"))?.Split(':')?[1]; @@ -128,61 +158,68 @@ private static Transport GetTransport(string[] args) return selectedTransport; } - private static async Task RunSelectedConfigurationAsync(Transport transport, Protocol protocol, CancellationToken cancellationToken) + private static async Task RunSelectedConfigurationAsync(Transport transport, Buffering buffering, Protocol protocol, CancellationToken cancellationToken) { - var fabric = new LoggerFactory().AddConsole(LogLevel.Trace).AddDebug(LogLevel.Trace); var handler = new CalculatorAsyncHandler(); - ITAsyncProcessor processor = null; TServerTransport serverTransport = null; - switch (transport) { case Transport.Tcp: - serverTransport = new TServerSocketTransport(9090); - break; - case Transport.TcpBuffered: - serverTransport = new TServerSocketTransport(port: 9090, clientTimeout: 10000, useBufferedSockets: true); + serverTransport = new TServerSocketTransport(9090, Configuration); break; case Transport.NamedPipe: - serverTransport = new TNamedPipeServerTransport(".test"); + serverTransport = new TNamedPipeServerTransport(".test", Configuration); break; case Transport.TcpTls: - serverTransport = new TTlsServerSocketTransport(9090, false, GetCertificate(), ClientCertValidator, LocalCertificateSelectionCallback); - break; - case Transport.Framed: - serverTransport = new TServerFramedTransport(9090); + serverTransport = new TTlsServerSocketTransport(9090, Configuration, + GetCertificate(), ClientCertValidator, LocalCertificateSelectionCallback); break; } - ITProtocolFactory inputProtocolFactory; - ITProtocolFactory outputProtocolFactory; + TTransportFactory inputTransportFactory = null; + TTransportFactory outputTransportFactory = null; + switch (buffering) + { + case Buffering.Buffered: + inputTransportFactory = new TBufferedTransport.Factory(); + outputTransportFactory = new TBufferedTransport.Factory(); + break; + + case Buffering.Framed: + inputTransportFactory = new TFramedTransport.Factory(); + outputTransportFactory = new TFramedTransport.Factory(); + break; + + default: // layered transport(s) are optional + Debug.Assert(buffering == Buffering.None, "unhandled case"); + break; + } + TProtocolFactory inputProtocolFactory = null; + TProtocolFactory outputProtocolFactory = null; + ITAsyncProcessor processor = null; switch (protocol) { case Protocol.Binary: - { inputProtocolFactory = new TBinaryProtocol.Factory(); outputProtocolFactory = new TBinaryProtocol.Factory(); processor = new Calculator.AsyncProcessor(handler); - } break; + case Protocol.Compact: - { inputProtocolFactory = new TCompactProtocol.Factory(); outputProtocolFactory = new TCompactProtocol.Factory(); processor = new Calculator.AsyncProcessor(handler); - } break; + case Protocol.Json: - { inputProtocolFactory = new TJsonProtocol.Factory(); outputProtocolFactory = new TJsonProtocol.Factory(); processor = new Calculator.AsyncProcessor(handler); - } break; + case Protocol.Multiplexed: - { inputProtocolFactory = new TBinaryProtocol.Factory(); outputProtocolFactory = new TBinaryProtocol.Factory(); @@ -197,20 +234,31 @@ private static async Task RunSelectedConfigurationAsync(Transport transport, Pro multiplexedProcessor.RegisterProcessor(nameof(SharedService), sharedServiceProcessor); processor = multiplexedProcessor; - } break; + default: throw new ArgumentOutOfRangeException(nameof(protocol), protocol, null); } + try { Logger.LogInformation( $"Selected TAsyncServer with {serverTransport} transport, {processor} processor and {inputProtocolFactory} protocol factories"); - var server = new AsyncBaseServer(processor, serverTransport, inputProtocolFactory, outputProtocolFactory, fabric); + var loggerFactory = ServiceCollection.BuildServiceProvider().GetService(); + + var server = new TSimpleAsyncServer( + itProcessorFactory: new TSingletonProcessorFactory(processor), + serverTransport: serverTransport, + inputTransportFactory: inputTransportFactory, + outputTransportFactory: outputTransportFactory, + inputProtocolFactory: inputProtocolFactory, + outputProtocolFactory: outputProtocolFactory, + logger: loggerFactory.CreateLogger()); Logger.LogInformation("Starting the server..."); + await server.ServeAsync(cancellationToken); } catch (Exception x) @@ -258,11 +306,16 @@ private static bool ClientCertValidator(object sender, X509Certificate certifica private enum Transport { Tcp, - TcpBuffered, NamedPipe, Http, TcpTls, - Framed + } + + private enum Buffering + { + None, + Buffered, + Framed, } private enum Protocol @@ -287,14 +340,17 @@ public void Run(CancellationToken cancellationToken) .UseUrls("http://localhost:9090") .UseContentRoot(Directory.GetCurrentDirectory()) .UseStartup() + .ConfigureLogging((ctx,logging) => ConfigureLogging(logging)) .Build(); + Logger.LogTrace("test"); + Logger.LogCritical("test"); host.RunAsync(cancellationToken).GetAwaiter().GetResult(); } public class Startup { - public Startup(IHostingEnvironment env) + public Startup(IWebHostEnvironment env) { var builder = new ConfigurationBuilder() .SetBasePath(env.ContentRootPath) @@ -314,8 +370,7 @@ public void ConfigureServices(IServiceCollection services) } // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. - public void Configure(IApplicationBuilder app, IHostingEnvironment env, - ILoggerFactory loggerFactory) + public void Configure(IApplicationBuilder app, IWebHostEnvironment env, ILoggerFactory loggerFactory) { app.UseMiddleware(); } diff --git a/tutorial/netcore/Server/Properties/AssemblyInfo.cs b/tutorial/netstd/Server/Properties/AssemblyInfo.cs similarity index 100% rename from tutorial/netcore/Server/Properties/AssemblyInfo.cs rename to tutorial/netstd/Server/Properties/AssemblyInfo.cs diff --git a/tutorial/netcore/Server/Properties/launchSettings.json b/tutorial/netstd/Server/Properties/launchSettings.json similarity index 100% rename from tutorial/netcore/Server/Properties/launchSettings.json rename to tutorial/netstd/Server/Properties/launchSettings.json diff --git a/tutorial/netstd/Server/Server.csproj b/tutorial/netstd/Server/Server.csproj new file mode 100644 index 00000000000..b3ff516e3ab --- /dev/null +++ b/tutorial/netstd/Server/Server.csproj @@ -0,0 +1,44 @@ + + + + + netcoreapp3.1 + Server + Server + Exe + false + false + false + false + + + + + + + + + + + + + + + diff --git a/tutorial/netcore/Server/ThriftTest.pfx b/tutorial/netstd/Server/ThriftTest.pfx similarity index 100% rename from tutorial/netcore/Server/ThriftTest.pfx rename to tutorial/netstd/Server/ThriftTest.pfx diff --git a/tutorial/netcore/Tutorial.sln b/tutorial/netstd/Tutorial.sln similarity index 98% rename from tutorial/netcore/Tutorial.sln rename to tutorial/netstd/Tutorial.sln index 2ddcd4617c0..84b25790269 100644 --- a/tutorial/netcore/Tutorial.sln +++ b/tutorial/netstd/Tutorial.sln @@ -2,7 +2,7 @@ Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 15 VisualStudioVersion = 15.0.26114.2 MinimumVisualStudioVersion = 10.0.40219.1 -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Thrift", "..\..\lib\netcore\Thrift\Thrift.csproj", "{C20EA2A9-7660-47DE-9A49-D1EF12FB2895}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Thrift", "..\..\lib\netstd\Thrift\Thrift.csproj", "{C20EA2A9-7660-47DE-9A49-D1EF12FB2895}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Interfaces", "Interfaces\Interfaces.csproj", "{B9E24D84-2712-4158-8F1A-DDE44CD1BB0A}" EndProject diff --git a/tutorial/netcore/build.cmd b/tutorial/netstd/build.cmd similarity index 100% rename from tutorial/netcore/build.cmd rename to tutorial/netstd/build.cmd diff --git a/tutorial/netcore/build.sh b/tutorial/netstd/build.sh similarity index 100% rename from tutorial/netcore/build.sh rename to tutorial/netstd/build.sh diff --git a/tutorial/nodejs/NodeServer.js b/tutorial/nodejs/NodeServer.js index 55d3817e9cf..2e198825c10 100644 --- a/tutorial/nodejs/NodeServer.js +++ b/tutorial/nodejs/NodeServer.js @@ -77,7 +77,6 @@ var server = thrift.createServer(Calculator, { zip: function() { console.log("zip()"); - result(null); } }); diff --git a/tutorial/ocaml/_oasis b/tutorial/ocaml/_oasis index a0ea6c526b2..7491436e933 100644 --- a/tutorial/ocaml/_oasis +++ b/tutorial/ocaml/_oasis @@ -1,5 +1,5 @@ Name: tutorial -Version: 0.12.0 +Version: 0.14.0 OASISFormat: 0.3 Synopsis: OCaml Tutorial example Authors: Apache Thrift Developers diff --git a/tutorial/perl/Makefile.am b/tutorial/perl/Makefile.am new file mode 100755 index 00000000000..fe77213bbf3 --- /dev/null +++ b/tutorial/perl/Makefile.am @@ -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. +# + +gen-perl/tutorial/Calculator.pm gen-perl/shared/SharedService.pm: $(top_srcdir)/tutorial/tutorial.thrift + $(THRIFT) --gen perl -r $< + +all-local: gen-perl/tutorial/Calculator.pm + +tutorialserver: all + ${PERL} PerlServer.pl + +tutorialclient: all + ${PERL} PerlClient.pl + +clean-local: + $(RM) -r gen-* + +EXTRA_DIST = \ + PerlServer.pl \ + PerlClient.pl diff --git a/tutorial/perl/PerlClient.pl b/tutorial/perl/PerlClient.pl index 7c23289ce5a..5f6a3b2d1dc 100644 --- a/tutorial/perl/PerlClient.pl +++ b/tutorial/perl/PerlClient.pl @@ -23,7 +23,7 @@ use warnings; use lib '../../lib/perl/lib'; -use lib '../gen-perl'; +use lib 'gen-perl'; use Thrift; use Thrift::BinaryProtocol; diff --git a/tutorial/perl/PerlServer.pl b/tutorial/perl/PerlServer.pl index dfe6b89a156..15129873101 100644 --- a/tutorial/perl/PerlServer.pl +++ b/tutorial/perl/PerlServer.pl @@ -20,9 +20,11 @@ # use strict; -use lib '../gen-perl'; +use lib '../../lib/perl/lib'; +use lib 'gen-perl'; use Thrift::Socket; use Thrift::Server; +use Thrift::ServerSocket; use tutorial::Calculator; package CalculatorHandler; diff --git a/tutorial/php/Makefile.am b/tutorial/php/Makefile.am new file mode 100755 index 00000000000..3d30ad999e6 --- /dev/null +++ b/tutorial/php/Makefile.am @@ -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. +# + +gen-php/tutorial/Calculator.php gen-php/shared/SharedService.php: $(top_srcdir)/tutorial/tutorial.thrift + $(THRIFT) --gen php:server -r $< + +all-local: gen-php/tutorial/Calculator.php + +tutorialserver: all + ${PYTHON} runserver.py + +tutorialclient: all + ${PHP} PhpClient.php --http + +clean-local: + $(RM) -r gen-* + +EXTRA_DIST = \ + PhpServer.php \ + PhpClient.php \ + runserver.py diff --git a/tutorial/php/PhpClient.php b/tutorial/php/PhpClient.php index 92dc3cbacc3..5ac154718b3 100755 --- a/tutorial/php/PhpClient.php +++ b/tutorial/php/PhpClient.php @@ -5,16 +5,16 @@ error_reporting(E_ALL); -require_once __DIR__.'/../../lib/php/lib/ClassLoader/ThriftClassLoader.php'; +require_once __DIR__.'/../../vendor/autoload.php'; use Thrift\ClassLoader\ThriftClassLoader; -$GEN_DIR = realpath(dirname(__FILE__).'/..').'/gen-php'; +$GEN_DIR = realpath(dirname(__FILE__)).'/gen-php'; $loader = new ThriftClassLoader(); $loader->registerNamespace('Thrift', __DIR__ . '/../../lib/php/lib'); -$loader->registerDefinition('shared', $GEN_DIR); -$loader->registerDefinition('tutorial', $GEN_DIR); +$loader->registerNamespace('shared', $GEN_DIR); +$loader->registerNamespace('tutorial', $GEN_DIR); $loader->register(); /* diff --git a/tutorial/php/PhpServer.php b/tutorial/php/PhpServer.php index 5a9b49bdef7..f4b3b319f2a 100755 --- a/tutorial/php/PhpServer.php +++ b/tutorial/php/PhpServer.php @@ -5,16 +5,16 @@ error_reporting(E_ALL); -require_once __DIR__.'/../../lib/php/lib/ClassLoader/ThriftClassLoader.php'; +require_once __DIR__.'/../../vendor/autoload.php'; use Thrift\ClassLoader\ThriftClassLoader; -$GEN_DIR = realpath(dirname(__FILE__).'/..').'/gen-php'; +$GEN_DIR = realpath(dirname(__FILE__)).'/gen-php'; $loader = new ThriftClassLoader(); $loader->registerNamespace('Thrift', __DIR__ . '/../../lib/php/lib'); -$loader->registerDefinition('shared', $GEN_DIR); -$loader->registerDefinition('tutorial', $GEN_DIR); +$loader->registerNamespace('shared', $GEN_DIR); +$loader->registerNamespace('tutorial', $GEN_DIR); $loader->register(); /* diff --git a/tutorial/php/runserver.py b/tutorial/php/runserver.py index 077daa102ac..8cc30fbe5ce 100755 --- a/tutorial/php/runserver.py +++ b/tutorial/php/runserver.py @@ -30,4 +30,5 @@ class Handler(CGIHTTPServer.CGIHTTPRequestHandler): cgi_directories = ['/php'] + BaseHTTPServer.HTTPServer(('', 8080), Handler).serve_forever() diff --git a/tutorial/py.tornado/PythonServer.py b/tutorial/py.tornado/PythonServer.py index e0229a26e09..b472195a498 100755 --- a/tutorial/py.tornado/PythonServer.py +++ b/tutorial/py.tornado/PythonServer.py @@ -58,16 +58,10 @@ def calculate(self, logid, work): val = work.num1 * work.num2 elif work.op == Operation.DIVIDE: if work.num2 == 0: - x = InvalidOperation() - x.whatOp = work.op - x.why = "Cannot divide by 0" - raise x + raise InvalidOperation(work.op, "Cannot divide by 0") val = work.num1 / work.num2 else: - x = InvalidOperation() - x.whatOp = work.op - x.why = "Invalid operation" - raise x + raise InvalidOperation(work.op, "Invalid operation") log = SharedStruct() log.key = logid diff --git a/tutorial/py.twisted/PythonClient.py b/tutorial/py.twisted/PythonClient.py index 63dde7e7a89..2976495e314 100755 --- a/tutorial/py.twisted/PythonClient.py +++ b/tutorial/py.twisted/PythonClient.py @@ -67,6 +67,7 @@ def main(client): print(('Check log: %s' % (log.value))) reactor.stop() + if __name__ == '__main__': d = ClientCreator(reactor, TTwisted.ThriftClientProtocol, diff --git a/tutorial/py.twisted/PythonServer.py b/tutorial/py.twisted/PythonServer.py index 1b0e2d5b968..c3e64db5c33 100755 --- a/tutorial/py.twisted/PythonServer.py +++ b/tutorial/py.twisted/PythonServer.py @@ -60,16 +60,10 @@ def calculate(self, logid, work): val = work.num1 * work.num2 elif work.op == Operation.DIVIDE: if work.num2 == 0: - x = InvalidOperation() - x.whatOp = work.op - x.why = 'Cannot divide by 0' - raise x + raise InvalidOperation(work.op, 'Cannot divide by 0') val = work.num1 / work.num2 else: - x = InvalidOperation() - x.whatOp = work.op - x.why = 'Invalid operation' - raise x + raise InvalidOperation(work.op, 'Invalid operation') log = SharedStruct() log.key = logid @@ -85,6 +79,7 @@ def getStruct(self, key): def zip(self): print('zip()') + if __name__ == '__main__': handler = CalculatorHandler() processor = Calculator.Processor(handler) diff --git a/tutorial/py/PythonServer.py b/tutorial/py/PythonServer.py index e6421ef0815..d2343edd3d3 100755 --- a/tutorial/py/PythonServer.py +++ b/tutorial/py/PythonServer.py @@ -57,16 +57,10 @@ def calculate(self, logid, work): val = work.num1 * work.num2 elif work.op == Operation.DIVIDE: if work.num2 == 0: - x = InvalidOperation() - x.whatOp = work.op - x.why = 'Cannot divide by 0' - raise x + raise InvalidOperation(work.op, 'Cannot divide by 0') val = work.num1 / work.num2 else: - x = InvalidOperation() - x.whatOp = work.op - x.why = 'Invalid operation' - raise x + raise InvalidOperation(work.op, 'Invalid operation') log = SharedStruct() log.key = logid diff --git a/tutorial/rb/Makefile.am b/tutorial/rb/Makefile.am index 937241367fe..885cd92318e 100755 --- a/tutorial/rb/Makefile.am +++ b/tutorial/rb/Makefile.am @@ -17,10 +17,10 @@ # under the License. # -gen-py/calculator.rb gen-py/shared_service.rb: $(top_srcdir)/tutorial/tutorial.thrift +gen-rb/calculator.rb gen-rb/shared_service.rb: $(top_srcdir)/tutorial/tutorial.thrift $(THRIFT) --gen rb -r $< -all-local: gen-py/calculator.rb +all-local: gen-rb/calculator.rb tutorialserver: all ${RUBY} RubyServer.rb diff --git a/tutorial/rs/Cargo.toml b/tutorial/rs/Cargo.toml index 210a0daf9ff..5e21d51a6ed 100644 --- a/tutorial/rs/Cargo.toml +++ b/tutorial/rs/Cargo.toml @@ -1,15 +1,14 @@ [package] name = "thrift-tutorial" version = "0.1.0" +edition = "2018" license = "Apache-2.0" authors = ["Apache Thrift Developers "] exclude = ["Makefile*", "shared.rs", "tutorial.rs"] publish = false [dependencies] -clap = "<2.28.0" -ordered-float = "0.3.0" -try_from = "0.2.0" +clap = "2.33" [dependencies.thrift] path = "../../lib/rs" diff --git a/tutorial/rs/README.md b/tutorial/rs/README.md index 384e9f8bb30..97b1490fe5d 100644 --- a/tutorial/rs/README.md +++ b/tutorial/rs/README.md @@ -4,43 +4,29 @@ 1. Get the [Thrift compiler](https://thrift.apache.org). -2. Add the following crates to your `Cargo.toml`. +2. Add the thrift crate to your `Cargo.toml`. ```toml thrift = "x.y.z" # x.y.z is the version of the thrift compiler -ordered_float = "0.3.0" -try_from = "0.2.0" ``` -3. Add the same crates to your `lib.rs` or `main.rs`. - -```rust -extern crate ordered_float; -extern crate thrift; -extern crate try_from; -``` - -4. Generate Rust sources for your IDL (for example, `Tutorial.thrift`). +3. Generate Rust sources for your IDL (for example, `Tutorial.thrift`). ```shell thrift -out my_rust_program/src --gen rs -r Tutorial.thrift ``` -5. Use the generated source in your code. +4. Use the generated source in your code. ```rust -// add extern crates here, or in your lib.rs -extern crate ordered_float; -extern crate thrift; -extern crate try_from; - -// generated Rust module -use tutorial; +// generated Rust module from Thrift IDL +mod tutorial; use thrift::protocol::{TCompactInputProtocol, TCompactOutputProtocol}; use thrift::protocol::{TInputProtocol, TOutputProtocol}; use thrift::transport::{TFramedReadTransport, TFramedWriteTransport}; use thrift::transport::{TIoChannel, TTcpChannel}; + use tutorial::{CalculatorSyncClient, TCalculatorSyncClient}; use tutorial::{Operation, Work}; @@ -60,7 +46,7 @@ fn run() -> thrift::Result<()> { // println!("connect to server on 127.0.0.1:9090"); - let mut c = TTcpTransport::new(); + let mut c = TTcpChannel::new(); c.open("127.0.0.1:9090")?; let (i_chan, o_chan) = c.split()?; @@ -72,7 +58,7 @@ fn run() -> thrift::Result<()> { TFramedWriteTransport::new(o_chan) ); - let client = CalculatorSyncClient::new(i_prot, o_prot); + let mut client = CalculatorSyncClient::new(i_prot, o_prot); // // alright! - let's make some calls @@ -84,14 +70,14 @@ fn run() -> thrift::Result<()> { // two-way with some return let res = client.calculate( 72, - Work::new(7, 8, Operation::MULTIPLY, None) + Work::new(7, 8, Operation::Multiply, None) )?; println!("multiplied 7 and 8, got {}", res); // two-way and returns a Thrift-defined exception let res = client.calculate( 77, - Work::new(2, 0, Operation::DIVIDE, None) + Work::new(2, 0, Operation::Divide, None) ); match res { Ok(v) => panic!("shouldn't have succeeded with result {}", v), @@ -119,7 +105,7 @@ each generated file. ### Results and Errors The Thrift runtime library defines a `thrift::Result` and a `thrift::Error` type, -both of which are used throught the runtime library and in all generated code. +both of which are used throughout the runtime library and in all generated code. Conversions are defined from `std::io::Error`, `str` and `String` into `thrift::Error`. diff --git a/tutorial/rs/src/bin/tutorial_client.rs b/tutorial/rs/src/bin/tutorial_client.rs index c80fafc2f10..f7de23f0666 100644 --- a/tutorial/rs/src/bin/tutorial_client.rs +++ b/tutorial/rs/src/bin/tutorial_client.rs @@ -15,11 +15,7 @@ // specific language governing permissions and limitations // under the License. -#[macro_use] -extern crate clap; - -extern crate thrift; -extern crate thrift_tutorial; +use clap::{clap_app, value_t}; use thrift::protocol::{TCompactInputProtocol, TCompactOutputProtocol}; use thrift::transport::{ @@ -75,7 +71,7 @@ fn run() -> thrift::Result<()> { println!("multiplied 7 and 8 and got {}", res); // let's get the log for it - let res = client.get_struct(32)?; + let res = client.get_struct(logid /* 32 */)?; println!("got log {:?} for operation {}", res, logid); // ok - let's be bad :( diff --git a/tutorial/rs/src/bin/tutorial_server.rs b/tutorial/rs/src/bin/tutorial_server.rs index 95b1a2b6eab..fbccb69c973 100644 --- a/tutorial/rs/src/bin/tutorial_server.rs +++ b/tutorial/rs/src/bin/tutorial_server.rs @@ -15,21 +15,17 @@ // specific language governing permissions and limitations // under the License. -#[macro_use] -extern crate clap; - -extern crate thrift; -extern crate thrift_tutorial; - use std::collections::HashMap; use std::convert::{From, Into}; use std::default::Default; use std::sync::Mutex; +use clap::{clap_app, value_t}; + use thrift::protocol::{TCompactInputProtocolFactory, TCompactOutputProtocolFactory}; use thrift::server::TServer; - use thrift::transport::{TFramedReadTransportFactory, TFramedWriteTransportFactory}; + use thrift_tutorial::shared::{SharedServiceSyncHandler, SharedStruct}; use thrift_tutorial::tutorial::{CalculatorSyncHandler, CalculatorSyncProcessor}; use thrift_tutorial::tutorial::{InvalidOperation, Operation, Work}; diff --git a/tutorial/rs/src/lib.rs b/tutorial/rs/src/lib.rs index 40007e5d346..ac509cc921f 100644 --- a/tutorial/rs/src/lib.rs +++ b/tutorial/rs/src/lib.rs @@ -15,9 +15,5 @@ // specific language governing permissions and limitations // under the License. -extern crate ordered_float; -extern crate thrift; -extern crate try_from; - pub mod shared; pub mod tutorial; diff --git a/tutorial/shared.thrift b/tutorial/shared.thrift index f1685bd1618..3c6a69db871 100644 --- a/tutorial/shared.thrift +++ b/tutorial/shared.thrift @@ -30,7 +30,8 @@ namespace java shared namespace perl shared namespace php shared namespace haxe shared -namespace netcore shared +namespace netstd shared + struct SharedStruct { 1: i32 key diff --git a/tutorial/tutorial.thrift b/tutorial/tutorial.thrift index e0275464440..f1e291297dd 100644 --- a/tutorial/tutorial.thrift +++ b/tutorial/tutorial.thrift @@ -71,7 +71,7 @@ namespace java tutorial namespace php tutorial namespace perl tutorial namespace haxe tutorial -namespace netcore tutorial +namespace netstd tutorial /** * Thrift lets you do typedefs to get pretty names for your types. Standard